From 1a3cd2a1c4e06372fb65dabefea8344c7ec5c318 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 29 Nov 2014 15:22:35 -0500 Subject: add single art limit setting to dlna profile --- MediaBrowser.Server.Implementations/Localization/Server/server.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'MediaBrowser.Server.Implementations/Localization') diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 117a5c407..e328ca2c6 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1291,5 +1291,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } -- cgit v1.2.3 From d7bdb744ca9d4b3955071dfe3c38ed631dbafbfd Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 30 Nov 2014 14:01:33 -0500 Subject: add new image params --- MediaBrowser.Api/Library/LibraryService.cs | 6 +- MediaBrowser.Api/Movies/MoviesService.cs | 2 +- MediaBrowser.Api/PackageService.cs | 7 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 5 - MediaBrowser.Api/PlaylistService.cs | 2 +- MediaBrowser.Api/StartupWizardService.cs | 1 + MediaBrowser.Api/UserLibrary/ArtistsService.cs | 2 +- .../UserLibrary/BaseItemsByNameService.cs | 12 +- MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs | 47 ++++++- MediaBrowser.Api/UserLibrary/ItemsService.cs | 9 +- MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 2 +- MediaBrowser.Common/Extensions/BaseExtensions.cs | 2 + MediaBrowser.Controller/Dlna/DlnaIconResponse.cs | 22 --- MediaBrowser.Controller/Dlna/IDlnaManager.cs | 5 +- MediaBrowser.Controller/Dto/IDtoService.cs | 18 ++- MediaBrowser.Controller/Entities/BaseItem.cs | 6 +- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Controller/Entities/User.cs | 3 +- MediaBrowser.Controller/Library/ILibraryManager.cs | 8 ++ MediaBrowser.Controller/Library/IUserManager.cs | 14 ++ MediaBrowser.Controller/LiveTv/ILiveTvService.cs | 7 +- .../LiveTv/StreamResponseInfo.cs | 20 --- .../MediaBrowser.Controller.csproj | 2 - .../ContentDirectory/ControlHandler.cs | 111 +++++++++++---- MediaBrowser.Dlna/Didl/DidlBuilder.cs | 13 +- MediaBrowser.Dlna/DlnaManager.cs | 5 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 3 + .../MediaBrowser.Model.Portable.csproj | 6 + .../MediaBrowser.Model.net35.csproj | 6 + .../Configuration/ServerConfiguration.cs | 6 + MediaBrowser.Model/Dlna/StreamBuilder.cs | 1 - MediaBrowser.Model/Dlna/TranscodingProfile.cs | 3 - MediaBrowser.Model/Dto/BaseItemDto.cs | 15 +- MediaBrowser.Model/Dto/DtoOptions.cs | 32 +++++ MediaBrowser.Model/MediaBrowser.Model.csproj | 2 + MediaBrowser.Model/Querying/ItemFields.cs | 25 ++++ MediaBrowser.Model/Users/UserPolicy.cs | 11 ++ .../Connect/ConnectManager.cs | 41 +++++- .../Dto/DtoService.cs | 154 +++++++++++++-------- .../Library/LibraryManager.cs | 46 ++++-- .../Library/ResolverHelper.cs | 7 +- .../Library/UserManager.cs | 15 +- .../LiveTv/LiveTvDtoService.cs | 16 ++- .../Localization/Server/server.json | 2 +- 44 files changed, 519 insertions(+), 205 deletions(-) delete mode 100644 MediaBrowser.Controller/Dlna/DlnaIconResponse.cs delete mode 100644 MediaBrowser.Controller/LiveTv/StreamResponseInfo.cs create mode 100644 MediaBrowser.Model/Dto/DtoOptions.cs create mode 100644 MediaBrowser.Model/Users/UserPolicy.cs (limited to 'MediaBrowser.Server.Implementations/Localization') diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 06d8e0478..5cb007f8f 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Library var fields = Enum.GetNames(typeof(ItemFields)) .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) .ToList(); - + var result = new ItemsResult { TotalRecordCount = items.Count, @@ -353,7 +353,7 @@ namespace MediaBrowser.Api.Library .ToList(); BaseItem parent = item.Parent; - + while (parent != null) { if (user != null) @@ -607,7 +607,7 @@ namespace MediaBrowser.Api.Library } } } - + var dtos = themeSongIds.Select(_libraryManager.GetItemById) .OrderBy(i => i.SortName) .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item)); diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index ef86a46e8..8d6568d79 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -208,7 +208,7 @@ namespace MediaBrowser.Api.Movies { returnItems = returnItems.Take(request.Limit.Value); } - + var result = new ItemsResult { Items = returnItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(), diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index eebdafc5c..e24fa4964 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -16,6 +16,7 @@ namespace MediaBrowser.Api /// Class GetPackage /// [Route("/Packages/{Name}", "GET", Summary = "Gets a package, by name or assembly guid")] + [Authenticated] public class GetPackage : IReturn { /// @@ -37,6 +38,7 @@ namespace MediaBrowser.Api /// Class GetPackages /// [Route("/Packages", "GET", Summary = "Gets available packages")] + [Authenticated] public class GetPackages : IReturn> { /// @@ -60,6 +62,7 @@ namespace MediaBrowser.Api /// Class GetPackageVersionUpdates /// [Route("/Packages/Updates", "GET", Summary = "Gets available package updates for currently installed packages")] + [Authenticated(Roles = "Admin")] public class GetPackageVersionUpdates : IReturn> { /// @@ -74,6 +77,7 @@ namespace MediaBrowser.Api /// Class InstallPackage /// [Route("/Packages/Installed/{Name}", "POST", Summary = "Installs a package")] + [Authenticated(Roles = "Admin")] public class InstallPackage : IReturnVoid { /// @@ -109,6 +113,7 @@ namespace MediaBrowser.Api /// Class CancelPackageInstallation /// [Route("/Packages/Installing/{Id}", "DELETE", Summary = "Cancels a package installation")] + [Authenticated(Roles = "Admin")] public class CancelPackageInstallation : IReturnVoid { /// @@ -122,7 +127,6 @@ namespace MediaBrowser.Api /// /// Class PackageService /// - [Authenticated(Roles = "Admin")] public class PackageService : BaseApiService { private readonly IInstallationManager _installationManager; @@ -139,7 +143,6 @@ namespace MediaBrowser.Api /// /// The request. /// System.Object. - /// Unsupported PackageType public object Get(GetPackageVersionUpdates request) { var result = new List(); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 21c4a3dff..12ccfb6b1 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -2011,11 +2011,6 @@ namespace MediaBrowser.Api.Playback state.EstimateContentLength = transcodingProfile.EstimateContentLength; state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; - - if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile)) - { - state.VideoRequest.Profile = transcodingProfile.VideoProfile; - } } } diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs index 5325e9c44..7f7717f71 100644 --- a/MediaBrowser.Api/PlaylistService.cs +++ b/MediaBrowser.Api/PlaylistService.cs @@ -151,7 +151,7 @@ namespace MediaBrowser.Api { items = items.Take(request.Limit.Value).ToArray(); } - + var dtos = items .Select(i => _dtoService.GetBaseItemDto(i.Item2, request.GetItemFields().ToList(), user)) .ToArray(); diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index fd5019126..4443b2a2b 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -61,6 +61,7 @@ namespace MediaBrowser.Api public void Post(ReportStartupWizardComplete request) { _config.Configuration.IsStartupWizardCompleted = true; + _config.Configuration.EnableLocalizedGuids = true; _config.SaveConfiguration(); } diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index 07015ecae..2299b2b1a 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary if (request.UserId.HasValue) { var user = UserManager.GetUserById(request.UserId.Value); - + return DtoService.GetBaseItemDto(item, fields.ToList(), user); } diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 3ae53daf8..9d211a419 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -127,11 +127,11 @@ namespace MediaBrowser.Api.UserLibrary } - var fields = request.GetItemFields().ToList(); - var tuples = ibnItems.Select(i => new Tuple>(i, i.GetTaggedItems(libraryItems).ToList())); - var dtos = tuples.Select(i => GetDto(i.Item1, user, fields, i.Item2)); + var dtoOptions = request.GetDtoOptions(); + + var dtos = tuples.Select(i => GetDto(i.Item1, user, dtoOptions, i.Item2)); result.Items = dtos.Where(i => i != null).ToArray(); @@ -332,12 +332,12 @@ namespace MediaBrowser.Api.UserLibrary /// /// The item. /// The user. - /// The fields. + /// The options. /// The library items. /// Task{DtoBaseItem}. - private BaseItemDto GetDto(TItemType item, User user, List fields, List libraryItems) + private BaseItemDto GetDto(TItemType item, User user, DtoOptions options, List libraryItems) { - var dto = DtoService.GetItemByNameDto(item, fields, libraryItems, user); + var dto = DtoService.GetItemByNameDto(item, options, libraryItems, user); return dto; } diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 6b0c64b79..808dbb1ff 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using ServiceStack; using System; @@ -9,6 +10,11 @@ namespace MediaBrowser.Api.UserLibrary { public abstract class BaseItemsRequest : IHasItemFields { + protected BaseItemsRequest() + { + EnableImages = true; + } + /// /// Skips over a given number of items within the results. Use for paging. /// @@ -116,6 +122,15 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "Years", Description = "Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Years { get; set; } + [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] + public bool EnableImages { get; set; } + + [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? ImageTypeLimit { get; set; } + + [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string EnableImageTypes { get; set; } + public string[] GetGenres() { return (Genres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); @@ -198,5 +213,35 @@ namespace MediaBrowser.Api.UserLibrary return val.Split(','); } + + public DtoOptions GetDtoOptions() + { + var options = new DtoOptions(); + + options.Fields = this.GetItemFields().ToList(); + options.EnableImages = EnableImages; + + if (ImageTypeLimit.HasValue) + { + options.ImageTypeLimit = ImageTypeLimit.Value; + } + + if (string.IsNullOrWhiteSpace(EnableImageTypes)) + { + if (options.EnableImages) + { + // Get everything + options.ImageTypes = Enum.GetNames(typeof(ImageType)) + .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true)) + .ToList(); + } + } + else + { + options.ImageTypes = (EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList(); + } + + return options; + } } } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index b87ee895a..d15556f55 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -321,15 +321,14 @@ namespace MediaBrowser.Api.UserLibrary var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false); var isFiltered = result.Item2; + var dtoOptions = request.GetDtoOptions(); if (isFiltered) { - var currentFields = request.GetItemFields().ToList(); - return new ItemsResult { TotalRecordCount = result.Item1.TotalRecordCount, - Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, currentFields, user)).ToArray() + Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray() }; } @@ -363,9 +362,7 @@ namespace MediaBrowser.Api.UserLibrary var pagedItems = ApplyPaging(request, itemsArray); - var fields = request.GetItemFields().ToList(); - - var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(); + var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray(); return new ItemsResult { diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index fd0e79a21..511312a63 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -374,7 +374,7 @@ namespace MediaBrowser.Api.UserLibrary item = i.Item1; childCount = i.Item2.Count; } - + var dto = _dtoService.GetBaseItemDto(item, fields, user); dto.ChildCount = childCount; diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index be2fbffc6..4daee4875 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -96,6 +96,8 @@ namespace MediaBrowser.Common.Extensions /// The STR. /// The type. /// Guid. + /// type + [Obsolete("Use LibraryManager.GetNewItemId")] public static Guid GetMBId(this string str, Type type) { if (type == null) diff --git a/MediaBrowser.Controller/Dlna/DlnaIconResponse.cs b/MediaBrowser.Controller/Dlna/DlnaIconResponse.cs deleted file mode 100644 index 5ae92082d..000000000 --- a/MediaBrowser.Controller/Dlna/DlnaIconResponse.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.IO; -using MediaBrowser.Model.Drawing; - -namespace MediaBrowser.Controller.Dlna -{ - public class DlnaIconResponse : IDisposable - { - public Stream Stream { get; set; } - - public ImageFormat Format { get; set; } - - public void Dispose() - { - if (Stream != null) - { - Stream.Dispose(); - Stream = null; - } - } - } -} diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs index b7a06b368..34464f6a2 100644 --- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs +++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Dlna; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.Dlna; using System.Collections.Generic; namespace MediaBrowser.Controller.Dlna @@ -69,6 +70,6 @@ namespace MediaBrowser.Controller.Dlna /// /// The filename. /// DlnaIconResponse. - DlnaIconResponse GetIcon(string filename); + ImageStream GetIcon(string filename); } } diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 61b2caec0..7c7ec56d5 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -22,7 +22,8 @@ namespace MediaBrowser.Controller.Dto /// /// The dto. /// The item. - void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item); + /// The fields. + void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List fields); /// /// Gets the base item dto. @@ -34,6 +35,16 @@ namespace MediaBrowser.Controller.Dto /// Task{BaseItemDto}. BaseItemDto GetBaseItemDto(BaseItem item, List fields, User user = null, BaseItem owner = null); + /// + /// Gets the base item dto. + /// + /// The item. + /// The options. + /// The user. + /// The owner. + /// BaseItemDto. + BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null); + /// /// Gets the chapter information dto. /// @@ -51,12 +62,13 @@ namespace MediaBrowser.Controller.Dto /// /// Gets the item by name dto. /// + /// /// The item. - /// The fields. + /// The options. /// The tagged items. /// The user. /// BaseItemDto. - BaseItemDto GetItemByNameDto(T item, List fields, List taggedItems, User user = null) + BaseItemDto GetItemByNameDto(T item, DtoOptions options, List taggedItems, User user = null) where T : BaseItem, IItemByName; } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 990ea49f6..ea615f023 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1820,7 +1820,11 @@ namespace MediaBrowser.Controller.Entities if (pct > 0) { pct = userData.PlaybackPositionTicks / pct; - dto.PlayedPercentage = 100 * pct; + + if (pct > 0) + { + dto.PlayedPercentage = 100 * pct; + } } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 34f52aac5..0e712dc45 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Controller.Entities if (item.Id == Guid.Empty) { - item.Id = item.Path.GetMBId(item.GetType()); + item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType()); } if (ActualChildren.Any(i => i.Id == item.Id)) diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 0bbd2eeca..3fa0a0435 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Connect; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Users; using System; using System.IO; using System.Linq; @@ -287,7 +288,7 @@ namespace MediaBrowser.Controller.Entities var localTime = date.ToLocalTime(); - return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) && + return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) && IsWithinTime(schedule, localTime); } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 3367f98e4..10fee05a9 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -414,5 +414,13 @@ namespace MediaBrowser.Controller.Library IEnumerable GetAdditionalParts(string file, VideoType type, IEnumerable files); + + /// + /// Gets the new item identifier. + /// + /// The key. + /// The type. + /// Guid. + Guid GetNewItemId(string key, Type type); } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index bd44f786f..debdafe4d 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -164,5 +164,19 @@ namespace MediaBrowser.Controller.Library /// The pin. /// true if XXXX, false otherwise. Task RedeemPasswordResetPin(string pin); + + /// + /// Gets the user policy. + /// + /// The user identifier. + /// UserPolicy. + UserPolicy GetUserPolicy(string userId); + + /// + /// Updates the user policy. + /// + /// The user identifier. + /// The user policy. + Task UpdateUserPolicy(string userId, UserPolicy userPolicy); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs index eda69b164..993db0004 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvService.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Drawing; namespace MediaBrowser.Controller.LiveTv { @@ -109,7 +110,7 @@ namespace MediaBrowser.Controller.LiveTv /// The channel identifier. /// The cancellation token. /// Task{Stream}. - Task GetChannelImageAsync(string channelId, CancellationToken cancellationToken); + Task GetChannelImageAsync(string channelId, CancellationToken cancellationToken); /// /// Gets the recording image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to RecordingInfo @@ -117,7 +118,7 @@ namespace MediaBrowser.Controller.LiveTv /// The recording identifier. /// The cancellation token. /// Task{ImageResponseInfo}. - Task GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken); + Task GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken); /// /// Gets the program image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ProgramInfo @@ -126,7 +127,7 @@ namespace MediaBrowser.Controller.LiveTv /// The channel identifier. /// The cancellation token. /// Task{ImageResponseInfo}. - Task GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken); + Task GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken); /// /// Gets the recordings asynchronous. diff --git a/MediaBrowser.Controller/LiveTv/StreamResponseInfo.cs b/MediaBrowser.Controller/LiveTv/StreamResponseInfo.cs deleted file mode 100644 index cb6aa22d2..000000000 --- a/MediaBrowser.Controller/LiveTv/StreamResponseInfo.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.IO; -using MediaBrowser.Model.Drawing; - -namespace MediaBrowser.Controller.LiveTv -{ - public class StreamResponseInfo - { - /// - /// Gets or sets the stream. - /// - /// The stream. - public Stream Stream { get; set; } - - /// - /// Gets or sets the type of the MIME. - /// - /// The type of the MIME. - public ImageFormat Format { get; set; } - } -} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 931bc51d1..dbff88fd8 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -105,7 +105,6 @@ - @@ -191,7 +190,6 @@ - diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 438b5a4ce..d979e3d89 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -236,7 +236,7 @@ namespace MediaBrowser.Dlna.ContentDirectory foreach (var i in childrenResult.Items) { var childItem = i.Item; - var displayStubType = GetDisplayStubType(childItem, serverItem.Item); + var displayStubType = i.StubType; if (childItem.IsFolder || displayStubType.HasValue) { @@ -263,29 +263,6 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } - private StubType? GetDisplayStubType(BaseItem item, BaseItem context) - { - if (context == null || context.IsFolder) - { - var movie = item as Movie; - if (movie != null) - { - if (movie.LocalTrailerIds.Count > 0 || - movie.SpecialFeatureIds.Count > 0) - { - return StubType.Folder; - } - - if (movie.People.Count > 0) - { - return StubType.Folder; - } - } - } - - return null; - } - private async Task>> HandleSearch(Headers sparams, User user, string deviceId) { var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", "")); @@ -418,11 +395,36 @@ namespace MediaBrowser.Dlna.ContentDirectory { if (stubType.HasValue) { - var movie = item as Movie; + if (stubType.Value == StubType.People) + { + var items = item.People.Select(i => + { + try + { + return _libraryManager.GetPerson(i.Name); + } + catch + { + return null; + } + + }).Where(i => i != null).ToArray(); + + var result = new QueryResult + { + Items = items.Select(i => new ServerItem { Item = i }).ToArray(), + TotalRecordCount = items.Length + }; - if (movie != null) + return ApplyPaging(result, startIndex, limit); + } + if (stubType.Value == StubType.Folder) { - return await GetMovieItems(movie).ConfigureAwait(false); + var movie = item as Movie; + if (movie != null) + { + return ApplyPaging(await GetMovieItems(movie).ConfigureAwait(false), startIndex, limit); + } } } @@ -445,13 +447,52 @@ namespace MediaBrowser.Dlna.ContentDirectory }).ConfigureAwait(false); + var serverItems = queryResult + .Items + .Select(i => new ServerItem + { + Item = i, + StubType = GetDisplayStubType(i, item) + }) + .ToArray(); + return new QueryResult { TotalRecordCount = queryResult.TotalRecordCount, - Items = queryResult.Items.Select(i => new ServerItem { Item = i, StubType = null }).ToArray() + Items = serverItems }; } + private QueryResult ApplyPaging(QueryResult result, int? startIndex, int? limit) + { + result.Items = result.Items.Skip(startIndex ?? 0).Take(limit ?? int.MaxValue).ToArray(); + + return result; + } + + private StubType? GetDisplayStubType(BaseItem item, BaseItem context) + { + if (context == null || context.IsFolder) + { + var movie = item as Movie; + if (movie != null) + { + if (movie.LocalTrailerIds.Count > 0 || + movie.SpecialFeatureIds.Count > 0) + { + return StubType.Folder; + } + + if (movie.People.Count > 0) + { + return StubType.Folder; + } + } + } + + return null; + } + private Task> GetMovieItems(Movie item) { var list = new List(); @@ -464,6 +505,12 @@ namespace MediaBrowser.Dlna.ContentDirectory var serverItems = list.Select(i => new ServerItem { Item = i, StubType = null }) .ToList(); + serverItems.Add(new ServerItem + { + Item = item, + StubType = StubType.People + }); + return Task.FromResult(new QueryResult { Items = serverItems.ToArray(), @@ -512,6 +559,11 @@ namespace MediaBrowser.Dlna.ContentDirectory stubType = StubType.Folder; id = id.Split(new[] { '_' }, 2)[1]; } + else if (id.StartsWith("people_", StringComparison.OrdinalIgnoreCase)) + { + stubType = StubType.People; + id = id.Split(new[] { '_' }, 2)[1]; + } if (Guid.TryParse(id, out itemId)) { @@ -538,6 +590,7 @@ namespace MediaBrowser.Dlna.ContentDirectory public enum StubType { - Folder = 0 + Folder = 0, + People = 1 } } diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 565431758..5d5d24b1c 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -293,8 +293,17 @@ namespace MediaBrowser.Dlna.Didl container.AppendChild(res); } - private string GetDisplayName(BaseItem item, BaseItem context) + private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context) { + if (itemStubType.HasValue && itemStubType.Value == StubType.People) + { + if (item is Video) + { + return _localization.GetLocalizedString("HeaderCastCrew"); + } + return _localization.GetLocalizedString("HeaderPeople"); + } + var episode = item as Episode; var season = context as Season; @@ -491,7 +500,7 @@ namespace MediaBrowser.Dlna.Didl // MediaMonkey for example won't display content without a title //if (filter.Contains("dc:title")) { - AddValue(element, "dc", "title", GetDisplayName(item, context), NS_DC); + AddValue(element, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC); } element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType)); diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index 219196ecc..fad23ae42 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Plugins; using MediaBrowser.Dlna.Profiles; using MediaBrowser.Dlna.Server; @@ -469,13 +470,13 @@ namespace MediaBrowser.Dlna return new DescriptionXmlBuilder(profile, serverUuId, "").GetXml(); } - public DlnaIconResponse GetIcon(string filename) + public ImageStream GetIcon(string filename) { var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase) ? ImageFormat.Png : ImageFormat.Jpg; - return new DlnaIconResponse + return new ImageStream { Format = format, Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower()) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 82d5e0344..7fb27e25f 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -299,6 +299,9 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + // TODO: Output in webp for smaller sizes + // -f image2 -f webp + // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case. var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf) : string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf); diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 1e0c5c7ea..6f0943cfa 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -422,6 +422,9 @@ Dto\ChapterInfoDto.cs + + Dto\DtoOptions.cs + Dto\GameSystemSummary.cs @@ -1118,6 +1121,9 @@ Users\PinRedeemResult.cs + + Users\UserPolicy.cs + Properties\SharedVersion.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index e1ad7b36a..088fd023c 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -387,6 +387,9 @@ Dto\ChapterInfoDto.cs + + Dto\DtoOptions.cs + Dto\GameSystemSummary.cs @@ -1077,6 +1080,9 @@ Users\PinRedeemResult.cs + + Users\UserPolicy.cs + Properties\SharedVersion.cs diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index c9df615e1..b9eaf7001 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -55,6 +55,12 @@ namespace MediaBrowser.Model.Configuration /// true if [save local meta]; otherwise, false. public bool SaveLocalMeta { get; set; } + /// + /// Gets or sets a value indicating whether [enable localized guids]. + /// + /// true if [enable localized guids]; otherwise, false. + public bool EnableLocalizedGuids { get; set; } + /// /// Gets or sets the preferred metadata language. /// diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 4907abfd7..fb26f1ff8 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -298,7 +298,6 @@ namespace MediaBrowser.Model.Dlna playlistItem.VideoCodec = transcodingProfile.VideoCodec; playlistItem.Protocol = transcodingProfile.Protocol; playlistItem.AudioStreamIndex = audioStreamIndex; - playlistItem.VideoProfile = transcodingProfile.VideoProfile; List videoTranscodingConditions = new List(); foreach (CodecProfile i in options.Profile.CodecProfiles) diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index ad82d6fac..d9963eb75 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -29,9 +29,6 @@ namespace MediaBrowser.Model.Dlna [XmlAttribute("transcodeSeekInfo")] public TranscodeSeekInfo TranscodeSeekInfo { get; set; } - [XmlAttribute("videoProfile")] - public string VideoProfile { get; set; } - [XmlAttribute("context")] public EncodingContext Context { get; set; } diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index a9f13374b..e0f250deb 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Model.Dto public float? Metascore { get; set; } - public bool IsUnidentified { get; set; } + public bool? IsUnidentified { get; set; } public int? AnimeSeriesIndex { get; set; } @@ -217,6 +217,12 @@ namespace MediaBrowser.Model.Dto /// The run time ticks. public long? RunTimeTicks { get; set; } + /// + /// Gets or sets the recursive unplayed item count. + /// + /// The recursive unplayed item count. + public int? RecursiveUnplayedItemCount { get; set; } + /// /// Gets or sets the play access. /// @@ -235,13 +241,6 @@ namespace MediaBrowser.Model.Dto /// The production year. public int? ProductionYear { get; set; } - /// - /// Gets or sets the recursive unplayed item count. - /// - /// The recursive unplayed item count. - [Obsolete] - public int? RecursiveUnplayedItemCount { get; set; } - /// /// Gets or sets the season count. /// diff --git a/MediaBrowser.Model/Dto/DtoOptions.cs b/MediaBrowser.Model/Dto/DtoOptions.cs new file mode 100644 index 000000000..069d71fce --- /dev/null +++ b/MediaBrowser.Model/Dto/DtoOptions.cs @@ -0,0 +1,32 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Dto +{ + public class DtoOptions + { + public List Fields { get; set; } + public List ImageTypes { get; set; } + public int ImageTypeLimit { get; set; } + public bool EnableImages { get; set; } + + public DtoOptions() + { + Fields = new List(); + ImageTypes = new List(); + ImageTypeLimit = int.MaxValue; + EnableImages = true; + } + + public int GetImageLimit(ImageType type) + { + if (EnableImages && ImageTypes.Contains(type)) + { + return ImageTypeLimit; + } + + return 0; + } + } +} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index df4e6949e..f7eb54292 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -125,6 +125,7 @@ + @@ -412,6 +413,7 @@ + diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index 9ceca311c..84f6bd651 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -81,11 +81,21 @@ namespace MediaBrowser.Model.Querying /// Keywords, + /// + /// The media source count + /// + MediaSourceCount, + /// /// The media versions /// MediaSources, + /// + /// The metascore + /// + Metascore, + /// /// The metadata settings /// @@ -101,6 +111,11 @@ namespace MediaBrowser.Model.Querying /// ParentId, + /// + /// The part count + /// + PartCount, + /// /// The physical path of the item /// @@ -126,6 +141,11 @@ namespace MediaBrowser.Model.Querying /// PrimaryImageAspectRatio, + /// + /// The original primary image aspect ratio + /// + OriginalPrimaryImageAspectRatio, + /// /// The revenue /// @@ -171,6 +191,11 @@ namespace MediaBrowser.Model.Querying /// Tags, + /// + /// The vote count + /// + VoteCount, + /// /// The TMDB collection name /// diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs new file mode 100644 index 000000000..02d177747 --- /dev/null +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MediaBrowser.Model.Users +{ + public class UserPolicy + { + } +} diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs index cff49df10..cbd75cdeb 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs @@ -88,6 +88,11 @@ namespace MediaBrowser.Server.Implementations.Connect } } + private string XApplicationValue + { + get { return "Media Browser Server/" + _appHost.ApplicationVersion; } + } + public ConnectManager(ILogger logger, IApplicationPaths appPaths, IJsonSerializer json, @@ -204,9 +209,18 @@ namespace MediaBrowser.Server.Implementations.Connect postData["localAddress"] = localAddress; } - using (var stream = await _httpClient.Post(url, postData, CancellationToken.None).ConfigureAwait(false)) + var options = new HttpRequestOptions + { + Url = url, + CancellationToken = CancellationToken.None + }; + + options.SetPostData(postData); + SetApplicationHeader(options); + + using (var response = await _httpClient.Post(options).ConfigureAwait(false)) { - var data = _json.DeserializeFromStream(stream); + var data = _json.DeserializeFromStream(response.Content); _data.ServerId = data.Id; _data.AccessKey = data.AccessKey; @@ -252,6 +266,7 @@ namespace MediaBrowser.Server.Implementations.Connect options.SetPostData(postData); SetServerAccessToken(options); + SetApplicationHeader(options); // No need to examine the response using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content) @@ -398,6 +413,7 @@ namespace MediaBrowser.Server.Implementations.Connect options.SetPostData(postData); SetServerAccessToken(options); + SetApplicationHeader(options); var result = new UserLinkResult(); @@ -517,6 +533,7 @@ namespace MediaBrowser.Server.Implementations.Connect options.SetPostData(postData); SetServerAccessToken(options); + SetApplicationHeader(options); // No need to examine the response using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content) @@ -562,6 +579,7 @@ namespace MediaBrowser.Server.Implementations.Connect }; options.SetPostData(postData); + SetApplicationHeader(options); // No need to examine the response using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content) @@ -629,6 +647,7 @@ namespace MediaBrowser.Server.Implementations.Connect }; SetServerAccessToken(options); + SetApplicationHeader(options); using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) { @@ -645,6 +664,11 @@ namespace MediaBrowser.Server.Implementations.Connect } } + private void SetApplicationHeader(HttpRequestOptions options) + { + options.RequestHeaders.Add("X-Application", XApplicationValue); + } + private void SetServerAccessToken(HttpRequestOptions options) { if (string.IsNullOrWhiteSpace(ConnectAccessKey)) @@ -687,6 +711,7 @@ namespace MediaBrowser.Server.Implementations.Connect }; SetServerAccessToken(options); + SetApplicationHeader(options); try { @@ -974,6 +999,7 @@ namespace MediaBrowser.Server.Implementations.Connect options.SetPostData(postData); SetServerAccessToken(options); + SetApplicationHeader(options); try { @@ -1006,20 +1032,22 @@ namespace MediaBrowser.Server.Implementations.Connect { throw new ArgumentNullException("passwordMd5"); } - - var request = new HttpRequestOptions + + var options = new HttpRequestOptions { Url = GetConnectUrl("user/authenticate") }; - request.SetPostData(new Dictionary + options.SetPostData(new Dictionary { {"userName",username}, {"password",passwordMd5} }); + SetApplicationHeader(options); + // No need to examine the response - using (var stream = (await _httpClient.SendAsync(request, "POST").ConfigureAwait(false)).Content) + using (var response = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content) { } } @@ -1062,6 +1090,7 @@ namespace MediaBrowser.Server.Implementations.Connect options.SetPostData(postData); SetServerAccessToken(options); + SetApplicationHeader(options); try { diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index bfed3887f..b4393572e 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -69,7 +69,22 @@ namespace MediaBrowser.Server.Implementations.Dto /// item public BaseItemDto GetBaseItemDto(BaseItem item, List fields, User user = null, BaseItem owner = null) { - var dto = GetBaseItemDtoInternal(item, fields, user, owner); + var options = new DtoOptions + { + Fields = fields + }; + + // Get everything + options.ImageTypes = Enum.GetNames(typeof(ImageType)) + .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true)) + .ToList(); + + return GetBaseItemDto(item, options, user, owner); + } + + public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) + { + var dto = GetBaseItemDtoInternal(item, options, user, owner); var byName = item as IItemByName; @@ -87,8 +102,10 @@ namespace MediaBrowser.Server.Implementations.Dto return dto; } - private BaseItemDto GetBaseItemDtoInternal(BaseItem item, List fields, User user = null, BaseItem owner = null) + private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null) { + var fields = options.Fields; + if (item == null) { throw new ArgumentNullException("item"); @@ -115,7 +132,7 @@ namespace MediaBrowser.Server.Implementations.Dto { try { - AttachPrimaryImageAspectRatio(dto, item); + AttachPrimaryImageAspectRatio(dto, item, fields); } catch (Exception ex) { @@ -155,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.Dto AttachStudios(dto, item); } - AttachBasicFields(dto, item, owner, fields); + AttachBasicFields(dto, item, owner, options); if (fields.Contains(ItemFields.SyncInfo)) { @@ -174,18 +191,19 @@ namespace MediaBrowser.Server.Implementations.Dto } } - if (item is Playlist) + var playlist = item as Playlist; + if (playlist != null) { - AttachLinkedChildImages(dto, (Folder)item, user); + AttachLinkedChildImages(dto, playlist, user, options); } return dto; } - public BaseItemDto GetItemByNameDto(T item, List fields, List taggedItems, User user = null) + public BaseItemDto GetItemByNameDto(T item, DtoOptions options, List taggedItems, User user = null) where T : BaseItem, IItemByName { - var dto = GetBaseItemDtoInternal(item, fields, user); + var dto = GetBaseItemDtoInternal(item, options, user); SetItemByNameInfo(item, dto, taggedItems, user); @@ -369,36 +387,27 @@ namespace MediaBrowser.Server.Implementations.Dto dto.GameSystem = item.GameSystemName; } - /// - /// Gets the backdrop image tags. - /// - /// The item. - /// List{System.String}. - private List GetBackdropImageTags(BaseItem item) + private List GetBackdropImageTags(BaseItem item, int limit) { - return GetCacheTags(item, ImageType.Backdrop).ToList(); + return GetCacheTags(item, ImageType.Backdrop, limit).ToList(); } - /// - /// Gets the screenshot image tags. - /// - /// The item. - /// List{Guid}. - private List GetScreenshotImageTags(BaseItem item) + private List GetScreenshotImageTags(BaseItem item, int limit) { var hasScreenshots = item as IHasScreenshots; if (hasScreenshots == null) { return new List(); } - return GetCacheTags(item, ImageType.Screenshot).ToList(); + return GetCacheTags(item, ImageType.Screenshot, limit).ToList(); } - private IEnumerable GetCacheTags(BaseItem item, ImageType type) + private IEnumerable GetCacheTags(BaseItem item, ImageType type, int limit) { return item.GetImages(type) .Select(p => GetImageCacheTag(item, p)) .Where(i => i != null) + .Take(limit) .ToList(); } @@ -649,9 +658,11 @@ namespace MediaBrowser.Server.Implementations.Dto /// The dto. /// The item. /// The owner. - /// The fields. - private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem owner, List fields) + /// The options. + private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem owner, DtoOptions options) { + var fields = options.Fields; + if (fields.Contains(ItemFields.DateCreated)) { dto.DateCreated = item.DateCreated; @@ -662,7 +673,11 @@ namespace MediaBrowser.Server.Implementations.Dto dto.DisplayMediaType = item.DisplayMediaType; } - dto.IsUnidentified = item.IsUnidentified; + // Leave null if false + if (item.IsUnidentified) + { + dto.IsUnidentified = item.IsUnidentified; + } if (fields.Contains(ItemFields.Settings)) { @@ -736,10 +751,13 @@ namespace MediaBrowser.Server.Implementations.Dto dto.AspectRatio = hasAspectRatio.AspectRatio; } - var hasMetascore = item as IHasMetascore; - if (hasMetascore != null) + if (fields.Contains(ItemFields.ProductionLocations)) { - dto.Metascore = hasMetascore.Metascore; + var hasMetascore = item as IHasMetascore; + if (hasMetascore != null) + { + dto.Metascore = hasMetascore.Metascore; + } } if (fields.Contains(ItemFields.AwardSummary)) @@ -751,11 +769,19 @@ namespace MediaBrowser.Server.Implementations.Dto } } - dto.BackdropImageTags = GetBackdropImageTags(item); + var backdropLimit = options.GetImageLimit(ImageType.Backdrop); + if (backdropLimit > 0) + { + dto.BackdropImageTags = GetBackdropImageTags(item, backdropLimit); + } if (fields.Contains(ItemFields.ScreenshotImageTags)) { - dto.ScreenshotImageTags = GetScreenshotImageTags(item); + var screenshotLimit = options.GetImageLimit(ImageType.Screenshot); + if (screenshotLimit > 0) + { + dto.ScreenshotImageTags = GetScreenshotImageTags(item, screenshotLimit); + } } if (fields.Contains(ItemFields.Genres)) @@ -769,11 +795,14 @@ namespace MediaBrowser.Server.Implementations.Dto var currentItem = item; foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type))) { - var tag = GetImageCacheTag(item, image); - - if (tag != null) + if (options.GetImageLimit(image.Type) > 0) { - dto.ImageTags[image.Type] = tag; + var tag = GetImageCacheTag(item, image); + + if (tag != null) + { + dto.ImageTags[image.Type] = tag; + } } } @@ -851,14 +880,14 @@ namespace MediaBrowser.Server.Implementations.Dto } // If there are no backdrops, indicate what parent has them in case the Ui wants to allow inheritance - if (dto.BackdropImageTags.Count == 0) + if (backdropLimit > 0 && dto.BackdropImageTags.Count == 0) { var parentWithBackdrop = GetParentBackdropItem(item, owner); if (parentWithBackdrop != null) { dto.ParentBackdropItemId = GetDtoId(parentWithBackdrop); - dto.ParentBackdropImageTags = GetBackdropImageTags(parentWithBackdrop); + dto.ParentBackdropImageTags = GetBackdropImageTags(parentWithBackdrop, backdropLimit); } } @@ -874,7 +903,7 @@ namespace MediaBrowser.Server.Implementations.Dto dto.ParentIndexNumber = item.ParentIndexNumber; // If there is no logo, indicate what parent has one in case the Ui wants to allow inheritance - if (!dto.HasLogo) + if (!dto.HasLogo && options.GetImageLimit(ImageType.Logo) > 0) { var parentWithLogo = GetParentImageItem(item, ImageType.Logo, owner); @@ -887,7 +916,7 @@ namespace MediaBrowser.Server.Implementations.Dto } // If there is no art, indicate what parent has one in case the Ui wants to allow inheritance - if (!dto.HasArtImage) + if (!dto.HasArtImage && options.GetImageLimit(ImageType.Thumb) > 0) { var parentWithImage = GetParentImageItem(item, ImageType.Art, owner); @@ -900,7 +929,7 @@ namespace MediaBrowser.Server.Implementations.Dto } // If there is no thumb, indicate what parent has one in case the Ui wants to allow inheritance - if (!dto.HasThumb) + if (!dto.HasThumb && options.GetImageLimit(ImageType.Thumb) > 0) { var parentWithImage = GetParentImageItem(item, ImageType.Thumb, owner); @@ -953,7 +982,11 @@ namespace MediaBrowser.Server.Implementations.Dto dto.Type = item.GetClientTypeName(); dto.CommunityRating = item.CommunityRating; - dto.VoteCount = item.VoteCount; + + if (fields.Contains(ItemFields.VoteCount)) + { + dto.VoteCount = item.VoteCount; + } if (item.IsFolder) { @@ -1017,8 +1050,15 @@ namespace MediaBrowser.Server.Implementations.Dto dto.IsoType = video.IsoType; dto.IsHD = video.IsHD; - dto.PartCount = video.AdditionalPartIds.Count + 1; - dto.MediaSourceCount = video.MediaSourceCount; + if (fields.Contains(ItemFields.Chapters)) + { + dto.PartCount = video.AdditionalPartIds.Count + 1; + } + + if (fields.Contains(ItemFields.MediaSourceCount)) + { + dto.MediaSourceCount = video.MediaSourceCount; + } if (fields.Contains(ItemFields.Chapters)) { @@ -1200,28 +1240,28 @@ namespace MediaBrowser.Server.Implementations.Dto } } - private void AttachLinkedChildImages(BaseItemDto dto, Folder folder, User user) + private void AttachLinkedChildImages(BaseItemDto dto, Folder folder, User user, DtoOptions options) { List linkedChildren = null; - if (dto.BackdropImageTags.Count == 0) + var backdropLimit = options.GetImageLimit(ImageType.Backdrop); + + if (backdropLimit > 0 && dto.BackdropImageTags.Count == 0) { - if (linkedChildren == null) - { - linkedChildren = user == null - ? folder.GetRecursiveChildren().ToList() - : folder.GetRecursiveChildren(user, true).ToList(); - } + linkedChildren = user == null + ? folder.GetRecursiveChildren().ToList() + : folder.GetRecursiveChildren(user, true).ToList(); + var parentWithBackdrop = linkedChildren.FirstOrDefault(i => i.GetImages(ImageType.Backdrop).Any()); if (parentWithBackdrop != null) { dto.ParentBackdropItemId = GetDtoId(parentWithBackdrop); - dto.ParentBackdropImageTags = GetBackdropImageTags(parentWithBackdrop); + dto.ParentBackdropImageTags = GetBackdropImageTags(parentWithBackdrop, backdropLimit); } } - if (!dto.ImageTags.ContainsKey(ImageType.Primary)) + if (!dto.ImageTags.ContainsKey(ImageType.Primary) && options.GetImageLimit(ImageType.Primary) > 0) { if (linkedChildren == null) { @@ -1380,8 +1420,9 @@ namespace MediaBrowser.Server.Implementations.Dto /// /// The dto. /// The item. + /// The fields. /// Task. - public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item) + public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List fields) { var imageInfo = item.GetImageInfo(ImageType.Primary, 0); @@ -1412,7 +1453,10 @@ namespace MediaBrowser.Server.Implementations.Dto return; } - dto.OriginalPrimaryImageAspectRatio = size.Width / size.Height; + if (fields.Contains(ItemFields.OriginalPrimaryImageAspectRatio)) + { + dto.OriginalPrimaryImageAspectRatio = size.Width / size.Height; + } var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList(); diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 63ced8559..81751545c 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -17,7 +17,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Naming.Audio; using MediaBrowser.Naming.Common; -using MediaBrowser.Naming.IO; using MediaBrowser.Naming.Video; using MediaBrowser.Server.Implementations.Library.Resolvers.TV; using MediaBrowser.Server.Implementations.Library.Validators; @@ -485,12 +484,36 @@ namespace MediaBrowser.Server.Implementations.Library if (item != null) { - ResolverHelper.SetInitialItemValues(item, args, _fileSystem); + ResolverHelper.SetInitialItemValues(item, args, _fileSystem, this); } return item; } + public Guid GetNewItemId(string key, Type type) + { + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentNullException("key"); + } + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (ConfigurationManager.Configuration.EnableLocalizedGuids && key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath)) + { + // Try to normalize paths located underneath program-data in an attempt to make them more portable + key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length) + .TrimStart(new[] { '/', '\\' }) + .Replace("/", "\\"); + } + + key = type.FullName + key.ToLower(); + + return key.GetMD5(); + } + public IEnumerable ReplaceVideosWithPrimaryVersions(IEnumerable items) { var dict = new Dictionary(); @@ -651,7 +674,7 @@ namespace MediaBrowser.Server.Implementations.Library Directory.CreateDirectory(rootFolderPath); - var rootFolder = GetItemById(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath)); + var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath)); // Add in the plug-in folders foreach (var child in PluginFolderCreators) @@ -662,7 +685,14 @@ namespace MediaBrowser.Server.Implementations.Library { if (folder.Id == Guid.Empty) { - folder.Id = (folder.Path ?? folder.GetType().Name).GetMBId(folder.GetType()); + if (string.IsNullOrWhiteSpace(folder.Path)) + { + folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType()); + } + else + { + folder.Id = GetNewItemId(folder.Path, folder.GetType()); + } } folder = GetItemById(folder.Id) as BasePluginFolder ?? folder; @@ -685,7 +715,7 @@ namespace MediaBrowser.Server.Implementations.Library Directory.CreateDirectory(userRootPath); - _userRootFolder = GetItemById(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ?? + _userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder ?? (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)); } @@ -801,7 +831,7 @@ namespace MediaBrowser.Server.Implementations.Library Path.Combine(path, validFilename) : Path.Combine(path, subFolderPrefix, validFilename); - var id = fullPath.GetMBId(type); + var id = GetNewItemId(fullPath, type); BaseItem obj; @@ -1513,7 +1543,7 @@ namespace MediaBrowser.Server.Implementations.Library path = Path.Combine(path, _fileSystem.GetValidFilename(type)); - var id = (path + "_namedview_" + name).GetMBId(typeof(UserView)); + var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView)); var item = GetItemById(id) as UserView; @@ -1578,7 +1608,7 @@ namespace MediaBrowser.Server.Implementations.Library throw new ArgumentNullException("viewType"); } - var id = ("7_namedview_" + name + user.Id.ToString("N") + parentId).GetMBId(typeof(UserView)); + var id = GetNewItemId("7_namedview_" + name + user.Id.ToString("N") + parentId, typeof(UserView)); var path = BaseItem.GetInternalMetadataPathForId(id); diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs index 92c837932..d071fd232 100644 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using System; @@ -20,7 +19,7 @@ namespace MediaBrowser.Server.Implementations.Library /// The item. /// The args. /// The file system. - public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem) + public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem, ILibraryManager libraryManager) { // If the resolver didn't specify this if (string.IsNullOrEmpty(item.Path)) @@ -34,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.Library item.Parent = args.Parent; } - item.Id = item.Path.GetMBId(item.GetType()); + item.Id = libraryManager.GetNewItemId(item.Path, item.GetType()); // If the resolver didn't specify this if (string.IsNullOrEmpty(item.DisplayMediaType)) diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index 54c584d47..ed45e890b 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -17,6 +17,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Users; using System; @@ -327,7 +328,10 @@ namespace MediaBrowser.Server.Implementations.Library try { - _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user); + _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user, new List + { + ItemFields.PrimaryImageAspectRatio + }); } catch (Exception ex) { @@ -765,5 +769,14 @@ namespace MediaBrowser.Server.Implementations.Library public DateTime ExpirationDate { get; set; } } + public UserPolicy GetUserPolicy(string userId) + { + throw new NotImplementedException(); + } + + public Task UpdateUserPolicy(string userId, UserPolicy userPolicy) + { + throw new NotImplementedException(); + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs index 371619c08..b3066b460 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Querying; namespace MediaBrowser.Server.Implementations.LiveTv { @@ -247,7 +248,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (imageTag != null) { dto.ImageTags[ImageType.Primary] = imageTag; - _dtoService.AttachPrimaryImageAspectRatio(dto, recording); + _dtoService.AttachPrimaryImageAspectRatio(dto, recording, new List + { + ItemFields.PrimaryImageAspectRatio + }); } if (user != null) @@ -337,7 +341,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv { dto.ImageTags[ImageType.Primary] = imageTag; - _dtoService.AttachPrimaryImageAspectRatio(dto, info); + _dtoService.AttachPrimaryImageAspectRatio(dto, info, new List + { + ItemFields.PrimaryImageAspectRatio + }); } if (currentProgram != null) @@ -401,7 +408,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (imageTag != null) { dto.ImageTags[ImageType.Primary] = imageTag; - _dtoService.AttachPrimaryImageAspectRatio(dto, item); + _dtoService.AttachPrimaryImageAspectRatio(dto, item, new List + { + ItemFields.PrimaryImageAspectRatio + }); } if (user != null) diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index e328ca2c6..6da7adee1 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -542,7 +542,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", -- cgit v1.2.3 From ea9e8b957cdf5bb335967eeb1a018c4fc2a1db53 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 11 Dec 2014 01:20:28 -0500 Subject: update sync objects --- MediaBrowser.Api/Devices/DeviceService.cs | 10 ++ MediaBrowser.Api/Session/SessionsService.cs | 7 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 2 +- MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 2 +- MediaBrowser.Controller/Entities/Game.cs | 13 +++ MediaBrowser.Controller/Entities/IHasTrailers.cs | 9 +- MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 13 +++ MediaBrowser.Controller/Entities/Movies/Movie.cs | 13 +++ MediaBrowser.Controller/Entities/TV/Series.cs | 15 ++- MediaBrowser.Controller/Entities/Trailer.cs | 5 +- .../Entities/UserViewBuilder.cs | 2 +- MediaBrowser.Controller/Sync/ISyncManager.cs | 4 +- MediaBrowser.Controller/Sync/ISyncRepository.cs | 15 +++ .../ContentDirectory/ControlHandler.cs | 23 ++++- .../MediaBrowser.Model.Portable.csproj | 3 - .../MediaBrowser.Model.net35.csproj | 3 - MediaBrowser.Model/ApiClient/IApiClient.cs | 15 +++ MediaBrowser.Model/MediaBrowser.Model.csproj | 1 - MediaBrowser.Model/Querying/ItemFields.cs | 2 +- MediaBrowser.Model/Session/ClientCapabilities.cs | 2 + MediaBrowser.Model/Sync/SyncJob.cs | 22 ++-- MediaBrowser.Model/Sync/SyncJobRequest.cs | 18 ++-- MediaBrowser.Model/Sync/SyncJobStatus.cs | 9 +- MediaBrowser.Model/Sync/SyncLimitType.cs | 7 -- .../Dto/DtoService.cs | 2 +- .../Library/CoreResolutionIgnoreRule.cs | 27 +++-- .../Library/EntityResolutionHelper.cs | 30 ------ .../Library/LocalTrailerPostScanTask.cs | 94 +++++++++++++++++ .../Library/Resolvers/Audio/AudioResolver.cs | 11 +- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 6 -- .../Library/Resolvers/TV/SeriesResolver.cs | 36 +------ .../Localization/JavaScript/ar.json | 3 +- .../Localization/JavaScript/ca.json | 3 +- .../Localization/JavaScript/cs.json | 3 +- .../Localization/JavaScript/da.json | 3 +- .../Localization/JavaScript/de.json | 5 +- .../Localization/JavaScript/el.json | 3 +- .../Localization/JavaScript/en_GB.json | 3 +- .../Localization/JavaScript/en_US.json | 3 +- .../Localization/JavaScript/es.json | 3 +- .../Localization/JavaScript/es_MX.json | 7 +- .../Localization/JavaScript/fi.json | 3 +- .../Localization/JavaScript/fr.json | 5 +- .../Localization/JavaScript/he.json | 3 +- .../Localization/JavaScript/hr.json | 3 +- .../Localization/JavaScript/it.json | 3 +- .../Localization/JavaScript/javascript.json | 6 +- .../Localization/JavaScript/kk.json | 3 +- .../Localization/JavaScript/ms.json | 3 +- .../Localization/JavaScript/nb.json | 3 +- .../Localization/JavaScript/nl.json | 5 +- .../Localization/JavaScript/pl.json | 3 +- .../Localization/JavaScript/pt_BR.json | 3 +- .../Localization/JavaScript/pt_PT.json | 3 +- .../Localization/JavaScript/ru.json | 3 +- .../Localization/JavaScript/sv.json | 3 +- .../Localization/JavaScript/tr.json | 3 +- .../Localization/JavaScript/vi.json | 3 +- .../Localization/JavaScript/zh_CN.json | 3 +- .../Localization/JavaScript/zh_TW.json | 3 +- .../Localization/Server/ar.json | 6 +- .../Localization/Server/ca.json | 6 +- .../Localization/Server/cs.json | 6 +- .../Localization/Server/da.json | 6 +- .../Localization/Server/de.json | 10 +- .../Localization/Server/el.json | 6 +- .../Localization/Server/en_GB.json | 6 +- .../Localization/Server/en_US.json | 6 +- .../Localization/Server/es.json | 6 +- .../Localization/Server/es_MX.json | 12 ++- .../Localization/Server/fi.json | 6 +- .../Localization/Server/fr.json | 14 +-- .../Localization/Server/he.json | 6 +- .../Localization/Server/hr.json | 6 +- .../Localization/Server/it.json | 6 +- .../Localization/Server/kk.json | 12 ++- .../Localization/Server/ko.json | 6 +- .../Localization/Server/ms.json | 6 +- .../Localization/Server/nb.json | 26 ++--- .../Localization/Server/nl.json | 10 +- .../Localization/Server/pl.json | 6 +- .../Localization/Server/pt_BR.json | 6 +- .../Localization/Server/pt_PT.json | 6 +- .../Localization/Server/ru.json | 32 +++--- .../Localization/Server/sv.json | 6 +- .../Localization/Server/tr.json | 6 +- .../Localization/Server/vi.json | 6 +- .../Localization/Server/zh_CN.json | 6 +- .../Localization/Server/zh_TW.json | 6 +- .../MediaBrowser.Server.Implementations.csproj | 3 +- .../Sync/MockSyncProvider.cs | 6 +- .../Sync/SyncJobProcessor.cs | 89 ++++++++++++++++ .../Sync/SyncManager.cs | 74 ++++++------- .../Sync/SyncRepository.cs | 115 ++++++++++++++++++--- .../ApplicationHost.cs | 4 +- 95 files changed, 730 insertions(+), 330 deletions(-) delete mode 100644 MediaBrowser.Model/Sync/SyncLimitType.cs delete mode 100644 MediaBrowser.Server.Implementations/Library/EntityResolutionHelper.cs create mode 100644 MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs create mode 100644 MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs (limited to 'MediaBrowser.Server.Implementations/Localization') diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index 0d86d6a5c..135397308 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -17,6 +17,9 @@ namespace MediaBrowser.Api.Devices { [ApiMember(Name = "SupportsContentUploading", Description = "SupportsContentUploading", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public bool? SupportsContentUploading { get; set; } + + [ApiMember(Name = "SupportsDeviceId", Description = "SupportsDeviceId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public bool? SupportsDeviceId { get; set; } } [Route("/Devices", "DELETE", Summary = "Deletes a device")] @@ -118,6 +121,13 @@ namespace MediaBrowser.Api.Devices devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsContentUploading == val); } + if (request.SupportsDeviceId.HasValue) + { + var val = request.SupportsDeviceId.Value; + + devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsDeviceId == val); + } + return ToOptimizedResult(devices.ToList()); } diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 772110794..551771338 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -238,6 +238,9 @@ namespace MediaBrowser.Api.Session [ApiMember(Name = "SupportsContentUploading", Description = "Determines whether camera upload is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool SupportsContentUploading { get; set; } + + [ApiMember(Name = "SupportsDeviceId", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] + public bool SupportsDeviceId { get; set; } } [Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")] @@ -516,7 +519,9 @@ namespace MediaBrowser.Api.Session MessageCallbackUrl = request.MessageCallbackUrl, - SupportsContentUploading = request.SupportsContentUploading + SupportsContentUploading = request.SupportsContentUploading, + + SupportsDeviceId = request.SupportsDeviceId }); } } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index d15556f55..cf9b0b438 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -877,7 +877,7 @@ namespace MediaBrowser.Api.UserLibrary var hasTrailers = i as IHasTrailers; if (hasTrailers != null) { - trailerCount = hasTrailers.LocalTrailerIds.Count; + trailerCount = hasTrailers.GetTrailerIds().Count; } var ok = val ? trailerCount > 0 : trailerCount == 0; diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index a64e0758a..040cad436 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -515,7 +515,7 @@ namespace MediaBrowser.Api.UserLibrary var hasTrailers = item as IHasTrailers; if (hasTrailers != null) { - trailerIds = hasTrailers.LocalTrailerIds; + trailerIds = hasTrailers.GetTrailerIds(); } var dtos = trailerIds diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index 062bdfa88..e4d032359 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -28,12 +28,14 @@ namespace MediaBrowser.Controller.Entities SoundtrackIds = new List(); RemoteTrailers = new List(); LocalTrailerIds = new List(); + RemoteTrailerIds = new List(); ThemeSongIds = new List(); ThemeVideoIds = new List(); Tags = new List(); } public List LocalTrailerIds { get; set; } + public List RemoteTrailerIds { get; set; } /// /// Gets or sets the tags. @@ -119,5 +121,16 @@ namespace MediaBrowser.Controller.Entities return id; } + + /// + /// Gets the trailer ids. + /// + /// List<Guid>. + public List GetTrailerIds() + { + var list = LocalTrailerIds.ToList(); + list.AddRange(RemoteTrailerIds); + return list; + } } } diff --git a/MediaBrowser.Controller/Entities/IHasTrailers.cs b/MediaBrowser.Controller/Entities/IHasTrailers.cs index 47779064b..bc1c7d875 100644 --- a/MediaBrowser.Controller/Entities/IHasTrailers.cs +++ b/MediaBrowser.Controller/Entities/IHasTrailers.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace MediaBrowser.Controller.Entities { - public interface IHasTrailers + public interface IHasTrailers : IHasProviderIds { /// /// Gets or sets the remote trailers. @@ -17,5 +17,12 @@ namespace MediaBrowser.Controller.Entities /// /// The local trailer ids. List LocalTrailerIds { get; set; } + List RemoteTrailerIds { get; set; } + + /// + /// Gets the trailer ids. + /// + /// List<Guid>. + List GetTrailerIds(); } } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 705cf9057..731226ede 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -21,6 +21,7 @@ namespace MediaBrowser.Controller.Entities.Movies { RemoteTrailers = new List(); LocalTrailerIds = new List(); + RemoteTrailerIds = new List(); DisplayOrder = ItemSortBy.PremiereDate; Keywords = new List(); @@ -35,6 +36,7 @@ namespace MediaBrowser.Controller.Entities.Movies } public List LocalTrailerIds { get; set; } + public List RemoteTrailerIds { get; set; } /// /// Gets or sets the remote trailers. @@ -76,6 +78,17 @@ namespace MediaBrowser.Controller.Entities.Movies } } + /// + /// Gets the trailer ids. + /// + /// List<Guid>. + public List GetTrailerIds() + { + var list = LocalTrailerIds.ToList(); + list.AddRange(RemoteTrailerIds); + return list; + } + public override IEnumerable GetChildren(User user, bool includeLinkedChildren) { var children = base.GetChildren(user, includeLinkedChildren); diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 8ae024f37..e749d89e4 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -36,6 +36,7 @@ namespace MediaBrowser.Controller.Entities.Movies SoundtrackIds = new List(); RemoteTrailers = new List(); LocalTrailerIds = new List(); + RemoteTrailerIds = new List(); ThemeSongIds = new List(); ThemeVideoIds = new List(); BoxSetIdList = new List(); @@ -49,6 +50,7 @@ namespace MediaBrowser.Controller.Entities.Movies public float? Metascore { get; set; } public List LocalTrailerIds { get; set; } + public List RemoteTrailerIds { get; set; } public List Keywords { get; set; } public List RemoteTrailers { get; set; } @@ -89,6 +91,17 @@ namespace MediaBrowser.Controller.Entities.Movies /// The name of the TMDB collection. public string TmdbCollectionName { get; set; } + /// + /// Gets the trailer ids. + /// + /// List<Guid>. + public List GetTrailerIds() + { + var list = LocalTrailerIds.ToList(); + list.AddRange(RemoteTrailerIds); + return list; + } + /// /// Gets the user data key. /// diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 3d1051b18..4c0d1fdfb 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -36,6 +36,7 @@ namespace MediaBrowser.Controller.Entities.TV SoundtrackIds = new List(); RemoteTrailers = new List(); LocalTrailerIds = new List(); + RemoteTrailerIds = new List(); DisplaySpecialsWithSeasons = true; } @@ -57,7 +58,8 @@ namespace MediaBrowser.Controller.Entities.TV public bool DisplaySpecialsWithSeasons { get; set; } public List LocalTrailerIds { get; set; } - + public List RemoteTrailerIds { get; set; } + public List RemoteTrailers { get; set; } /// @@ -109,6 +111,17 @@ namespace MediaBrowser.Controller.Entities.TV return this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Tvcom) ?? base.GetUserDataKey(); } + /// + /// Gets the trailer ids. + /// + /// List<Guid>. + public List GetTrailerIds() + { + var list = LocalTrailerIds.ToList(); + list.AddRange(RemoteTrailerIds); + return list; + } + // Studio, Genre and Rating will all be the same so makes no sense to index by these protected override IEnumerable GetIndexByOptions() { diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 07173d26f..bb165d790 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.Entities /// Class Trailer /// [Obsolete] - public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasTrailers, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo + public class Trailer : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTaglines, IHasMetascore, IHasLookupInfo { public List SoundtrackIds { get; set; } @@ -24,15 +24,12 @@ namespace MediaBrowser.Controller.Entities RemoteTrailers = new List(); Taglines = new List(); SoundtrackIds = new List(); - LocalTrailerIds = new List(); Keywords = new List(); ProductionLocations = new List(); } public float? Metascore { get; set; } - public List LocalTrailerIds { get; set; } - public List RemoteTrailers { get; set; } public List Keywords { get; set; } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index aff4af468..166d56c51 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -1428,7 +1428,7 @@ namespace MediaBrowser.Controller.Entities var hasTrailers = item as IHasTrailers; if (hasTrailers != null) { - trailerCount = hasTrailers.LocalTrailerIds.Count; + trailerCount = hasTrailers.GetTrailerIds().Count; } var ok = val ? trailerCount > 0 : trailerCount == 0; diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs index 1e744a087..1d5ab7d3e 100644 --- a/MediaBrowser.Controller/Sync/ISyncManager.cs +++ b/MediaBrowser.Controller/Sync/ISyncManager.cs @@ -1,6 +1,4 @@ -using System.IO; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Devices; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Sync; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Sync/ISyncRepository.cs b/MediaBrowser.Controller/Sync/ISyncRepository.cs index 9cce69bdc..d0cf87182 100644 --- a/MediaBrowser.Controller/Sync/ISyncRepository.cs +++ b/MediaBrowser.Controller/Sync/ISyncRepository.cs @@ -1,5 +1,6 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Sync; +using System.Collections.Generic; using System.Threading.Tasks; namespace MediaBrowser.Controller.Sync @@ -27,6 +28,13 @@ namespace MediaBrowser.Controller.Sync /// Task. Task Update(SyncJob job); + /// + /// Deletes the job. + /// + /// The identifier. + /// Task. + Task DeleteJob(string id); + /// /// Gets the jobs. /// @@ -54,5 +62,12 @@ namespace MediaBrowser.Controller.Sync /// The job item. /// Task. Task Update(SyncJobItem jobItem); + + /// + /// Gets the job items. + /// + /// The job identifier. + /// IEnumerable<SyncJobItem>. + IEnumerable GetJobItems(string jobId); } } diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 7b2b1f3f5..1faf690c9 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -492,7 +492,24 @@ namespace MediaBrowser.Dlna.ContentDirectory }, CancellationToken.None).ConfigureAwait(false); - items.AddRange(trailerResult.Items.Where(i => i.ContainsPerson(person.Name))); + var currentIds = items.Select(i => i.GetProviderId(MetadataProviders.Imdb)) + .ToList(); + + var trailersToAdd = trailerResult.Items + .Where(i => i.ContainsPerson(person.Name)) + .Where(i => + { + // Try to filter out dupes using imdb id + var imdb = i.GetProviderId(MetadataProviders.Imdb); + if (!string.IsNullOrWhiteSpace(imdb) && + currentIds.Contains(imdb, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + return true; + }); + + items.AddRange(trailersToAdd); items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending) .Skip(startIndex ?? 0) @@ -527,7 +544,7 @@ namespace MediaBrowser.Dlna.ContentDirectory var movie = item as Movie; if (movie != null) { - if (movie.LocalTrailerIds.Count > 0 || + if (movie.GetTrailerIds().Count > 0 || movie.SpecialFeatureIds.Count > 0) { return StubType.Folder; @@ -559,7 +576,7 @@ namespace MediaBrowser.Dlna.ContentDirectory list.Add(item); - list.AddRange(item.LocalTrailerIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); + list.AddRange(item.GetTrailerIds().Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); list.AddRange(item.SpecialFeatureIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); var serverItems = list.Select(i => new ServerItem { Item = i, StubType = null }) diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 6f0943cfa..bcd6e08f1 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -1043,9 +1043,6 @@ Sync\SyncJobStatus.cs - - Sync\SyncLimitType.cs - Sync\SyncQuality.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 088fd023c..55d18c1b3 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -1002,9 +1002,6 @@ Sync\SyncJobStatus.cs - - Sync\SyncLimitType.cs - Sync\SyncQuality.cs diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index b5b0b641a..13907e5c6 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -14,6 +14,7 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Search; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; +using MediaBrowser.Model.Sync; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Users; @@ -1371,6 +1372,20 @@ namespace MediaBrowser.Model.ApiClient /// Task<DevicesOptions>. Task GetDevicesOptions(); + /// + /// Updates the item. + /// + /// The item. + /// Task. + Task UpdateItem(BaseItemDto item); + + /// + /// Requests the synchronize. + /// + /// The request. + /// Task<SyncJob>. + Task RequestSync(SyncJobRequest request); + /// /// Opens the web socket. /// diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index f7eb54292..4825cb4cc 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -368,7 +368,6 @@ - diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index d0e2c9511..a5a906f95 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -165,7 +165,7 @@ namespace MediaBrowser.Model.Querying /// The series studio /// SeriesStudio, - + /// /// The soundtrack ids /// diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index cbc1501d2..fc0d3a1fb 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -13,11 +13,13 @@ namespace MediaBrowser.Model.Session public string MessageCallbackUrl { get; set; } public bool SupportsContentUploading { get; set; } + public bool SupportsDeviceId { get; set; } public ClientCapabilities() { PlayableMediaTypes = new List(); SupportedCommands = new List(); + SupportsDeviceId = true; } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs index f69fccae5..db67f3cbb 100644 --- a/MediaBrowser.Model/Sync/SyncJob.cs +++ b/MediaBrowser.Model/Sync/SyncJob.cs @@ -46,26 +46,26 @@ namespace MediaBrowser.Model.Sync /// true if [unwatched only]; otherwise, false. public bool UnwatchedOnly { get; set; } /// - /// Gets or sets the limit. + /// Gets or sets a value indicating whether [remove when watched]. /// - /// The limit. - public long? Limit { get; set; } + /// true if [remove when watched]; otherwise, false. + public bool RemoveWhenWatched { get; set; } /// - /// Gets or sets the type of the limit. + /// Gets or sets a value indicating whether [synchronize new content]. /// - /// The type of the limit. - public SyncLimitType? LimitType { get; set; } + /// true if [synchronize new content]; otherwise, false. + public bool SyncNewContent { get; set; } + /// + /// Gets or sets the item limit. + /// + /// The item limit. + public int? ItemLimit { get; set; } /// /// Gets or sets the requested item ids. /// /// The requested item ids. public List RequestedItemIds { get; set; } /// - /// Gets or sets a value indicating whether this instance is dynamic. - /// - /// true if this instance is dynamic; otherwise, false. - public bool IsDynamic { get; set; } - /// /// Gets or sets the date created. /// /// The date created. diff --git a/MediaBrowser.Model/Sync/SyncJobRequest.cs b/MediaBrowser.Model/Sync/SyncJobRequest.cs index 987f396e4..4e044d62a 100644 --- a/MediaBrowser.Model/Sync/SyncJobRequest.cs +++ b/MediaBrowser.Model/Sync/SyncJobRequest.cs @@ -35,19 +35,25 @@ namespace MediaBrowser.Model.Sync /// true if [unwatched only]; otherwise, false. public bool UnwatchedOnly { get; set; } /// - /// Gets or sets the limit. + /// Gets or sets a value indicating whether [remove when watched]. /// - /// The limit. - public long? Limit { get; set; } + /// true if [remove when watched]; otherwise, false. + public bool RemoveWhenWatched { get; set; } + /// + /// Gets or sets a value indicating whether [synchronize new content]. + /// + /// true if [synchronize new content]; otherwise, false. + public bool SyncNewContent { get; set; } /// - /// Gets or sets the type of the limit. + /// Gets or sets the limit. /// - /// The type of the limit. - public SyncLimitType? LimitType { get; set; } + /// The limit. + public int? ItemLimit { get; set; } public SyncJobRequest() { ItemIds = new List(); + SyncNewContent = true; } } } diff --git a/MediaBrowser.Model/Sync/SyncJobStatus.cs b/MediaBrowser.Model/Sync/SyncJobStatus.cs index ebe375ad8..42af96509 100644 --- a/MediaBrowser.Model/Sync/SyncJobStatus.cs +++ b/MediaBrowser.Model/Sync/SyncJobStatus.cs @@ -4,10 +4,9 @@ namespace MediaBrowser.Model.Sync public enum SyncJobStatus { Queued = 0, - Transcoding = 1, - TranscodingFailed = 2, - Transferring = 3, - Completed = 4, - Cancelled = 5 + Converting = 1, + Transferring = 2, + Completed = 3, + Cancelled = 4 } } diff --git a/MediaBrowser.Model/Sync/SyncLimitType.cs b/MediaBrowser.Model/Sync/SyncLimitType.cs deleted file mode 100644 index d20f9e33d..000000000 --- a/MediaBrowser.Model/Sync/SyncLimitType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MediaBrowser.Model.Sync -{ - public enum SyncLimitType - { - ItemCount = 0 - } -} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 6485eaf67..e1e8da5c5 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -833,7 +833,7 @@ namespace MediaBrowser.Server.Implementations.Dto var hasTrailers = item as IHasTrailers; if (hasTrailers != null) { - dto.LocalTrailerCount = hasTrailers.LocalTrailerIds.Count; + dto.LocalTrailerCount = hasTrailers.GetTrailerIds().Count; } var hasDisplayOrder = item as IHasDisplayOrder; diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index 3768b4492..7edd9541f 100644 --- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -16,6 +17,21 @@ namespace MediaBrowser.Server.Implementations.Library private readonly IFileSystem _fileSystem; private readonly ILibraryManager _libraryManager; + /// + /// Any folder named in this list will be ignored - can be added to at runtime for extensibility + /// + public static readonly List IgnoreFolders = new List + { + "metadata", + "ps3_update", + "ps3_vprm", + "extrafanart", + "extrathumbs", + ".actors", + ".wd_tv" + + }; + public CoreResolutionIgnoreRule(IFileSystem fileSystem, ILibraryManager libraryManager) { _fileSystem = fileSystem; @@ -64,7 +80,7 @@ namespace MediaBrowser.Server.Implementations.Library if (args.IsDirectory) { // Ignore any folders in our list - if (EntityResolutionHelper.IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) + if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) { return true; } @@ -88,15 +104,6 @@ namespace MediaBrowser.Server.Implementations.Library } else { - if (args.Parent != null) - { - // Don't resolve these into audio files - if (string.Equals(_fileSystem.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && _libraryManager.IsAudioFile(filename)) - { - return true; - } - } - // Ignore samples if (filename.IndexOf(".sample.", StringComparison.OrdinalIgnoreCase) != -1) { diff --git a/MediaBrowser.Server.Implementations/Library/EntityResolutionHelper.cs b/MediaBrowser.Server.Implementations/Library/EntityResolutionHelper.cs deleted file mode 100644 index 2a8c4f7f3..000000000 --- a/MediaBrowser.Server.Implementations/Library/EntityResolutionHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using System; -using System.Collections.Generic; -using System.IO; - -namespace MediaBrowser.Server.Implementations.Library -{ - /// - /// Class EntityResolutionHelper - /// - public static class EntityResolutionHelper - { - /// - /// Any folder named in this list will be ignored - can be added to at runtime for extensibility - /// - public static readonly List IgnoreFolders = new List - { - "metadata", - "ps3_update", - "ps3_vprm", - "extrafanart", - "extrathumbs", - ".actors", - ".wd_tv" - - }; - } -} diff --git a/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs new file mode 100644 index 000000000..9196bf734 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Entities; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Library +{ + public class LocalTrailerPostScanTask : ILibraryPostScanTask + { + private readonly ILibraryManager _libraryManager; + private readonly IChannelManager _channelManager; + + public LocalTrailerPostScanTask(ILibraryManager libraryManager, IChannelManager channelManager) + { + _libraryManager = libraryManager; + _channelManager = channelManager; + } + + public async Task Run(IProgress progress, CancellationToken cancellationToken) + { + var items = _libraryManager.RootFolder + .RecursiveChildren + .OfType() + .ToList(); + + var channelTrailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery + { + ExtraTypes = new[] { ExtraType.Trailer } + + }, CancellationToken.None); + var channelTrailers = channelTrailerResult.Items; + + var numComplete = 0; + + foreach (var item in items) + { + cancellationToken.ThrowIfCancellationRequested(); + + await AssignTrailers(item, channelTrailers).ConfigureAwait(false); + + numComplete++; + double percent = numComplete; + percent /= items.Count; + progress.Report(percent * 100); + } + + progress.Report(100); + } + + private async Task AssignTrailers(IHasTrailers item, BaseItem[] channelTrailers) + { + if (item is Game) + { + return; + } + + var imdbId = item.GetProviderId(MetadataProviders.Imdb); + var tmdbId = item.GetProviderId(MetadataProviders.Tmdb); + + var trailers = channelTrailers.Where(i => + { + if (!string.IsNullOrWhiteSpace(imdbId) && + string.Equals(imdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)) + { + return true; + } + if (!string.IsNullOrWhiteSpace(tmdbId) && + string.Equals(tmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)) + { + return true; + } + return false; + }); + + var trailerIds = trailers.Select(i => i.Id) + .ToList(); + + if (!trailerIds.SequenceEqual(item.RemoteTrailerIds)) + { + item.RemoteTrailerIds = trailerIds; + + var baseItem = (BaseItem)item; + await baseItem.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None) + .ConfigureAwait(false); + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 62eb1f47d..b4cda39cd 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -41,10 +41,19 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio { var collectionType = args.GetCollectionType(); + var isMixed = string.IsNullOrWhiteSpace(collectionType); + + // For conflicting extensions, give priority to videos + if (isMixed && _libraryManager.IsVideoFile(args.Path)) + { + return null; + } + var isStandalone = args.Parent == null; if (isStandalone || - string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) + string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || + isMixed) { return new Controller.Entities.Audio.Audio(); } diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 5ba07cdae..7f844ca0e 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -141,12 +141,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio if (libraryManager.IsAudioFile(fullName)) { - // Don't resolve these into audio files - if (string.Equals(fileSystem.GetFileNameWithoutExtension(fullName), BaseItem.ThemeSongFilename)) - { - continue; - } - return true; } } diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 1efe4e880..ce7491524 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -99,12 +99,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV /// The file system children. /// The directory service. /// The file system. + /// The logger. + /// The library manager. /// true if [is series folder] [the specified path]; otherwise, false. public static bool IsSeriesFolder(string path, bool considerSeasonlessEntries, IEnumerable fileSystemChildren, IDirectoryService directoryService, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager) { - // A folder with more than 3 non-season folders in will not becounted as a series - var nonSeriesFolders = 0; - foreach (var child in fileSystemChildren) { var attributes = child.Attributes; @@ -129,19 +128,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV //logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName); return true; } - - if (IsBadFolder(child.Name)) - { - logger.Debug("Invalid folder under series: {0}", child.FullName); - - nonSeriesFolders++; - } - - if (nonSeriesFolders >= 3) - { - logger.Debug("{0} not a series due to 3 or more invalid folders.", path); - return false; - } } else { @@ -179,24 +165,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV return string.Equals(extension, ".disc", StringComparison.OrdinalIgnoreCase); } - private static bool IsBadFolder(string name) - { - if (string.Equals(name, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - if (string.Equals(name, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - if (string.Equals(name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return !EntityResolutionHelper.IgnoreFolders.Contains(name, StringComparer.OrdinalIgnoreCase); - } - /// /// Determines whether [is season folder] [the specified path]. /// diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/ar.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/ar.json index ec08593ed..81c5ef633 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/ar.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/ar.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/ca.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/ca.json index 63d7b02c0..e84d0892f 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/ca.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/ca.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/cs.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/cs.json index c90af9fb1..008c3bf27 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/cs.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/cs.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/da.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/da.json index dca2f1daa..5aca8c406 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/da.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/da.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json index f3170f463..257716dd5 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/de.json @@ -610,7 +610,7 @@ "MessageInvitationSentToUser": "Eine E-Mail mit der Einladung zum Sharing ist an {0} geschickt worden.", "MessageInvitationSentToNewUser": "Eine E-Mail mit der Einladung zur Anmeldung am Media Browser ist an {0} geschickt worden.", "HeaderConnectionFailure": "Verbindungsfehler", - "MessageUnableToConnectToServer": "We're unable to connect to the selected server right now. Please try again later.", + "MessageUnableToConnectToServer": "Wir sind momentan nicht in der Lage, zum ausgew\u00e4hlten Server zu verbinden. Bitte versuche es sp\u00e4ter noch einmal.", "ButtonSelectServer": "W\u00e4hle Server", "MessagePluginConfigurationRequiresLocalAccess": "Melde dich bitte direkt an deinem lokalen Server an, um dieses Plugin konfigurieren zu k\u00f6nnen.", "MessageLoggedOutParentalControl": "Der Zugriff ist derzeit eingeschr\u00e4nkt. Bitte versuche es sp\u00e4ter erneut.", @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passw\u00f6rter wurden f\u00fcr die folgenden Benutzer zur\u00fcckgesetzt:", "HeaderInviteGuest": "Lade G\u00e4ste ein", "ButtonLinkMyMediaBrowserAccount": "Verkn\u00fcpfe jetzt meinen Account", - "MessageConnectAccountRequiredToInviteGuest": "Um G\u00e4ste einladen zu k\u00f6nnen, musst du zuerst deinen Media Browser Account auf diesem Server verkn\u00fcpfen." + "MessageConnectAccountRequiredToInviteGuest": "Um G\u00e4ste einladen zu k\u00f6nnen, musst du zuerst deinen Media Browser Account auf diesem Server verkn\u00fcpfen.", + "ButtonSync": "Synchronisieren" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/el.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/el.json index e0f427287..ca51e93be 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/el.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/el.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/en_GB.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/en_GB.json index 1412882be..72bca0232 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/en_GB.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/en_GB.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json index d0479eb2a..709d260b2 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/es.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/es.json index fa768f480..2bd8ca507 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/es.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/es.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json index 7855fff74..bf00b5454 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json @@ -485,7 +485,7 @@ "ValueOneMusicVideo": "1 video musical", "ValueMusicVideoCount": "{0} videos musicales", "HeaderOffline": "Fuera de L\u00ednea", - "HeaderUnaired": "No transmitido", + "HeaderUnaired": "No Emitido", "HeaderMissing": "Falta", "ButtonWebsite": "Sitio web", "TooltipFavorite": "Favorito", @@ -610,7 +610,7 @@ "MessageInvitationSentToUser": "Se ha enviado un correo electr\u00f3nico a {0}, invit\u00e1ndolo a aceptar tu invitaci\u00f3n para compartir.", "MessageInvitationSentToNewUser": "Se ha enviado un correo electr\u00f3nico a {0} invit\u00e1ndolo a registrarse con Media Browser.", "HeaderConnectionFailure": "Falla de Conexi\u00f3n", - "MessageUnableToConnectToServer": "We're unable to connect to the selected server right now. Please try again later.", + "MessageUnableToConnectToServer": "No es posible conectarse al servidor seleccionado en este momento. Por favor int\u00e9ntelo nuevamente m\u00e1s tarde.", "ButtonSelectServer": "Seleccionar servidor", "MessagePluginConfigurationRequiresLocalAccess": "Para configurar este complemento por favor inicie sesi\u00f3n en su servidor local directamente.", "MessageLoggedOutParentalControl": "El acceso se encuentra restringido en este momento. Por favor int\u00e9ntelo de nuevo mas tarde.", @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Las contrase\u00f1as se han restablecido para los siguientes usuarios:", "HeaderInviteGuest": "Agregar un Invitado", "ButtonLinkMyMediaBrowserAccount": "Enlazar mi cuenta ahora", - "MessageConnectAccountRequiredToInviteGuest": "Para poder agregar invitados primero necesitas vincular tu cuenta de Media Browser a este servidor." + "MessageConnectAccountRequiredToInviteGuest": "Para poder agregar invitados primero necesitas vincular tu cuenta de Media Browser a este servidor.", + "ButtonSync": "Sinc" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/fi.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/fi.json index a5ffa3cd6..a1fa3c2ad 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/fi.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/fi.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json index e2190a380..144eb1207 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json @@ -610,7 +610,7 @@ "MessageInvitationSentToUser": "Un mail a \u00e9t\u00e9 envoy\u00e9 \u00e0 {0} pour les inviter \u00e0 accepter votre invitation de partage.", "MessageInvitationSentToNewUser": "Un mail a \u00e9t\u00e9 envoy\u00e9 \u00e0 {0} pour les inviter \u00e0 s'inscrire sur Media Browser.", "HeaderConnectionFailure": "Erreur de connexion", - "MessageUnableToConnectToServer": "We're unable to connect to the selected server right now. Please try again later.", + "MessageUnableToConnectToServer": "Nous sommes dans l'impossibilit\u00e9 de nous connect\u00e9 au serveur pour le moment. Veuillez r\u00e9essayer plus tard.", "ButtonSelectServer": "S\u00e9lectionner le serveur", "MessagePluginConfigurationRequiresLocalAccess": "Pour configurer ce plugin, S.v.p. connecter vous \u00e0 votre serveur local directement.", "MessageLoggedOutParentalControl": "L'acc\u00e8s est actuellement limit\u00e9. S.v.p. r\u00e9essayez plus tard", @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Les mot de passes ont \u00e9t\u00e9 r\u00e9initialis\u00e9s pour les utilisateurs suivants:", "HeaderInviteGuest": "Inviter une personne", "ButtonLinkMyMediaBrowserAccount": "Lier \u00e0 mon compte maintenant", - "MessageConnectAccountRequiredToInviteGuest": "Pour inviter des personnes vous devez d'abord lier votre compte Media Browser \u00e0 ce serveur." + "MessageConnectAccountRequiredToInviteGuest": "Pour inviter des personnes vous devez d'abord lier votre compte Media Browser \u00e0 ce serveur.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/he.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/he.json index 323eb950e..77ea8846d 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/he.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/he.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/hr.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/hr.json index c5fb87ea9..188bc25e9 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/hr.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/hr.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/it.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/it.json index 5cbb53d29..b8e95864b 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/it.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/it.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Le password sono state rimesse per i seguenti utenti:", "HeaderInviteGuest": "Invita Ospite", "ButtonLinkMyMediaBrowserAccount": "Collega il mio account", - "MessageConnectAccountRequiredToInviteGuest": "Per invitare gli ospiti \u00e8 necessario collegare prima il tuo account browser media a questo server." + "MessageConnectAccountRequiredToInviteGuest": "Per invitare gli ospiti \u00e8 necessario collegare prima il tuo account browser media a questo server.", + "ButtonSync": "Sinc." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index d0975221e..5f2c7b2bc 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -634,5 +634,9 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync", + "SyncMedia": "Sync Media", + "HeaderCancelSyncJob": "Cancel Sync", + "CancelSyncJobConfirmation": "Are you sure you wish to cancel this sync job?" } diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json index 371087bc4..6ad161351 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "\u049a\u04b1\u043f\u0438\u044f \u0441\u04e9\u0437\u0434\u0435\u0440 \u043a\u0435\u043b\u0435\u0441\u0456 \u043f\u0430\u0439\u0434\u0430\u043b\u0430\u043d\u0443\u0448\u044b\u043b\u0430\u0440 \u04af\u0448\u0456\u043d \u044b\u0441\u044b\u0440\u044b\u043b\u0434\u044b:", "HeaderInviteGuest": "\u049a\u043e\u043d\u0430\u049b\u0442\u044b \u0448\u0430\u049b\u044b\u0440\u0443", "ButtonLinkMyMediaBrowserAccount": "\u0422\u0456\u0440\u043a\u0435\u043b\u0433\u0456\u043c\u0434\u0456 \u0431\u0430\u0439\u043b\u0430\u043d\u044b\u0441\u0442\u044b\u0440\u0443", - "MessageConnectAccountRequiredToInviteGuest": "\u049a\u043e\u043d\u0430\u049b\u0442\u0430\u0440\u0434\u044b \u0448\u0430\u049b\u044b\u0440\u0443 \u04af\u0448\u0456\u043d \u0435\u04a3 \u0430\u043b\u0434\u044b\u043d\u0434\u0430 \u043e\u0441\u044b \u0441\u0435\u0440\u0432\u0435\u0440\u0433\u0435 Media Browser \u0442\u0456\u0440\u043a\u0435\u043b\u0433\u0456\u04a3\u0456\u0437\u0434\u0456 \u0431\u0430\u0439\u043b\u0430\u043d\u044b\u0441\u0442\u0440\u0443\u044b\u04a3\u044b\u0437 \u049b\u0430\u0436\u0435\u0442." + "MessageConnectAccountRequiredToInviteGuest": "\u049a\u043e\u043d\u0430\u049b\u0442\u0430\u0440\u0434\u044b \u0448\u0430\u049b\u044b\u0440\u0443 \u04af\u0448\u0456\u043d \u0435\u04a3 \u0430\u043b\u0434\u044b\u043d\u0434\u0430 \u043e\u0441\u044b \u0441\u0435\u0440\u0432\u0435\u0440\u0433\u0435 Media Browser \u0442\u0456\u0440\u043a\u0435\u043b\u0433\u0456\u04a3\u0456\u0437\u0434\u0456 \u0431\u0430\u0439\u043b\u0430\u043d\u044b\u0441\u0442\u0440\u0443\u044b\u04a3\u044b\u0437 \u049b\u0430\u0436\u0435\u0442.", + "ButtonSync": "\u0421\u0438\u043d\u0445\u0440\u043e" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/ms.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/ms.json index 95d07be55..a82d2b462 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/ms.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/ms.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/nb.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/nb.json index 66c5fa862..baae629e1 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/nb.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/nb.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Synk" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json index fc53e577e..42917eacc 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json @@ -610,7 +610,7 @@ "MessageInvitationSentToUser": "Een email is verzonden naar {0} om je uitnodiging om media te delen te accepteren.", "MessageInvitationSentToNewUser": "Een email is verzonden naar {0} om je uitnodiging aan te melden bij Media Browser", "HeaderConnectionFailure": "Verbindingsfout", - "MessageUnableToConnectToServer": "We're unable to connect to the selected server right now. Please try again later.", + "MessageUnableToConnectToServer": "Het is momenteel niet mogelijk met de geselecteerde server te verbinden, porbeer het later opnieuw,", "ButtonSelectServer": "Selecteer server", "MessagePluginConfigurationRequiresLocalAccess": "Meld svp. op de lokale server aan om deze plugin te configureren.", "MessageLoggedOutParentalControl": "Toegang is momenteel bepertk, probeer later opnieuw.", @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Wachtwoorden zijn gereset voor de volgende gebruikers:", "HeaderInviteGuest": "Nodig gast uit", "ButtonLinkMyMediaBrowserAccount": "Koppel mijn account nu", - "MessageConnectAccountRequiredToInviteGuest": "Om gasten uit te kunnen nodigen moet je Media Browser account aan deze server gekoppeld worden." + "MessageConnectAccountRequiredToInviteGuest": "Om gasten uit te kunnen nodigen moet je Media Browser account aan deze server gekoppeld worden.", + "ButtonSync": "Synchronisatie" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/pl.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/pl.json index 14acfd921..5379e137a 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/pl.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/pl.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json index bab01f5a2..30f664681 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Foram redefinidas as senhas dos seguintes usu\u00e1rios:", "HeaderInviteGuest": "Convidar Usu\u00e1rio", "ButtonLinkMyMediaBrowserAccount": "Conectar minha conta agora", - "MessageConnectAccountRequiredToInviteGuest": "Para convidar usu\u00e1rios voc\u00ea necessita primeiro conectar sua conta Media Browser com este servidor." + "MessageConnectAccountRequiredToInviteGuest": "Para convidar usu\u00e1rios voc\u00ea necessita primeiro conectar sua conta Media Browser com este servidor.", + "ButtonSync": "Sincronizar" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json index f6433902e..06c731d67 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sincronizar" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json index 02955316a..50ed192e9 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "\u041f\u0430\u0440\u043e\u043b\u0438 \u0431\u044b\u043b\u0438 \u0441\u0431\u0440\u043e\u0448\u0435\u043d\u044b \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439:", "HeaderInviteGuest": "\u041f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u0435 \u0433\u043e\u0441\u0442\u044f", "ButtonLinkMyMediaBrowserAccount": "\u0421\u0432\u044f\u0437\u0430\u0442\u044c \u043c\u043e\u044e \u0443\u0447\u0451\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c", - "MessageConnectAccountRequiredToInviteGuest": "\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u0442\u044c \u0433\u043e\u0441\u0442\u0435\u0439, \u0432\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e, \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0443\u0447\u0451\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Media Browser \u0441 \u0434\u0430\u043d\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c." + "MessageConnectAccountRequiredToInviteGuest": "\u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0433\u043b\u0430\u0448\u0430\u0442\u044c \u0433\u043e\u0441\u0442\u0435\u0439, \u0432\u0430\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e, \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0443\u0447\u0451\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c Media Browser \u0441 \u0434\u0430\u043d\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c.", + "ButtonSync": "\u0421\u0438\u043d\u0445\u0440-\u0442\u044c" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json index 95ce375f1..c568c8664 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Synk" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/tr.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/tr.json index 782398101..1488fd151 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/tr.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/tr.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/vi.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/vi.json index c2506300c..2dbaa24c4 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/vi.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/vi.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/zh_CN.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/zh_CN.json index d83f9743d..3d2149464 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/zh_CN.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/zh_CN.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "\u540c\u6b65" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/zh_TW.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/zh_TW.json index 15f5bf454..22672ff29 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/zh_TW.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/zh_TW.json @@ -626,5 +626,6 @@ "MessagePasswordResetForUsers": "Passwords have been reset for the following users:", "HeaderInviteGuest": "Invite Guest", "ButtonLinkMyMediaBrowserAccount": "Link my account now", - "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server." + "MessageConnectAccountRequiredToInviteGuest": "In order to invite guests you need to first link your Media Browser account to this server.", + "ButtonSync": "Sync" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ar.json b/MediaBrowser.Server.Implementations/Localization/Server/ar.json index 8d23f8c2a..794755a50 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ar.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ar.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 cfe8405d0..549d1d173 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ca.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ca.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 25d5190b7..d702540ac 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/cs.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/cs.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "B\u011b\u017e\u00edc\u00ed \u00falohy", "HeaderActiveDevices": "Akt\u00edvn\u00ed za\u0159\u00edzen\u00ed", "HeaderPendingInstallations": "\u010cekaj\u00edc\u00ed instalace", - "HeaerServerInformation": "Informace o serveru", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restartovat nyn\u00ed", "ButtonRestart": "Restart", "ButtonShutdown": "Vypnout", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/da.json b/MediaBrowser.Server.Implementations/Localization/Server/da.json index 6cd18d44e..d4895c47a 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/da.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/da.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 ecbc4e671..8f10acd06 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/de.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/de.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Laufende Aufgaben", "HeaderActiveDevices": "Aktive Ger\u00e4te", "HeaderPendingInstallations": "Ausstehende Installationen", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Jetzt neustarten", "ButtonRestart": "Neu starten", "ButtonShutdown": "Herunterfahren", @@ -1273,7 +1273,9 @@ "HeaderParentalRatings": "Altersbeschr\u00e4nkung", "HeaderVideoTypes": "Videotypen", "HeaderYears": "Jahre", - "HeaderAddTag": "Add Tag", - "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "HeaderAddTag": "F\u00fcge Tag hinzu", + "LabelBlockItemsWithTags": "Blockiere Inhalte mit folgenden Tags:", + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 f6789062a..9b44ce488 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/el.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/el.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 e497fedc1..1c0a7fcb3 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/en_GB.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 d2ec40340..44547808b 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/en_US.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/en_US.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 e03198879..ada5cfc22 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/es.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/es.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Tareas en ejecuci\u00f3n", "HeaderActiveDevices": "Dispositivos activos", "HeaderPendingInstallations": "Instalaciones pendientes", - "HeaerServerInformation": "Informaci\u00f3n del servidor", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Reiniciar ahora", "ButtonRestart": "Reiniciar", "ButtonShutdown": "Apagar", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 0e26ffca1..7265ab453 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/es_MX.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Tareas en Ejecuci\u00f3n", "HeaderActiveDevices": "Dispositivos Activos", "HeaderPendingInstallations": "Instalaciones Pendientes", - "HeaerServerInformation": "Informaci\u00f3n del Servidor", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Reiniciar Ahora", "ButtonRestart": "Reiniciar", "ButtonShutdown": "Apagar", @@ -1216,7 +1216,7 @@ "HeaderCameraUploadHelp": "Suba a Media Broswer fotos y videos tomados desde sus dispositivos m\u00f3viles de forma autom\u00e1tica.", "MessageNoDevicesSupportCameraUpload": "Actualmente no cuenta con ning\u00fan dispositivo que soporte subir desde la c\u00e1mara.", "LabelCameraUploadPath": "Ruta para subir desde la c\u00e1mara:", - "LabelCameraUploadPathHelp": "Select a custom upload path, if desired. If unspecified a default folder will be used. If using a custom path it will also need to be added in the library setup area.", + "LabelCameraUploadPathHelp": "Seleccione una trayectoria personalizada de subida. Si no se especifica, una carpeta por omisi\u00f3n ser\u00e1 usada. Si usa una trayectoria personalizada, tambi\u00e9n ser\u00e1 necesario a\u00f1adirla en el \u00e1rea de configuraci\u00f3n de la biblioteca.", "LabelCreateCameraUploadSubfolder": "Crear una subcarpeta para cada dispositivo", "LabelCreateCameraUploadSubfolderHelp": "Se pueden especificar carpetas espec\u00edficas para un dispositivo haciendo clic en \u00e9l desde la p\u00e1gina de Dispositivos.", "LabelCustomDeviceDisplayName": "Nombre a Desplegar:", @@ -1273,7 +1273,9 @@ "HeaderParentalRatings": "Clasificaci\u00f3n Parental", "HeaderVideoTypes": "Tipos de Video", "HeaderYears": "A\u00f1os", - "HeaderAddTag": "Add Tag", - "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "HeaderAddTag": "Agregar Etiqueta", + "LabelBlockItemsWithTags": "Bloquear \u00edtems con etiquetas:", + "LabelTag": "Etiqueta:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/fi.json b/MediaBrowser.Server.Implementations/Localization/Server/fi.json index 5c25cf493..84cfe43bc 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/fi.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/fi.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 d9517131e..a2f4f864d 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/fr.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/fr.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "T\u00e2ches en ex\u00e9cution", "HeaderActiveDevices": "P\u00e9riph\u00e9riques actifs", "HeaderPendingInstallations": "Installations en suspens", - "HeaerServerInformation": "Information du serveur", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Red\u00e9marrer maintenant", "ButtonRestart": "Red\u00e9marrer", "ButtonShutdown": "\u00c9teindre", @@ -888,7 +888,7 @@ "LabelProtocolInfoHelp": "La valeur qui sera utilis\u00e9e pour r\u00e9pondre aux requ\u00eates GetProtocolInfo du p\u00e9riph\u00e9rique.", "TabKodiMetadata": "Kodi", "HeaderKodiMetadataHelp": "Media Browser supporte nativement les m\u00e9tadonn\u00e9es Nfo et les images de Kodi. Pour activer ou d\u00e9sactiver les m\u00e9tadonn\u00e9es Kodi, utiliser l'onglet Avanc\u00e9 pour configurer les options de vos types de m\u00e9dias.", - "LabelKodiMetadataUser": "Sync user watch data to nfo's for:", + "LabelKodiMetadataUser": "Les utilisateurs synchronis\u00e9s voient les donn\u00e9es nfo pour:", "LabelKodiMetadataUserHelp": "Activer pour garder les donn\u00e9es de visualisation synchronis\u00e9es entre Media Browser et Kodi.", "LabelKodiMetadataDateFormat": "Format de la date de sortie :", "LabelKodiMetadataDateFormatHelp": "Toutes les dates du nfo seront lues et \u00e9crites en utilisant ce format.", @@ -1216,7 +1216,7 @@ "HeaderCameraUploadHelp": "Uploader automatiquement les photos et les vid\u00e9os depuis vos p\u00e9riph\u00e9riques mobiles dans Media Browser.", "MessageNoDevicesSupportCameraUpload": "Vous n'avez actuellement aucun p\u00e9riph\u00e9riques support\u00e9 par l'upload de la cam\u00e9ra.", "LabelCameraUploadPath": "R\u00e9pertoire de l'upload de la camera:", - "LabelCameraUploadPathHelp": "Select a custom upload path, if desired. If unspecified a default folder will be used. If using a custom path it will also need to be added in the library setup area.", + "LabelCameraUploadPathHelp": "Si vous le souhaitez, vous pouvez choisir un r\u00e9pertoire d'upload personnalis\u00e9. Si vous ne mettez rien, le r\u00e9pertoire par d\u00e9faut sera utilis\u00e9. Si vous utilisez un r\u00e9pertoire personnalis\u00e9, vous devrez le rajouter \u00e0 la biblioth\u00e8que.", "LabelCreateCameraUploadSubfolder": "Cr\u00e9er un sous-dossier pour chaque p\u00e9riph\u00e9rique", "LabelCreateCameraUploadSubfolderHelp": "Des r\u00e9pertoires sp\u00e9cifiques peuvent \u00eatres affect\u00e9 \u00e0 des appareils en cliquant sur l'appareil dans la page des appareils.", "LabelCustomDeviceDisplayName": "Nom d'affichage:", @@ -1273,7 +1273,9 @@ "HeaderParentalRatings": "Note parentale", "HeaderVideoTypes": "Types de vid\u00e9o", "HeaderYears": "Ann\u00e9es", - "HeaderAddTag": "Add Tag", - "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "HeaderAddTag": "Ajouter un tag", + "LabelBlockItemsWithTags": "Bloquer les \u00e9l\u00e9ments contenant les tags:", + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 4f4691bd7..64f51f501 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/he.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/he.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "\u05de\u05e9\u05d9\u05de\u05d5\u05ea \u05e8\u05e6\u05d5\u05ea", "HeaderActiveDevices": "\u05de\u05db\u05e9\u05d9\u05e8\u05d9\u05dd \u05e4\u05e2\u05d9\u05dc\u05d9\u05dd", "HeaderPendingInstallations": "\u05d4\u05ea\u05e7\u05e0\u05d5\u05ea \u05d1\u05d4\u05de\u05ea\u05e0\u05d4", - "HeaerServerInformation": "\u05de\u05d9\u05d3\u05e2 \u05e2\u05dc \u05d4\u05e9\u05e8\u05ea", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "\u05d4\u05ea\u05d7\u05dc \u05de\u05d7\u05d3\u05e9 \u05db\u05e2\u05d8", "ButtonRestart": "\u05d4\u05ea\u05d7\u05e8 \u05de\u05d7\u05d3\u05e9", "ButtonShutdown": "\u05db\u05d1\u05d4", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/hr.json b/MediaBrowser.Server.Implementations/Localization/Server/hr.json index 17c5764ba..6ca9666f7 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/hr.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/hr.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Zadatci koji se izvode", "HeaderActiveDevices": "Aktivni ure\u0111aji", "HeaderPendingInstallations": "Instalacije u toku", - "HeaerServerInformation": "Informacije servera", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Ponovo pokreni sad", "ButtonRestart": "Ponovo pokreni", "ButtonShutdown": "Ugasi", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 9780d5856..cf8f7f081 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/it.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/it.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Operazione in corso", "HeaderActiveDevices": "Dispositivi Connessi", "HeaderPendingInstallations": "installazioni in coda", - "HeaerServerInformation": "Informazioni del server", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Riavvia Adesso", "ButtonRestart": "Riavvia", "ButtonShutdown": "Arresta Server", @@ -1275,5 +1275,7 @@ "HeaderYears": "Anni", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 37e00d0d8..df57212fa 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/kk.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/kk.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "\u041e\u0440\u044b\u043d\u0434\u0430\u043b\u044b\u043f \u0436\u0430\u0442\u049b\u0430\u043d \u0442\u0430\u043f\u0441\u044b\u0440\u043c\u0430\u043b\u0430\u0440", "HeaderActiveDevices": "\u0411\u0435\u043b\u0441\u0435\u043d\u0434\u0456 \u049b\u04b1\u0440\u044b\u043b\u0493\u044b\u043b\u0430\u0440", "HeaderPendingInstallations": "\u0411\u04e9\u0433\u0435\u043b\u0456\u0441 \u043e\u0440\u043d\u0430\u0442\u044b\u043c\u0434\u0430\u0440", - "HeaerServerInformation": "\u0421\u0435\u0440\u0432\u0435\u0440 \u043c\u04d9\u043b\u0456\u043c\u0435\u0442\u0442\u0435\u0440\u0456", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "\u049a\u0430\u0437\u0456\u0440 \u049b\u0430\u0439\u0442\u0430 \u0456\u0441\u043a\u0435 \u049b\u043e\u0441\u0443", "ButtonRestart": "\u049a\u0430\u0439\u0442\u0430 \u0456\u0441\u043a\u0435 \u049b\u043e\u0441\u0443", "ButtonShutdown": "\u0416\u04b1\u043c\u044b\u0441\u0442\u044b \u0430\u044f\u049b\u0442\u0430\u0443", @@ -571,7 +571,7 @@ "TabPlayTo": "\u049a\u04b1\u0440\u044b\u043b\u0493\u044b\u0434\u0430 \u043e\u0439\u043d\u0430\u0442\u0443", "LabelEnableDlnaServer": "DLNA \u0441\u0435\u0440\u0432\u0435\u0440\u0456\u043d \u049b\u043e\u0441\u0443", "LabelEnableDlnaServerHelp": "\u0416\u0435\u043b\u0456\u0434\u0435\u0433\u0456 UPnP \u049b\u04b1\u0440\u044b\u043b\u0493\u044b\u043b\u0430\u0440\u0493\u0430 Media Browser \u043c\u0430\u0437\u043c\u04b1\u043d\u044b\u043d \u0448\u043e\u043b\u0443 \u043c\u0435\u043d \u043e\u0439\u043d\u0430\u0442\u0443 \u04af\u0448\u0456\u043d \u0440\u04b1\u049b\u0441\u0430\u0442 \u0435\u0442\u0443.", - "LabelEnableBlastAliveMessages": "\u0411\u0435\u043b\u0441\u0435\u043d\u0434\u0456\u043b\u0456\u043a\u0442\u0456 \u0442\u0435\u043a\u0441\u0435\u0440\u0443 \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u0440\u044b\u043d \u043b\u0430\u043f \u0435\u0442\u0443", + "LabelEnableBlastAliveMessages": "\u0411\u0435\u043b\u0441\u0435\u043d\u0434\u0456\u043b\u0456\u043a\u0442\u0456 \u0442\u0435\u043a\u0441\u0435\u0440\u0443 \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u0440\u044b\u043d \u0436\u0430\u0443\u0434\u044b\u0440\u0443", "LabelEnableBlastAliveMessagesHelp": "\u0415\u0433\u0435\u0440 \u0436\u0435\u043b\u0456\u0434\u0435\u0433\u0456 \u0431\u0430\u0441\u049b\u0430 UPnP \u049b\u04b1\u0440\u044b\u043b\u0493\u044b\u043b\u0430\u0440\u044b\u043c\u0435\u043d \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u044b\u049b \u0442\u0430\u0431\u044b\u043b\u043c\u0430\u0441\u0430 \u0431\u04b1\u043d\u044b \u049b\u043e\u0441\u044b\u04a3\u044b\u0437.", "LabelBlastMessageInterval": "\u0411\u0435\u043b\u0441\u0435\u043d\u0434\u0456\u043b\u0456\u043a\u0442\u0456 \u0442\u0435\u043a\u0441\u0435\u0440\u0443 \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u0440 \u0430\u0440\u0430\u043b\u044b\u0493\u044b, \u0441", "LabelBlastMessageIntervalHelp": "\u0421\u0435\u0440\u0432\u0435\u0440 \u0431\u0435\u043b\u0441\u0435\u043d\u0434\u0456\u043b\u0456\u0433\u0456\u043d \u0442\u0435\u043a\u0441\u0435\u0440\u0443 \u0445\u0430\u0431\u0430\u0440\u043b\u0430\u0440\u0434\u044b\u04a3 \u0430\u0440\u0430 \u04b1\u0437\u0430\u049b\u0442\u044b\u0493\u044b\u043d \u0441\u0435\u043a\u0443\u043d\u0434\u0442\u0430\u0440 \u0430\u0440\u049b\u044b\u043b\u044b \u0430\u043d\u044b\u049b\u0442\u0430\u0439\u0434\u044b.", @@ -1273,7 +1273,9 @@ "HeaderParentalRatings": "\u0416\u0430\u0441\u0442\u0430\u0441 \u0441\u0430\u043d\u0430\u0442\u0442\u0430\u0440", "HeaderVideoTypes": "\u0411\u0435\u0439\u043d\u0435 \u0442\u04af\u0440\u043b\u0435\u0440\u0456", "HeaderYears": "\u0416\u044b\u043b\u0434\u0430\u0440", - "HeaderAddTag": "Add Tag", - "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "HeaderAddTag": "\u0422\u0435\u0433\u0442\u0456 \u049b\u043e\u0441\u0443", + "LabelBlockItemsWithTags": "\u041c\u044b\u043d\u0430\u0434\u0430\u0439 \u0442\u0435\u0433\u0442\u0435\u0440\u0456 \u0431\u0430\u0440 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0442\u0435\u0440\u0434\u0456 \u049b\u04b1\u0440\u0441\u0430\u0443\u043b\u0430\u0443:", + "LabelTag": "\u0422\u0435\u0433:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/ko.json b/MediaBrowser.Server.Implementations/Localization/Server/ko.json index f88135298..9cfec64e7 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ko.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ko.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 d0bade196..7481f070e 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ms.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ms.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 b2acbc3fe..4c50c3fc2 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/nb.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/nb.json @@ -1,4 +1,16 @@ { + "TitleForgotPassword": "Glemt passord", + "TitlePasswordReset": "Resett passord", + "LabelPasswordRecoveryPinCode": "Pin code:", + "HeaderPasswordReset": "Resett passord", + "HeaderParentalRatings": "Parental Ratings", + "HeaderVideoTypes": "Video Types", + "HeaderYears": "Years", + "HeaderAddTag": "Add Tag", + "LabelBlockItemsWithTags": "Block items with tags:", + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl.", "LabelExit": "Avslutt", "LabelVisitCommunity": "Bes\u00f8k oss", "LabelGithub": "Github", @@ -539,7 +551,7 @@ "HeaderRunningTasks": "Kj\u00f8rende oppgaver", "HeaderActiveDevices": "Aktive enheter", "HeaderPendingInstallations": "ventede installasjoner", - "HeaerServerInformation": "Server informasjon", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart N\u00e5", "ButtonRestart": "Restart", "ButtonShutdown": "Sl\u00e5 Av", @@ -1265,15 +1277,5 @@ "MessageGuestSharingPermissionsHelp": "De fleste funksjonene er i utgangspunktet utilgjengelig for gjester, men kan aktiveres ved behov.", "HeaderInvitations": "Invitasjoner", "LabelForgotPasswordUsernameHelp": "Enter your username, if you remember it.", - "HeaderForgotPassword": "Glemt passord", - "TitleForgotPassword": "Glemt passord", - "TitlePasswordReset": "Resett passord", - "LabelPasswordRecoveryPinCode": "Pin code:", - "HeaderPasswordReset": "Resett passord", - "HeaderParentalRatings": "Parental Ratings", - "HeaderVideoTypes": "Video Types", - "HeaderYears": "Years", - "HeaderAddTag": "Add Tag", - "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "HeaderForgotPassword": "Glemt passord" } \ 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 1591b7284..ad571f184 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/nl.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/nl.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Actieve taken", "HeaderActiveDevices": "Actieve apparaten", "HeaderPendingInstallations": "In afwachting van installaties", - "HeaerServerInformation": "Server Informatie", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Nu opnieuw opstarten", "ButtonRestart": "Herstart", "ButtonShutdown": "Afsluiten", @@ -1273,7 +1273,9 @@ "HeaderParentalRatings": "Ouderlijke toezicht", "HeaderVideoTypes": "Video types", "HeaderYears": "Jaren", - "HeaderAddTag": "Add Tag", - "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "HeaderAddTag": "Voeg tag toe", + "LabelBlockItemsWithTags": "Blokkeer items met de tag:", + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/pl.json b/MediaBrowser.Server.Implementations/Localization/Server/pl.json index 0968c1d6a..3648847bf 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/pl.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/pl.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 ffcc542ec..f04af13fb 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Tarefas em Execu\u00e7\u00e3o", "HeaderActiveDevices": "Dispositivos Ativos", "HeaderPendingInstallations": "Instala\u00e7\u00f5es Pendentes", - "HeaerServerInformation": "Informa\u00e7\u00f5es do Servidor", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Reiniciar Agora", "ButtonRestart": "Reiniciar", "ButtonShutdown": "Desligar", @@ -1275,5 +1275,7 @@ "HeaderYears": "Anos", "HeaderAddTag": "Adicionar Tag", "LabelBlockItemsWithTags": "Bloquear itens com tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 8953c2bf7..f104c79be 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Tarefas em Execu\u00e7\u00e3o", "HeaderActiveDevices": "Dispositivos Ativos", "HeaderPendingInstallations": "Instala\u00e7\u00f5es Pendentes", - "HeaerServerInformation": "Informa\u00e7\u00e3o do Servidor", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Reiniciar Agora", "ButtonRestart": "Reiniciar", "ButtonShutdown": "Encerrar", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 f61b61b34..63b6c5f46 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/ru.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/ru.json @@ -1,6 +1,6 @@ { "LabelExit": "\u0412\u044b\u0445\u043e\u0434", - "LabelVisitCommunity": "\u041f\u043e\u0441\u0435\u0442\u0438\u0442\u044c \u0421\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e", + "LabelVisitCommunity": "\u041f\u043e\u0441\u0435\u0449\u0435\u043d\u0438\u0435 \u0421\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0430", "LabelGithub": "\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 Github", "LabelSwagger": "\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Swagger", "LabelStandard": "\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442", @@ -264,7 +264,7 @@ "LabelRunServerAtStartup": "\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b", "LabelRunServerAtStartupHelp": "\u041f\u0440\u0438 \u044d\u0442\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c\u0441\u044f \u0437\u043d\u0430\u0447\u043e\u043a \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u043e\u043c \u043b\u043e\u0442\u043a\u0435 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0441\u0442\u0430\u0440\u0442\u0430 Windows. \u0414\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u043b\u0443\u0436\u0431\u044b Windows, \u0441\u043d\u0438\u043c\u0438\u0442\u0435 \u0444\u043b\u0430\u0436\u043e\u043a \u0438 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u0435 \u0441\u043b\u0443\u0436\u0431\u0443 \u0438\u0437 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Windows. \u041f\u043e\u043c\u043d\u0438\u0442\u0435, \u0447\u0442\u043e \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0438\u0445 \u0432\u043c\u0435\u0441\u0442\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0437\u043d\u0430\u0447\u043e\u043a \u0432 \u043b\u043e\u0442\u043a\u0435 \u0434\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0441\u043b\u0443\u0436\u0431\u044b.", "ButtonSelectDirectory": "\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u043a\u0430\u0442\u0430\u043b\u043e\u0433", - "LabelCustomPaths": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u043f\u0443\u0442\u0438 \u043f\u043e \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\u043c. \u041e\u0441\u0442\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u043f\u043e\u043b\u044f \u043f\u0443\u0441\u0442\u044b\u043c\u0438, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435.", + "LabelCustomPaths": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u043f\u0443\u0442\u0438 \u043f\u043e \u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\u043c. \u041e\u0441\u0442\u0430\u0432\u043b\u044f\u0439\u0442\u0435 \u043f\u043e\u043b\u044f \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c\u0438, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435.", "LabelCachePath": "\u041f\u0443\u0442\u044c \u043a\u043e \u043a\u0435\u0448\u0443:", "LabelCachePathHelp": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0444\u0430\u0439\u043b\u043e\u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0433\u043e \u043a\u044d\u0448\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0440\u0438\u0441\u0443\u043d\u043a\u043e\u0432.", "LabelImagesByNamePath": "\u041f\u0443\u0442\u044c \u043a \u0440\u0438\u0441\u0443\u043d\u043a\u0430\u043c \u0447\u0435\u0440\u0435\u0437 \u0438\u043c\u044f:", @@ -272,7 +272,7 @@ "LabelMetadataPath": "\u041f\u0443\u0442\u044c \u043a \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u043c:", "LabelMetadataPathHelp": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u044b\u0445 \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0439 \u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445, \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u043d\u0435 \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u043c\u0435\u0434\u0438\u0430\u043f\u0430\u043f\u043e\u043a.", "LabelTranscodingTempPath": "\u041f\u0443\u0442\u044c \u043a\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c \u0444\u0430\u0439\u043b\u0430\u043c \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0438:", - "LabelTranscodingTempPathHelp": "\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u0441\u044f \u0440\u0430\u0431\u043e\u0447\u0438\u0435 \u0444\u0430\u0439\u043b\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0435. \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0443\u0442\u044c, \u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0430\u043f\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.", + "LabelTranscodingTempPathHelp": "\u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u043f\u0430\u043f\u043a\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u0441\u044f \u0440\u0430\u0431\u043e\u0447\u0438\u0435 \u0444\u0430\u0439\u043b\u044b, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0435. \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0443\u0442\u044c, \u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0430\u043f\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.", "TabBasics": "\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435", "TabTV": "\u0422\u0412", "TabGames": "\u0418\u0433\u0440\u044b", @@ -458,7 +458,7 @@ "LinkGithub": "\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 Github", "LinkApiDocumentation": "\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u043e API", "LabelFriendlyServerName": "\u041f\u043e\u043d\u044f\u0442\u043d\u043e\u0435 \u0438\u043c\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430:", - "LabelFriendlyServerNameHelp": "\u0414\u0430\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440. \u0415\u0441\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0438\u043c\u044f \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430.", + "LabelFriendlyServerNameHelp": "\u0414\u0430\u043d\u043d\u043e\u0435 \u0438\u043c\u044f \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0415\u0441\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u0435 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0438\u043c\u044f \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0430.", "LabelPreferredDisplayLanguage": "\u041f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u043c\u044b\u0439 \u044f\u0437\u044b\u043a \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f:", "LabelPreferredDisplayLanguageHelp": "\u041f\u0435\u0440\u0435\u0432\u043e\u0434 Media Browser \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c \u043d\u0430\u0445\u043e\u0434\u044f\u0449\u0438\u043c\u0441\u044f \u0432 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0438 \u0438 \u0432\u0441\u0451 \u0435\u0449\u0451 \u043d\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d.", "LabelReadHowYouCanContribute": "\u0427\u0438\u0442\u0430\u0439\u0442\u0435 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0432\u043d\u0435\u0441\u0442\u0438 \u0441\u0432\u043e\u0439 \u0432\u043a\u043b\u0430\u0434.", @@ -539,7 +539,7 @@ "HeaderRunningTasks": "\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0435\u0441\u044f \u0437\u0430\u0434\u0430\u043d\u0438\u044f", "HeaderActiveDevices": "\u0410\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "HeaderPendingInstallations": "\u041e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438", - "HeaerServerInformation": "\u0421\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0435", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e", "ButtonRestart": "\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c", "ButtonShutdown": "\u0417\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443", @@ -571,7 +571,7 @@ "TabPlayTo": "\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u041d\u0430", "LabelEnableDlnaServer": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c DLNA-\u0441\u0435\u0440\u0432\u0435\u0440", "LabelEnableDlnaServerHelp": "UPnP-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c \u0432 \u0441\u0435\u0442\u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f Media Browser.", - "LabelEnableBlastAliveMessages": "\u0412\u0441\u043f\u043b\u0435\u0441\u043a \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438", + "LabelEnableBlastAliveMessages": "\u0411\u043e\u043c\u0431\u0430\u0440\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438", "LabelEnableBlastAliveMessagesHelp": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u0435, \u0435\u0441\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430\u0434\u0451\u0436\u043d\u043e \u0434\u0440\u0443\u0433\u0438\u043c\u0438 UPnP \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u0432 \u0441\u0435\u0442\u0438.", "LabelBlastMessageInterval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438, \u0441", "LabelBlastMessageIntervalHelp": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 \u043c\u0435\u0436\u0434\u0443 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.", @@ -661,8 +661,8 @@ "HeaderLatestMedia": "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u0435", "OptionSpecialFeatures": "\u0414\u043e\u043f. \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b", "HeaderCollections": "\u041a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438", - "LabelProfileCodecsHelp": "\u0420\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u0437\u0430\u043f\u044f\u0442\u043e\u0439. \u041c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043e\u0434\u0435\u043a\u043e\u0432.", - "LabelProfileContainersHelp": "\u0420\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u0437\u0430\u043f\u044f\u0442\u043e\u0439. \u041c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432.", + "LabelProfileCodecsHelp": "\u0420\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u0437\u0430\u043f\u044f\u0442\u043e\u0439. \u041f\u043e\u043b\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043e\u0434\u0435\u043a\u043e\u0432.", + "LabelProfileContainersHelp": "\u0420\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u0437\u0430\u043f\u044f\u0442\u043e\u0439. \u041f\u043e\u043b\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432.", "HeaderResponseProfile": "\u041f\u0440\u043e\u0444\u0438\u043b\u044c \u043e\u0442\u043a\u043b\u0438\u043a\u0430", "LabelType": "\u0422\u0438\u043f:", "LabelPersonRole": "\u0420\u043e\u043b\u044c:", @@ -682,7 +682,7 @@ "OptionProfileVideoAudio": "\u0412\u0438\u0434\u0435\u043e \u0410\u0443\u0434\u0438\u043e", "OptionProfilePhoto": "\u0424\u043e\u0442\u043e", "LabelUserLibrary": "\u041c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f:", - "LabelUserLibraryHelp": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435, \u0447\u044c\u044e \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0443 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435. \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c \u0434\u043b\u044f \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430.", + "LabelUserLibraryHelp": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435, \u0447\u044c\u044e \u043c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0443 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u043d\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0435. \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440.", "OptionPlainStorageFolders": "\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u0430\u043f\u043a\u0438, \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u043f\u0430\u043f\u043a\u0438 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f", "OptionPlainStorageFoldersHelp": "\u041f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438, \u0432\u0441\u0435 \u043f\u0430\u043f\u043a\u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0432 DIDL \u043a\u0430\u043a \u00abobject.container.storageFolder\u00bb, \u0432\u043c\u0435\u0441\u0442\u043e \u0431\u043e\u043b\u0435\u0435 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u00abobject.container.person.musicArtist\u00bb.", "OptionPlainVideoItems": "\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432\u0441\u0435 \u0438\u043c\u0435\u044e\u0449\u0438\u0435\u0441\u044f \u0432\u0438\u0434\u0435\u043e, \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u0432\u0438\u0434\u0435\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b", @@ -834,7 +834,7 @@ "LabelEnableChannelContentDownloadingFor": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u043e\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f \u043a\u0430\u043d\u0430\u043b\u0430 \u0434\u043b\u044f:", "LabelEnableChannelContentDownloadingForHelp": "\u041d\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043a\u0430\u043d\u0430\u043b\u0430\u0445 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435, \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u044f\u044e\u0449\u0435\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440. \u0412\u043a\u043b\u044e\u0447\u0430\u0439\u0442\u0435 \u043f\u0440\u0438 \u0441\u0440\u0435\u0434\u0430\u0445 \u0441 \u043d\u0438\u0437\u043a\u043e\u0439 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u043d\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c\u044e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u043a\u0430\u043d\u0430\u043b\u0430 \u0432 \u043d\u0435\u0440\u0430\u0431\u043e\u0447\u0435\u0435 \u0432\u0440\u0435\u043c\u044f. \u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u044c \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u00ab\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043a\u0430\u043d\u0430\u043b\u043e\u0432\u00bb.", "LabelChannelDownloadPath": "\u041f\u0443\u0442\u044c \u0434\u043b\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u044b\u0445 \u043a\u0430\u043d\u0430\u043b\u043e\u0432:", - "LabelChannelDownloadPathHelp": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u043e\u0433\u043e, \u043f\u043e \u0436\u0435\u043b\u0430\u043d\u0438\u044e. \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0432\u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u043f\u0430\u043f\u043a\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.", + "LabelChannelDownloadPathHelp": "\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u043e\u0433\u043e, \u043f\u043e \u0436\u0435\u043b\u0430\u043d\u0438\u044e. \u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u0432\u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u044e\u044e \u043f\u0430\u043f\u043a\u0443 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.", "LabelChannelDownloadAge": "\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437, \u0434\u043d\u0438:", "LabelChannelDownloadAgeHelp": "\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435 \u0441\u0442\u0430\u0440\u0448\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u043e. \u041e\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u043c\u044b\u043c \u043f\u043e \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438.", "ChannelSettingsFormHelp": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043a\u0430\u043d\u0430\u043b\u044b (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: Trailers \u0438\u043b\u0438 Vimeo) \u0438\u0437 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u0432.", @@ -1119,7 +1119,7 @@ "HeaderTags": "\u0422\u0435\u0433\u0438", "HeaderMetadataSettings": "\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0445", "LabelLockItemToPreventChanges": "\u0424\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u043f\u0440\u0435\u0442\u0438\u0442\u044c \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f", - "MessageLeaveEmptyToInherit": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0442 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430, \u0438\u043b\u0438 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.", + "MessageLeaveEmptyToInherit": "\u041e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0442 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430, \u0438\u043b\u0438 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e.", "TabDonate": "\u041f\u043e\u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f", "HeaderDonationType": "\u0422\u0438\u043f \u043f\u043e\u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f:", "OptionMakeOneTimeDonation": "\u0421\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u0436\u0435\u0440\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u0435", @@ -1220,7 +1220,7 @@ "LabelCreateCameraUploadSubfolder": "\u0421\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u043f\u0430\u043f\u043a\u0443 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430", "LabelCreateCameraUploadSubfolderHelp": "\u0421\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043f\u0430\u043f\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u0434\u043b\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u0440\u0438 \u0449\u0435\u043b\u0447\u043a\u0435 \u043d\u0430 \u043d\u0451\u043c \u0441\u043e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \"\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\".", "LabelCustomDeviceDisplayName": "\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435:", - "LabelCustomDeviceDisplayNameHelp": "\u041f\u0440\u0438\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u0443\u0441\u0442\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0451\u043d\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c.", + "LabelCustomDeviceDisplayNameHelp": "\u041f\u0440\u0438\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043f\u043e\u043b\u0435 \u043d\u0435\u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u0441\u043e\u043e\u0431\u0449\u0451\u043d\u043d\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u043c.", "HeaderInviteUser": "\u041f\u0440\u0438\u0433\u043b\u0430\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "LabelConnectGuestUserNameHelp": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0432\u0430\u0448 \u0434\u0440\u0443\u0433 \u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u043d\u0430 \u0432\u0435\u0431-\u0441\u0430\u0439\u0442 Media Browser, \u0438\u043b\u0438 \u0430\u0434\u0440\u0435\u0441 \u044d-\u043f\u043e\u0447\u0442\u044b.", "HeaderInviteUserHelp": "Media Browser Connect \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0440\u0443\u0437\u044c\u044f\u043c \u043e\u0431\u0449\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0432\u0430\u0448\u0438\u043c \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u043c.", @@ -1273,7 +1273,9 @@ "HeaderParentalRatings": "\u0412\u043e\u0437\u0440\u0430\u0441\u0442\u043d\u0430\u044f \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f", "HeaderVideoTypes": "\u0422\u0438\u043f\u044b \u0432\u0438\u0434\u0435\u043e", "HeaderYears": "\u0413\u043e\u0434\u044b", - "HeaderAddTag": "Add Tag", - "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "HeaderAddTag": "\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0435\u0433\u0430", + "LabelBlockItemsWithTags": "\u0411\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0441 \u0442\u0435\u0433\u0430\u043c\u0438:", + "LabelTag": "\u0422\u0435\u0433:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 96c059ba9..7ba9b83fc 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/sv.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/sv.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "P\u00e5g\u00e5ende aktiviteter", "HeaderActiveDevices": "Aktiva enheter", "HeaderPendingInstallations": "V\u00e4ntande installationer", - "HeaerServerInformation": "Serverinformation", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Starta om nu", "ButtonRestart": "Starta om", "ButtonShutdown": "St\u00e4ng av", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/tr.json b/MediaBrowser.Server.Implementations/Localization/Server/tr.json index edaeb8d9c..68275c678 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/tr.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/tr.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Sunucu bilgisi", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/vi.json b/MediaBrowser.Server.Implementations/Localization/Server/vi.json index dc9b09364..0f43257e4 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/vi.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/vi.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/zh_CN.json b/MediaBrowser.Server.Implementations/Localization/Server/zh_CN.json index 204bd9330..85e5da346 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/zh_CN.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/zh_CN.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "\u8fd0\u884c\u7684\u4efb\u52a1", "HeaderActiveDevices": "\u6d3b\u52a8\u7684\u8bbe\u5907", "HeaderPendingInstallations": "\u7b49\u5f85\u5b89\u88c5", - "HeaerServerInformation": "\u670d\u52a1\u5668\u4fe1\u606f", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "\u73b0\u5728\u91cd\u542f", "ButtonRestart": "\u91cd\u542f", "ButtonShutdown": "\u5173\u673a", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 97fd408eb..c48ad70d0 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json @@ -539,7 +539,7 @@ "HeaderRunningTasks": "Running Tasks", "HeaderActiveDevices": "Active Devices", "HeaderPendingInstallations": "Pending Installations", - "HeaerServerInformation": "Server Information", + "HeaderServerInformation": "Server Information", "ButtonRestartNow": "Restart Now", "ButtonRestart": "Restart", "ButtonShutdown": "Shutdown", @@ -1275,5 +1275,7 @@ "HeaderYears": "Years", "HeaderAddTag": "Add Tag", "LabelBlockItemsWithTags": "Block items with tags:", - "LabelTag": "Tag:" + "LabelTag": "Tag:", + "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image", + "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl." } \ 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 c57d02fab..cf029df19 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -179,8 +179,8 @@ - + @@ -303,6 +303,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Sync/MockSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/MockSyncProvider.cs index bc079ad80..7d29446b9 100644 --- a/MediaBrowser.Server.Implementations/Sync/MockSyncProvider.cs +++ b/MediaBrowser.Server.Implementations/Sync/MockSyncProvider.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Server.Implementations.Sync { public string Name { - get { return "Dummy Sync"; } + get { return "Test Sync"; } } public IEnumerable GetSyncTargets() @@ -19,8 +19,8 @@ namespace MediaBrowser.Server.Implementations.Sync { new SyncTarget { - Id = "mock".GetMD5().ToString("N"), - Name = "Mock Sync" + Id = GetType().Name.GetMD5().ToString("N"), + Name = Name } }; } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs new file mode 100644 index 000000000..c7f02b3dd --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -0,0 +1,89 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Sync; +using MediaBrowser.Model.Sync; +using MoreLinq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sync +{ + public class SyncJobProcessor + { + private readonly ILibraryManager _libraryManager; + private readonly ISyncRepository _syncRepo; + + public SyncJobProcessor(ILibraryManager libraryManager, ISyncRepository syncRepo) + { + _libraryManager = libraryManager; + _syncRepo = syncRepo; + } + + public void ProcessJobItem(SyncJob job, SyncJobItem jobItem, SyncTarget target) + { + + } + + public async Task EnsureJobItems(SyncJob job) + { + var items = GetItemsForSync(job.RequestedItemIds) + .ToList(); + + var jobItems = _syncRepo.GetJobItems(job.Id) + .ToList(); + + var created = 0; + + foreach (var item in items) + { + var itemId = item.Id.ToString("N"); + + var jobItem = jobItems.FirstOrDefault(i => string.Equals(i.ItemId, itemId, StringComparison.OrdinalIgnoreCase)); + + if (jobItem != null) + { + continue; + } + + jobItem = new SyncJobItem + { + Id = Guid.NewGuid().ToString("N"), + ItemId = itemId, + JobId = job.Id, + TargetId = job.TargetId + }; + + await _syncRepo.Create(jobItem).ConfigureAwait(false); + + created++; + } + + job.ItemCount = jobItems.Count + created; + await _syncRepo.Update(job).ConfigureAwait(false); + } + + public IEnumerable GetItemsForSync(IEnumerable itemIds) + { + return itemIds.SelectMany(GetItemsForSync).DistinctBy(i => i.Id); + } + + private IEnumerable GetItemsForSync(string id) + { + var item = _libraryManager.GetItemById(id); + + if (item == null) + { + return new List(); + } + + return GetItemsForSync(item); + } + + private IEnumerable GetItemsForSync(BaseItem item) + { + return new[] { item }; + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 201eb8c7f..0c7b5c2b9 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -1,11 +1,10 @@ -using System.IO; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Sync; -using MediaBrowser.Model.Devices; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Querying; @@ -42,7 +41,14 @@ namespace MediaBrowser.Server.Implementations.Sync public async Task CreateJob(SyncJobRequest request) { - var items = GetItemsForSync(request.ItemIds).ToList(); + var items = new SyncJobProcessor(_libraryManager, _repo) + .GetItemsForSync(request.ItemIds) + .ToList(); + + if (items.Any(i => !SupportsSync(i))) + { + throw new ArgumentException("Item does not support sync."); + } if (items.Count == 1) { @@ -66,12 +72,14 @@ namespace MediaBrowser.Server.Implementations.Sync TargetId = target.Id, UserId = request.UserId, UnwatchedOnly = request.UnwatchedOnly, - Limit = request.Limit, - LimitType = request.LimitType, + ItemLimit = request.ItemLimit, RequestedItemIds = request.ItemIds, DateCreated = DateTime.UtcNow, DateLastModified = DateTime.UtcNow, - ItemCount = 1 + SyncNewContent = request.SyncNewContent, + RemoveWhenWatched = request.RemoveWhenWatched, + ItemCount = items.Count, + Quality = request.Quality }; await _repo.Create(job).ConfigureAwait(false); @@ -93,7 +101,8 @@ namespace MediaBrowser.Server.Implementations.Sync private void FillMetadata(SyncJob job) { - var item = GetItemsForSync(job.RequestedItemIds) + var item = new SyncJobProcessor(_libraryManager, _repo) + .GetItemsForSync(job.RequestedItemIds) .FirstOrDefault(); if (item != null) @@ -130,7 +139,11 @@ namespace MediaBrowser.Server.Implementations.Sync public Task CancelJob(string id) { - throw new NotImplementedException(); + var job = GetJob(id); + + job.Status = SyncJobStatus.Cancelled; + + return _repo.DeleteJob(id); } public SyncJob GetJob(string id) @@ -165,26 +178,26 @@ namespace MediaBrowser.Server.Implementations.Sync private string GetSyncProviderId(ISyncProvider provider) { - return (provider.GetType().Name + provider.Name).GetMD5().ToString("N"); + return (provider.GetType().Name).GetMD5().ToString("N"); } public bool SupportsSync(BaseItem item) { - if (item.LocationType == LocationType.Virtual) - { - return false; - } - if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) || string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) { + if (item.LocationType == LocationType.Virtual) + { + return false; + } + if (item.RunTimeTicks.HasValue) { var video = item as Video; if (video != null) { - if (video.VideoType != VideoType.VideoFile) + if (video.VideoType == VideoType.Iso) { return false; } @@ -201,34 +214,7 @@ namespace MediaBrowser.Server.Implementations.Sync return false; } - return false; - } - - private IEnumerable GetItemsForSync(IEnumerable itemIds) - { - return itemIds.SelectMany(GetItemsForSync).DistinctBy(i => i.Id); - } - - private IEnumerable GetItemsForSync(string id) - { - var item = _libraryManager.GetItemById(id); - - if (item == null) - { - throw new ArgumentException("Item with Id " + id + " not found."); - } - - if (!SupportsSync(item)) - { - throw new ArgumentException("Item with Id " + id + " does not support sync."); - } - - return GetItemsForSync(item); - } - - private IEnumerable GetItemsForSync(BaseItem item) - { - return new[] { item }; + return item.LocationType == LocationType.FileSystem || item is Season; } private string GetDefaultName(BaseItem item) diff --git a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs index 65da74f9e..338529043 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs @@ -23,6 +23,7 @@ namespace MediaBrowser.Server.Implementations.Sync private readonly IServerApplicationPaths _appPaths; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private IDbCommand _deleteJobCommand; private IDbCommand _saveJobCommand; private IDbCommand _saveJobItemCommand; @@ -34,13 +35,13 @@ namespace MediaBrowser.Server.Implementations.Sync public async Task Initialize() { - var dbFile = Path.Combine(_appPaths.DataPath, "sync.db"); + var dbFile = Path.Combine(_appPaths.DataPath, "sync2.db"); _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false); string[] queries = { - "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Quality TEXT NOT NULL, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, UnwatchedOnly BIT, SyncLimit BigInt, LimitType TEXT, IsDynamic BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)", + "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Quality TEXT NOT NULL, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, UnwatchedOnly BIT, ItemLimit INT, RemoveWhenWatched BIT, SyncNewContent BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)", "create index if not exists idx_SyncJobs on SyncJobs(Id)", "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, JobId TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT)", @@ -59,8 +60,12 @@ namespace MediaBrowser.Server.Implementations.Sync private void PrepareStatements() { + _deleteJobCommand = _connection.CreateCommand(); + _deleteJobCommand.CommandText = "delete from SyncJobs where Id=@Id; delete from SyncJobItems where JobId=@Id"; + _deleteJobCommand.Parameters.Add(_deleteJobCommand, "@Id"); + _saveJobCommand = _connection.CreateCommand(); - _saveJobCommand.CommandText = "replace into SyncJobs (Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, SyncLimit, LimitType, IsDynamic, DateCreated, DateLastModified, ItemCount) values (@Id, @TargetId, @Name, @Quality, @Status, @Progress, @UserId, @ItemIds, @UnwatchedOnly, @SyncLimit, @LimitType, @IsDynamic, @DateCreated, @DateLastModified, @ItemCount)"; + _saveJobCommand.CommandText = "replace into SyncJobs (Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, ItemLimit, RemoveWhenWatched, SyncNewContent, DateCreated, DateLastModified, ItemCount) values (@Id, @TargetId, @Name, @Quality, @Status, @Progress, @UserId, @ItemIds, @UnwatchedOnly, @ItemLimit, @RemoveWhenWatched, @SyncNewContent, @DateCreated, @DateLastModified, @ItemCount)"; _saveJobCommand.Parameters.Add(_saveJobCommand, "@Id"); _saveJobCommand.Parameters.Add(_saveJobCommand, "@TargetId"); @@ -71,9 +76,9 @@ namespace MediaBrowser.Server.Implementations.Sync _saveJobCommand.Parameters.Add(_saveJobCommand, "@UserId"); _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemIds"); _saveJobCommand.Parameters.Add(_saveJobCommand, "@UnwatchedOnly"); - _saveJobCommand.Parameters.Add(_saveJobCommand, "@SyncLimit"); - _saveJobCommand.Parameters.Add(_saveJobCommand, "@LimitType"); - _saveJobCommand.Parameters.Add(_saveJobCommand, "@IsDynamic"); + _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemLimit"); + _saveJobCommand.Parameters.Add(_saveJobCommand, "@RemoveWhenWatched"); + _saveJobCommand.Parameters.Add(_saveJobCommand, "@SyncNewContent"); _saveJobCommand.Parameters.Add(_saveJobCommand, "@DateCreated"); _saveJobCommand.Parameters.Add(_saveJobCommand, "@DateLastModified"); _saveJobCommand.Parameters.Add(_saveJobCommand, "@ItemCount"); @@ -88,7 +93,7 @@ namespace MediaBrowser.Server.Implementations.Sync _saveJobItemCommand.Parameters.Add(_saveJobCommand, "@Status"); } - private const string BaseJobSelectText = "select Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, SyncLimit, LimitType, IsDynamic, DateCreated, DateLastModified, ItemCount from SyncJobs"; + private const string BaseJobSelectText = "select Id, TargetId, Name, Quality, Status, Progress, UserId, ItemIds, UnwatchedOnly, ItemLimit, RemoveWhenWatched, SyncNewContent, DateCreated, DateLastModified, ItemCount from SyncJobs"; private const string BaseJobItemSelectText = "select Id, ItemId, JobId, OutputPath, Status, TargetId from SyncJobItems"; public SyncJob GetJob(string id) @@ -159,15 +164,12 @@ namespace MediaBrowser.Server.Implementations.Sync if (!reader.IsDBNull(9)) { - info.Limit = reader.GetInt64(9); + info.ItemLimit = reader.GetInt32(9); } - if (!reader.IsDBNull(10)) - { - info.LimitType = (SyncLimitType)Enum.Parse(typeof(SyncLimitType), reader.GetString(10), true); - } + info.RemoveWhenWatched = reader.GetBoolean(10); + info.SyncNewContent = reader.GetBoolean(11); - info.IsDynamic = reader.GetBoolean(11); info.DateCreated = reader.GetDateTime(12).ToUniversalTime(); info.DateLastModified = reader.GetDateTime(13).ToUniversalTime(); info.ItemCount = reader.GetInt32(14); @@ -206,9 +208,9 @@ namespace MediaBrowser.Server.Implementations.Sync _saveJobCommand.GetParameter(index++).Value = job.UserId; _saveJobCommand.GetParameter(index++).Value = string.Join(",", job.RequestedItemIds.ToArray()); _saveJobCommand.GetParameter(index++).Value = job.UnwatchedOnly; - _saveJobCommand.GetParameter(index++).Value = job.Limit; - _saveJobCommand.GetParameter(index++).Value = job.LimitType; - _saveJobCommand.GetParameter(index++).Value = job.IsDynamic; + _saveJobCommand.GetParameter(index++).Value = job.ItemLimit; + _saveJobCommand.GetParameter(index++).Value = job.RemoveWhenWatched; + _saveJobCommand.GetParameter(index++).Value = job.SyncNewContent; _saveJobCommand.GetParameter(index++).Value = job.DateCreated; _saveJobCommand.GetParameter(index++).Value = job.DateLastModified; _saveJobCommand.GetParameter(index++).Value = job.ItemCount; @@ -250,6 +252,62 @@ namespace MediaBrowser.Server.Implementations.Sync } } + public async Task DeleteJob(string id) + { + if (string.IsNullOrWhiteSpace(id)) + { + throw new ArgumentNullException("id"); + } + + await _writeLock.WaitAsync().ConfigureAwait(false); + + IDbTransaction transaction = null; + + try + { + transaction = _connection.BeginTransaction(); + + var index = 0; + + _deleteJobCommand.GetParameter(index++).Value = new Guid(id); + + _deleteJobCommand.Transaction = transaction; + + _deleteJobCommand.ExecuteNonQuery(); + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Failed to save record:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } + } + public QueryResult GetJobs(SyncJobQuery query) { if (query == null) @@ -336,6 +394,31 @@ namespace MediaBrowser.Server.Implementations.Sync return null; } + public IEnumerable GetJobItems(string jobId) + { + if (string.IsNullOrEmpty(jobId)) + { + throw new ArgumentNullException("jobId"); + } + + var guid = new Guid(jobId); + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = BaseJobItemSelectText + " where JobId=@Id"; + + cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + yield return GetSyncJobItem(reader); + } + } + } + } + public Task Create(SyncJobItem jobItem) { return Update(jobItem); diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 8ae117ebd..8b07e483e 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -389,8 +389,8 @@ namespace MediaBrowser.Server.Startup.Common AuthenticationRepository = await GetAuthenticationRepository().ConfigureAwait(false); RegisterSingleInstance(AuthenticationRepository); - //SyncRepository = await GetSyncRepository().ConfigureAwait(false); - //RegisterSingleInstance(SyncRepository); + SyncRepository = await GetSyncRepository().ConfigureAwait(false); + RegisterSingleInstance(SyncRepository); UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, () => ConnectManager, this); RegisterSingleInstance(UserManager); -- cgit v1.2.3 From ab3da461130b0db2f77e7e848c4bbd1280e5524a Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 12 Dec 2014 22:56:30 -0500 Subject: more sync movement --- MediaBrowser.Api/Devices/DeviceService.cs | 28 +- MediaBrowser.Api/Music/InstantMixService.cs | 17 ++ MediaBrowser.Api/Session/SessionsService.cs | 11 +- .../Collections/CollectionCreationOptions.cs | 2 + MediaBrowser.Controller/Devices/IDeviceManager.cs | 4 +- MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 20 +- MediaBrowser.Controller/Entities/Share.cs | 15 + MediaBrowser.Controller/Library/IMusicManager.cs | 8 + .../MediaBrowser.Controller.csproj | 1 + .../Persistence/IUserRepository.cs | 6 - MediaBrowser.Controller/Playlists/Playlist.cs | 19 +- .../Providers/BaseItemXmlParser.cs | 78 +++++ MediaBrowser.Controller/Sync/ISyncManager.cs | 10 +- MediaBrowser.Controller/Sync/ISyncRepository.cs | 5 +- .../Parsers/PlaylistXmlParser.cs | 12 +- .../Savers/PlaylistXmlSaver.cs | 5 - .../Savers/XmlSaverHelpers.cs | 23 ++ .../MediaBrowser.Model.Portable.csproj | 9 + .../MediaBrowser.Model.net35.csproj | 9 + MediaBrowser.Model/Devices/DeviceQuery.cs | 17 ++ MediaBrowser.Model/Dto/BaseItemDto.cs | 6 + MediaBrowser.Model/MediaBrowser.Model.csproj | 3 + MediaBrowser.Model/Querying/ItemFields.cs | 5 + MediaBrowser.Model/Session/ClientCapabilities.cs | 5 +- MediaBrowser.Model/Sync/SyncJobItem.cs | 13 +- MediaBrowser.Model/Sync/SyncJobItemQuery.cs | 27 ++ MediaBrowser.Model/Sync/SyncJobItemStatus.cs | 12 + MediaBrowser.Model/Sync/SyncJobQuery.cs | 5 + MediaBrowser.Model/Sync/SyncJobStatus.cs | 7 +- .../BoxSets/BoxSetMetadataService.cs | 5 + .../Playlists/PlaylistMetadataService.cs | 11 +- .../Collections/CollectionManager.cs | 9 +- .../Devices/DeviceManager.cs | 28 +- .../Dto/DtoService.cs | 9 + .../Library/LibraryManager.cs | 19 +- .../Library/MusicManager.cs | 13 + .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- .../Localization/JavaScript/javascript.json | 11 +- .../Localization/Server/server.json | 4 +- .../MediaBrowser.Server.Implementations.csproj | 1 + .../Playlists/ManualPlaylistsFolder.cs | 2 +- .../Playlists/PlaylistManager.cs | 9 +- .../Sync/SyncJobProcessor.cs | 334 ++++++++++++++++++++- .../Sync/SyncManager.cs | 101 +++++-- .../Sync/SyncRepository.cs | 125 ++++++-- .../Sync/SyncScheduledTask.cs | 62 ++++ .../ApplicationHost.cs | 2 +- MediaBrowser.WebDashboard/Api/PackageCreator.cs | 3 +- .../MediaBrowser.WebDashboard.csproj | 10 +- 49 files changed, 997 insertions(+), 145 deletions(-) create mode 100644 MediaBrowser.Controller/Entities/Share.cs create mode 100644 MediaBrowser.Model/Devices/DeviceQuery.cs create mode 100644 MediaBrowser.Model/Sync/SyncJobItemQuery.cs create mode 100644 MediaBrowser.Model/Sync/SyncJobItemStatus.cs create mode 100644 MediaBrowser.Server.Implementations/Sync/SyncScheduledTask.cs (limited to 'MediaBrowser.Server.Implementations/Localization') diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index 135397308..ab0a4a4b2 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -1,25 +1,19 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Devices; +using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using ServiceStack; using ServiceStack.Web; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; namespace MediaBrowser.Api.Devices { [Route("/Devices", "GET", Summary = "Gets all devices")] [Authenticated(Roles = "Admin")] - public class GetDevices : IReturn> + public class GetDevices : DeviceQuery, IReturn> { - [ApiMember(Name = "SupportsContentUploading", Description = "SupportsContentUploading", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? SupportsContentUploading { get; set; } - - [ApiMember(Name = "SupportsDeviceId", Description = "SupportsDeviceId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? SupportsDeviceId { get; set; } } [Route("/Devices", "DELETE", Summary = "Deletes a device")] @@ -112,23 +106,7 @@ namespace MediaBrowser.Api.Devices public object Get(GetDevices request) { - var devices = _deviceManager.GetDevices(); - - if (request.SupportsContentUploading.HasValue) - { - var val = request.SupportsContentUploading.Value; - - devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsContentUploading == val); - } - - if (request.SupportsDeviceId.HasValue) - { - var val = request.SupportsDeviceId.Value; - - devices = devices.Where(i => _deviceManager.GetCapabilities(i.Id).SupportsDeviceId == val); - } - - return ToOptimizedResult(devices.ToList()); + return ToOptimizedResult(_deviceManager.GetDevices(request)); } public object Get(GetCameraUploads request) diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index f34242242..43fd0894b 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Playlists; using MediaBrowser.Model.Querying; using ServiceStack; using System.Collections.Generic; @@ -20,6 +21,11 @@ namespace MediaBrowser.Api.Music { } + [Route("/Playlists/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given playlist")] + public class GetInstantMixFromPlaylist : BaseGetSimilarItemsFromItem + { + } + [Route("/Artists/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")] public class GetInstantMixFromArtist : BaseGetSimilarItems { @@ -109,6 +115,17 @@ namespace MediaBrowser.Api.Music return GetResult(items, user, request); } + public object Get(GetInstantMixFromPlaylist request) + { + var playlist = (Playlist)_libraryManager.GetItemById(request.Id); + + var user = _userManager.GetUserById(request.UserId.Value); + + var items = _musicManager.GetInstantMixFromPlaylist(playlist, user); + + return GetResult(items, user, request); + } + public object Get(GetInstantMixFromMusicGenre request) { var user = _userManager.GetUserById(request.UserId.Value); diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 551771338..df50255ab 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -239,8 +239,11 @@ namespace MediaBrowser.Api.Session [ApiMember(Name = "SupportsContentUploading", Description = "Determines whether camera upload is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool SupportsContentUploading { get; set; } - [ApiMember(Name = "SupportsDeviceId", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] - public bool SupportsDeviceId { get; set; } + [ApiMember(Name = "SupportsSync", Description = "Determines whether sync is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] + public bool SupportsSync { get; set; } + + [ApiMember(Name = "SupportsUniqueIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] + public bool SupportsUniqueIdentifier { get; set; } } [Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")] @@ -521,7 +524,9 @@ namespace MediaBrowser.Api.Session SupportsContentUploading = request.SupportsContentUploading, - SupportsDeviceId = request.SupportsDeviceId + SupportsSync = request.SupportsSync, + + SupportsUniqueIdentifier = request.SupportsUniqueIdentifier }); } } diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs index 74ae42095..4a2d39066 100644 --- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -15,11 +15,13 @@ namespace MediaBrowser.Controller.Collections public Dictionary ProviderIds { get; set; } public List ItemIdList { get; set; } + public List UserIds { get; set; } public CollectionCreationOptions() { ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); ItemIdList = new List(); + UserIds = new List(); } } } diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index af184e6e9..efd24336a 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -1,5 +1,6 @@ using MediaBrowser.Model.Devices; using MediaBrowser.Model.Events; +using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using System; using System.Collections.Generic; @@ -58,8 +59,9 @@ namespace MediaBrowser.Controller.Devices /// /// Gets the devices. /// + /// The query. /// IEnumerable<DeviceInfo>. - IEnumerable GetDevices(); + QueryResult GetDevices(DeviceQuery query); /// /// Deletes the device. diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 731226ede..9dc600675 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -15,8 +15,10 @@ namespace MediaBrowser.Controller.Entities.Movies /// /// Class BoxSet /// - public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo, IMetadataContainer + public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo, IMetadataContainer, IHasShares { + public List Shares { get; set; } + public BoxSet() { RemoteTrailers = new List(); @@ -25,6 +27,7 @@ namespace MediaBrowser.Controller.Entities.Movies DisplayOrder = ItemSortBy.PremiereDate; Keywords = new List(); + Shares = new List(); } protected override bool FilterLinkedChildrenPerUser @@ -160,5 +163,20 @@ namespace MediaBrowser.Controller.Entities.Movies progress.Report(100); } + + public override bool IsVisible(User user) + { + if (base.IsVisible(user)) + { + var userId = user.Id.ToString("N"); + + return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) || + + // Need to support this for boxsets created prior to the creation of Shares + Shares.Count == 0; + } + + return false; + } } } diff --git a/MediaBrowser.Controller/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs new file mode 100644 index 000000000..e194f6238 --- /dev/null +++ b/MediaBrowser.Controller/Entities/Share.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Entities +{ + public interface IHasShares + { + List Shares { get; set; } + } + + public class Share + { + public string UserId { get; set; } + public bool CanEdit { get; set; } + } +} diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs index 192ce2e83..f66f18401 100644 --- a/MediaBrowser.Controller/Library/IMusicManager.cs +++ b/MediaBrowser.Controller/Library/IMusicManager.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Playlists; using System.Collections.Generic; namespace MediaBrowser.Controller.Library @@ -28,6 +29,13 @@ namespace MediaBrowser.Controller.Library /// IEnumerable{Audio}. IEnumerable public interface IUserRepository : IRepository { - /// - /// Opens the connection to the repository - /// - /// Task. - Task Initialize(); - /// /// Deletes the user. /// diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 75e1bbde7..e48cddaaa 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -11,10 +11,17 @@ using System.Runtime.Serialization; namespace MediaBrowser.Controller.Playlists { - public class Playlist : Folder + public class Playlist : Folder, IHasShares { public string OwnerUserId { get; set; } + public List Shares { get; set; } + + public Playlist() + { + Shares = new List(); + } + [IgnoreDataMember] protected override bool FilterLinkedChildrenPerUser { @@ -166,7 +173,15 @@ namespace MediaBrowser.Controller.Playlists public override bool IsVisible(User user) { - return base.IsVisible(user) && string.Equals(user.Id.ToString("N"), OwnerUserId); + if (base.IsVisible(user)) + { + var userId = user.Id.ToString("N"); + + return Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase)) || + string.Equals(OwnerUserId, userId, StringComparison.OrdinalIgnoreCase); + } + + return false; } } } diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 307ab3cb8..a37f7eb8a 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -819,6 +819,19 @@ namespace MediaBrowser.Controller.Providers break; } + case "Shares": + { + using (var subtree = reader.ReadSubtree()) + { + var hasShares = item as IHasShares; + if (hasShares != null) + { + FetchFromSharesNode(subtree, hasShares); + } + } + break; + } + case "Format3D": { var video = item as Video; @@ -853,6 +866,71 @@ namespace MediaBrowser.Controller.Providers } } + private void FetchFromSharesNode(XmlReader reader, IHasShares item) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Share": + { + using (var subtree = reader.ReadSubtree()) + { + var share = GetShareFromNode(subtree); + if (share != null) + { + item.Shares.Add(share); + } + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + + private Share GetShareFromNode(XmlReader reader) + { + var share = new Share(); + + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "UserId": + { + share.UserId = reader.ReadElementContentAsString(); + break; + } + + case "CanEdit": + { + share.CanEdit = string.Equals(reader.ReadElementContentAsString(), true.ToString(), StringComparison.OrdinalIgnoreCase); + break; + } + + default: + reader.Skip(); + break; + } + } + } + + return share; + } + private void FetchFromCountriesNode(XmlReader reader, T item) { reader.MoveToContent(); diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs index 1d5ab7d3e..31c3c0c6d 100644 --- a/MediaBrowser.Controller/Sync/ISyncManager.cs +++ b/MediaBrowser.Controller/Sync/ISyncManager.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Sync; using System.Collections.Generic; @@ -27,7 +28,7 @@ namespace MediaBrowser.Controller.Sync /// The identifier. /// SyncJob. SyncJob GetJob(string id); - + /// /// Cancels the job. /// @@ -51,5 +52,12 @@ namespace MediaBrowser.Controller.Sync /// The item. /// true if XXXX, false otherwise. bool SupportsSync(BaseItem item); + + /// + /// Gets the device profile. + /// + /// The target identifier. + /// DeviceProfile. + DeviceProfile GetDeviceProfile(string targetId); } } diff --git a/MediaBrowser.Controller/Sync/ISyncRepository.cs b/MediaBrowser.Controller/Sync/ISyncRepository.cs index d0cf87182..f1bcd7f07 100644 --- a/MediaBrowser.Controller/Sync/ISyncRepository.cs +++ b/MediaBrowser.Controller/Sync/ISyncRepository.cs @@ -1,6 +1,5 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Sync; -using System.Collections.Generic; using System.Threading.Tasks; namespace MediaBrowser.Controller.Sync @@ -66,8 +65,8 @@ namespace MediaBrowser.Controller.Sync /// /// Gets the job items. /// - /// The job identifier. + /// The query. /// IEnumerable<SyncJobItem>. - IEnumerable GetJobItems(string jobId); + QueryResult GetJobItems(SyncJobItemQuery query); } } diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs index c7f974200..a12724ff7 100644 --- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs @@ -2,7 +2,9 @@ using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; +using System; using System.Collections.Generic; +using System.Linq; using System.Xml; namespace MediaBrowser.LocalMetadata.Parsers @@ -20,7 +22,15 @@ namespace MediaBrowser.LocalMetadata.Parsers { case "OwnerUserId": { - item.OwnerUserId = reader.ReadElementContentAsString(); + var userId = reader.ReadElementContentAsString(); + if (!item.Shares.Any(i => string.Equals(userId, i.UserId, StringComparison.OrdinalIgnoreCase))) + { + item.Shares.Add(new Share + { + UserId = userId, + CanEdit = true + }); + } break; } diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs index 16c437381..76ef4d4bf 100644 --- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs @@ -57,11 +57,6 @@ namespace MediaBrowser.LocalMetadata.Savers builder.Append(""); - if (!string.IsNullOrEmpty(playlist.OwnerUserId)) - { - builder.Append("" + SecurityElement.Escape(playlist.OwnerUserId) + ""); - } - if (!string.IsNullOrEmpty(playlist.PlaylistMediaType)) { builder.Append("" + SecurityElement.Escape(playlist.PlaylistMediaType) + ""); diff --git a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs index 93876f474..3e11c994b 100644 --- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs @@ -645,6 +645,29 @@ namespace MediaBrowser.LocalMetadata.Savers { AddLinkedChildren(playlist, builder, "PlaylistItems", "PlaylistItem"); } + + var hasShares = item as IHasShares; + if (hasShares != null) + { + + } + } + + public static void AddShares(IHasShares item, StringBuilder builder) + { + builder.Append(""); + + foreach (var share in item.Shares) + { + builder.Append(""); + + builder.Append("" + SecurityElement.Escape(share.UserId) + ""); + builder.Append("" + SecurityElement.Escape(share.CanEdit.ToString().ToLower()) + ""); + + builder.Append(""); + } + + builder.Append(""); } public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository) diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index bcd6e08f1..701d7d70b 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -275,6 +275,9 @@ Devices\DeviceOptions.cs + + Devices\DeviceQuery.cs + Devices\DevicesOptions.cs @@ -1034,6 +1037,12 @@ Sync\SyncJobItem.cs + + SyncJobItemQuery.cs + + + Sync\SyncJobItemStatus.cs + Sync\SyncJobQuery.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 55d18c1b3..9affbb199 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -240,6 +240,9 @@ Devices\DeviceOptions.cs + + Devices\DeviceQuery.cs + Devices\DevicesOptions.cs @@ -993,6 +996,12 @@ Sync\SyncJobItem.cs + + Sync\SyncJobItemQuery.cs + + + Sync\SyncJobItemStatus.cs + Sync\SyncJobQuery.cs diff --git a/MediaBrowser.Model/Devices/DeviceQuery.cs b/MediaBrowser.Model/Devices/DeviceQuery.cs new file mode 100644 index 000000000..76f7117b6 --- /dev/null +++ b/MediaBrowser.Model/Devices/DeviceQuery.cs @@ -0,0 +1,17 @@ + +namespace MediaBrowser.Model.Devices +{ + public class DeviceQuery + { + /// + /// Gets or sets a value indicating whether [supports content uploading]. + /// + /// null if [supports content uploading] contains no value, true if [supports content uploading]; otherwise, false. + public bool? SupportsContentUploading { get; set; } + /// + /// Gets or sets a value indicating whether [supports unique identifier]. + /// + /// null if [supports unique identifier] contains no value, true if [supports unique identifier]; otherwise, false. + public bool? SupportsUniqueIdentifier { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index b83243ba8..45f681066 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -186,6 +186,12 @@ namespace MediaBrowser.Model.Dto /// /// The genres. public List Genres { get; set; } + + /// + /// Gets or sets the series genres. + /// + /// The series genres. + public List SeriesGenres { get; set; } /// /// Gets or sets the community rating. diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 4825cb4cc..5aebe42eb 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -113,6 +113,7 @@ + @@ -365,6 +366,8 @@ + + diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index a5a906f95..19e30cd8a 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -161,6 +161,11 @@ namespace MediaBrowser.Model.Querying /// ScreenshotImageTags, + /// + /// The series genres + /// + SeriesGenres, + /// /// The series studio /// diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index fc0d3a1fb..f2faa0545 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -13,13 +13,14 @@ namespace MediaBrowser.Model.Session public string MessageCallbackUrl { get; set; } public bool SupportsContentUploading { get; set; } - public bool SupportsDeviceId { get; set; } + public bool SupportsUniqueIdentifier { get; set; } + public bool SupportsSync { get; set; } public ClientCapabilities() { PlayableMediaTypes = new List(); SupportedCommands = new List(); - SupportsDeviceId = true; + SupportsUniqueIdentifier = true; } } } \ No newline at end of file diff --git a/MediaBrowser.Model/Sync/SyncJobItem.cs b/MediaBrowser.Model/Sync/SyncJobItem.cs index 141546eb5..063f7feb2 100644 --- a/MediaBrowser.Model/Sync/SyncJobItem.cs +++ b/MediaBrowser.Model/Sync/SyncJobItem.cs @@ -1,4 +1,5 @@ - +using System; + namespace MediaBrowser.Model.Sync { public class SyncJobItem @@ -37,12 +38,18 @@ namespace MediaBrowser.Model.Sync /// Gets or sets the status. /// /// The status. - public SyncJobStatus Status { get; set; } + public SyncJobItemStatus Status { get; set; } /// /// Gets or sets the current progress. /// /// The current progress. - public double? CurrentProgress { get; set; } + public double? Progress { get; set; } + + /// + /// Gets or sets the date created. + /// + /// The date created. + public DateTime DateCreated { get; set; } } } diff --git a/MediaBrowser.Model/Sync/SyncJobItemQuery.cs b/MediaBrowser.Model/Sync/SyncJobItemQuery.cs new file mode 100644 index 000000000..e9af642ac --- /dev/null +++ b/MediaBrowser.Model/Sync/SyncJobItemQuery.cs @@ -0,0 +1,27 @@ + +namespace MediaBrowser.Model.Sync +{ + public class SyncJobItemQuery + { + /// + /// Gets or sets the start index. + /// + /// The start index. + public int? StartIndex { get; set; } + /// + /// Gets or sets the limit. + /// + /// The limit. + public int? Limit { get; set; } + /// + /// Gets or sets the job identifier. + /// + /// The job identifier. + public string JobId { get; set; } + /// + /// Gets or sets a value indicating whether this instance is completed. + /// + /// null if [is completed] contains no value, true if [is completed]; otherwise, false. + public bool? IsCompleted { get; set; } + } +} diff --git a/MediaBrowser.Model/Sync/SyncJobItemStatus.cs b/MediaBrowser.Model/Sync/SyncJobItemStatus.cs new file mode 100644 index 000000000..3d0579a3c --- /dev/null +++ b/MediaBrowser.Model/Sync/SyncJobItemStatus.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Model.Sync +{ + public enum SyncJobItemStatus + { + Queued = 0, + Converting = 1, + Transferring = 2, + Completed = 3, + Failed = 4 + } +} diff --git a/MediaBrowser.Model/Sync/SyncJobQuery.cs b/MediaBrowser.Model/Sync/SyncJobQuery.cs index 74b35186e..218b3823e 100644 --- a/MediaBrowser.Model/Sync/SyncJobQuery.cs +++ b/MediaBrowser.Model/Sync/SyncJobQuery.cs @@ -13,5 +13,10 @@ namespace MediaBrowser.Model.Sync /// /// The limit. public int? Limit { get; set; } + /// + /// Gets or sets a value indicating whether this instance is completed. + /// + /// null if [is completed] contains no value, true if [is completed]; otherwise, false. + public bool? IsCompleted { get; set; } } } diff --git a/MediaBrowser.Model/Sync/SyncJobStatus.cs b/MediaBrowser.Model/Sync/SyncJobStatus.cs index 42af96509..961ccf544 100644 --- a/MediaBrowser.Model/Sync/SyncJobStatus.cs +++ b/MediaBrowser.Model/Sync/SyncJobStatus.cs @@ -4,9 +4,8 @@ namespace MediaBrowser.Model.Sync public enum SyncJobStatus { Queued = 0, - Converting = 1, - Transferring = 2, - Completed = 3, - Cancelled = 4 + InProgress = 1, + Completed = 2, + CompletedWithError = 3 } } diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs index 31136b919..062519f9d 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs +++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs @@ -44,6 +44,11 @@ namespace MediaBrowser.Providers.BoxSets target.LinkedChildren = list; } + + if (replaceData || target.Shares.Count == 0) + { + target.Shares = source.Shares; + } } protected override ItemUpdateType BeforeSave(BoxSet item) diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs index 586c17d07..2e407f10c 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs @@ -7,7 +7,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using System.Linq; namespace MediaBrowser.Providers.Playlists { @@ -34,18 +33,14 @@ namespace MediaBrowser.Providers.Playlists target.PlaylistMediaType = source.PlaylistMediaType; } - if (replaceData || string.IsNullOrEmpty(target.OwnerUserId)) + if (replaceData || target.Shares.Count == 0) { - target.OwnerUserId = source.OwnerUserId; + target.Shares = source.Shares; } if (mergeMetadataSettings) { - var list = source.LinkedChildren.ToList(); - - list.AddRange(target.LinkedChildren); - - target.LinkedChildren = list; + target.LinkedChildren = source.LinkedChildren; } } } diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index e346d6d01..9fee27db9 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -72,7 +72,12 @@ namespace MediaBrowser.Server.Implementations.Collections DisplayMediaType = "Collection", Path = path, IsLocked = options.IsLocked, - ProviderIds = options.ProviderIds + ProviderIds = options.ProviderIds, + Shares = options.UserIds.Select(i => new Share + { + UserId = i.ToString("N") + + }).ToList() }; await parentFolder.AddChild(collection, CancellationToken.None).ConfigureAwait(false); @@ -156,7 +161,7 @@ namespace MediaBrowser.Server.Implementations.Collections } itemList.Add(item); - + if (currentLinkedChildren.Any(i => i.Id == itemId)) { throw new ArgumentException("Item already exists in collection"); diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs index e2c729b2d..8c67013ea 100644 --- a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs +++ b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs @@ -6,6 +6,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Model.Devices; using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; using System; using System.Collections.Generic; @@ -28,7 +29,7 @@ namespace MediaBrowser.Server.Implementations.Devices /// Occurs when [device options updated]. /// public event EventHandler> DeviceOptionsUpdated; - + public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IConfigurationManager config, ILogger logger) { _repo = repo; @@ -79,9 +80,30 @@ namespace MediaBrowser.Server.Implementations.Devices return _repo.GetDevice(id); } - public IEnumerable GetDevices() + public QueryResult GetDevices(DeviceQuery query) { - return _repo.GetDevices().OrderByDescending(i => i.DateLastModified); + IEnumerable devices = _repo.GetDevices().OrderByDescending(i => i.DateLastModified); + + if (query.SupportsContentUploading.HasValue) + { + var val = query.SupportsContentUploading.Value; + + devices = devices.Where(i => GetCapabilities(i.Id).SupportsContentUploading == val); + } + + if (query.SupportsUniqueIdentifier.HasValue) + { + var val = query.SupportsUniqueIdentifier.Value; + + devices = devices.Where(i => GetCapabilities(i.Id).SupportsUniqueIdentifier == val); + } + + var array = devices.ToArray(); + return new QueryResult + { + Items = array, + TotalRecordCount = array.Length + }; } public Task DeleteDevice(string id) diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index e1e8da5c5..a6f9f0675 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1142,6 +1142,15 @@ namespace MediaBrowser.Server.Implementations.Dto dto.SeasonId = episodeSeason.Id.ToString("N"); dto.SeasonName = episodeSeason.Name; } + + if (fields.Contains(ItemFields.SeriesGenres)) + { + var episodeseries = episode.Series; + if (episodeseries != null) + { + dto.SeriesGenres = episodeseries.Genres.ToList(); + } + } } // Add SeriesInfo diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index e06720192..bfdfc03ba 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -736,16 +736,27 @@ namespace MediaBrowser.Server.Implementations.Library } private UserRootFolder _userRootFolder; + private readonly object _syncLock = new object(); public Folder GetUserRootFolder() { if (_userRootFolder == null) { - var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; + lock (_syncLock) + { + if (_userRootFolder == null) + { + var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - Directory.CreateDirectory(userRootPath); + Directory.CreateDirectory(userRootPath); - _userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder ?? - (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)); + _userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder; + + if (_userRootFolder == null) + { + _userRootFolder = (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)); + } + } + } } return _userRootFolder; diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs index 7ffbab860..b8c29c19b 100644 --- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MusicManager.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Playlists; using System; using System.Collections.Generic; using System.Linq; @@ -53,6 +54,18 @@ namespace MediaBrowser.Server.Implementations.Library return GetInstantMixFromGenres(genres, user); } + public IEnumerable