diff options
Diffstat (limited to 'MediaBrowser.Api')
67 files changed, 704 insertions, 526 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 6691080bc..b041effb2 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -31,7 +31,7 @@ namespace MediaBrowser.Api /// <summary> /// The logger. /// </summary> - private ILogger _logger; + private ILogger<ApiEntryPoint> _logger; /// <summary> /// The configuration manager. @@ -43,7 +43,7 @@ namespace MediaBrowser.Api private readonly IMediaSourceManager _mediaSourceManager; /// <summary> - /// The active transcoding jobs + /// The active transcoding jobs. /// </summary> private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>(); @@ -284,8 +284,8 @@ namespace MediaBrowser.Api Width = state.OutputWidth, Height = state.OutputHeight, AudioChannels = state.OutputAudioChannels, - IsAudioDirect = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase), - IsVideoDirect = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase), + IsAudioDirect = EncodingHelper.IsCopyCodec(state.OutputAudioCodec), + IsVideoDirect = EncodingHelper.IsCopyCodec(state.OutputVideoCodec), TranscodeReasons = state.TranscodeReasons }); } @@ -293,7 +293,7 @@ namespace MediaBrowser.Api /// <summary> /// <summary> - /// The progressive + /// The progressive. /// </summary> /// Called when [transcode failed to start]. /// </summary> diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 2cd68ac1b..63a31a745 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using Jellyfin.Data.Enums; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -16,7 +17,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class BaseApiService + /// Class BaseApiService. /// </summary> public abstract class BaseApiService : IService, IRequiresRequest { @@ -94,8 +95,8 @@ namespace MediaBrowser.Api var authenticatedUser = auth.User; // If they're going to update the record of another user, they must be an administrator - if ((!userId.Equals(auth.UserId) && !authenticatedUser.Policy.IsAdministrator) - || (restrictUserPreferences && !authenticatedUser.Policy.EnableUserPreferenceAccess)) + if ((!userId.Equals(auth.UserId) && !authenticatedUser.HasPermission(PermissionKind.IsAdministrator)) + || (restrictUserPreferences && !authenticatedUser.EnableUserPreferenceAccess)) { throw new SecurityException("Unauthorized access."); } @@ -267,7 +268,6 @@ namespace MediaBrowser.Api Name = name.Replace(BaseItem.SlugChar, '&'), IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<T>().FirstOrDefault(); result ??= libraryManager.GetItemList(new InternalItemsQuery @@ -275,7 +275,6 @@ namespace MediaBrowser.Api Name = name.Replace(BaseItem.SlugChar, '/'), IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<T>().FirstOrDefault(); result ??= libraryManager.GetItemList(new InternalItemsQuery @@ -283,7 +282,6 @@ namespace MediaBrowser.Api Name = name.Replace(BaseItem.SlugChar, '?'), IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<T>().FirstOrDefault(); return result; diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs index fd9b8c396..8c336b1c9 100644 --- a/MediaBrowser.Api/ChannelService.cs +++ b/MediaBrowser.Api/ChannelService.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Api public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -90,7 +90,7 @@ namespace MediaBrowser.Api public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -149,7 +149,7 @@ namespace MediaBrowser.Api public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -240,7 +240,6 @@ namespace MediaBrowser.Api { Fields = request.GetItemFields() } - }; foreach (var filter in request.GetFilters()) diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index 316be04a0..19369ccca 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -11,13 +11,12 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class GetConfiguration + /// Class GetConfiguration. /// </summary> [Route("/System/Configuration", "GET", Summary = "Gets application configuration")] [Authenticated] public class GetConfiguration : IReturn<ServerConfiguration> { - } [Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")] @@ -29,7 +28,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UpdateConfiguration + /// Class UpdateConfiguration. /// </summary> [Route("/System/Configuration", "POST", Summary = "Updates application configuration")] [Authenticated(Roles = "Admin")] @@ -51,7 +50,6 @@ namespace MediaBrowser.Api [Authenticated(Roles = "Admin")] public class GetDefaultMetadataOptions : IReturn<MetadataOptions> { - } [Route("/System/MediaEncoder/Path", "POST", Summary = "Updates the path to the media encoder")] @@ -67,12 +65,12 @@ namespace MediaBrowser.Api public class ConfigurationService : BaseApiService { /// <summary> - /// The _json serializer + /// The _json serializer. /// </summary> private readonly IJsonSerializer _jsonSerializer; /// <summary> - /// The _configuration manager + /// The _configuration manager. /// </summary> private readonly IServerConfigurationManager _configurationManager; diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index 53eb9667d..18860983e 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -1,4 +1,3 @@ -using System.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Net; @@ -40,33 +39,6 @@ namespace MediaBrowser.Api.Devices public string Id { get; set; } } - [Route("/Devices/CameraUploads", "GET", Summary = "Gets camera upload history for a device")] - [Authenticated] - public class GetCameraUploads : IReturn<ContentUploadHistory> - { - [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string DeviceId { get; set; } - } - - [Route("/Devices/CameraUploads", "POST", Summary = "Uploads content")] - [Authenticated] - public class PostCameraUpload : IRequiresRequestStream, IReturnVoid - { - [ApiMember(Name = "DeviceId", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string DeviceId { get; set; } - - [ApiMember(Name = "Album", Description = "Album", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Album { get; set; } - - [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Name { get; set; } - - [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Id { get; set; } - - public Stream RequestStream { get; set; } - } - [Route("/Devices/Options", "POST", Summary = "Updates device options")] [Authenticated(Roles = "Admin")] public class PostDeviceOptions : DeviceOptions, IReturnVoid @@ -120,7 +92,6 @@ namespace MediaBrowser.Api.Devices var sessions = _authRepo.Get(new AuthenticationInfoQuery { DeviceId = request.Id - }).Items; foreach (var session in sessions) diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs index 62c4ff43f..c3ed40ad3 100644 --- a/MediaBrowser.Api/DisplayPreferencesService.cs +++ b/MediaBrowser.Api/DisplayPreferencesService.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class UpdateDisplayPreferences + /// Class UpdateDisplayPreferences. /// </summary> [Route("/DisplayPreferences/{DisplayPreferencesId}", "POST", Summary = "Updates a user's display preferences for an item")] public class UpdateDisplayPreferences : DisplayPreferences, IReturnVoid @@ -44,17 +44,17 @@ namespace MediaBrowser.Api } /// <summary> - /// Class DisplayPreferencesService + /// Class DisplayPreferencesService. /// </summary> [Authenticated] public class DisplayPreferencesService : BaseApiService { /// <summary> - /// The _display preferences manager + /// The _display preferences manager. /// </summary> private readonly IDisplayPreferencesRepository _displayPreferencesManager; /// <summary> - /// The _json serializer + /// The _json serializer. /// </summary> private readonly IJsonSerializer _jsonSerializer; diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index d199ce154..720a71025 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class GetDirectoryContents + /// Class GetDirectoryContents. /// </summary> [Route("/Environment/DirectoryContents", "GET", Summary = "Gets the contents of a given directory in the file system")] public class GetDirectoryContents : IReturn<List<FileSystemEntryInfo>> @@ -50,6 +50,7 @@ namespace MediaBrowser.Api public string Path { get; set; } public bool ValidateWriteable { get; set; } + public bool? IsFile { get; set; } } @@ -66,7 +67,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class GetDrives + /// Class GetDrives. /// </summary> [Route("/Environment/Drives", "GET", Summary = "Gets available drives from the server's file system")] public class GetDrives : IReturn<List<FileSystemEntryInfo>> @@ -74,7 +75,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class GetNetworkComputers + /// Class GetNetworkComputers. /// </summary> [Route("/Environment/NetworkDevices", "GET", Summary = "Gets a list of devices on the network")] public class GetNetworkDevices : IReturn<List<FileSystemEntryInfo>> @@ -100,11 +101,10 @@ namespace MediaBrowser.Api [Route("/Environment/DefaultDirectoryBrowser", "GET", Summary = "Gets the parent path of a given path")] public class GetDefaultDirectoryBrowser : IReturn<DefaultDirectoryBrowserInfo> { - } /// <summary> - /// Class EnvironmentService + /// Class EnvironmentService. /// </summary> [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)] public class EnvironmentService : BaseApiService @@ -113,7 +113,7 @@ namespace MediaBrowser.Api private const string UncSeparatorString = "\\"; /// <summary> - /// The _network manager + /// The _network manager. /// </summary> private readonly INetworkManager _networkManager; private readonly IFileSystem _fileSystem; @@ -221,17 +221,12 @@ namespace MediaBrowser.Api } /// <summary> - /// Gets the list that is returned when an empty path is supplied + /// Gets the list that is returned when an empty path is supplied. /// </summary> /// <returns>IEnumerable{FileSystemEntryInfo}.</returns> private IEnumerable<FileSystemEntryInfo> GetDrives() { - return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo - { - Name = d.Name, - Path = d.FullName, - Type = FileSystemEntryType.Directory - }); + return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo(d.Name, d.FullName, FileSystemEntryType.Directory)); } /// <summary> @@ -261,13 +256,7 @@ namespace MediaBrowser.Api return request.IncludeDirectories || !isDirectory; }); - return entries.Select(f => new FileSystemEntryInfo - { - Name = f.Name, - Path = f.FullName, - Type = f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File - - }); + return entries.Select(f => new FileSystemEntryInfo(f.Name, f.FullName, f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File)); } public object Get(GetParentPath request) diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs index 5eb72cdb1..dcfdcbfed 100644 --- a/MediaBrowser.Api/FilterService.cs +++ b/MediaBrowser.Api/FilterService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -72,11 +73,17 @@ namespace MediaBrowser.Api } public bool? IsAiring { get; set; } + public bool? IsMovie { get; set; } + public bool? IsSports { get; set; } + public bool? IsKids { get; set; } + public bool? IsNews { get; set; } + public bool? IsSeries { get; set; } + public bool? Recursive { get; set; } } @@ -118,7 +125,7 @@ namespace MediaBrowser.Api IncludeItemTypes = request.GetIncludeItemTypes(), DtoOptions = new Controller.Dto.DtoOptions { - Fields = new ItemFields[] { }, + Fields = Array.Empty<ItemFields>(), EnableImages = false, EnableUserData = false }, @@ -149,7 +156,6 @@ namespace MediaBrowser.Api { Name = i.Item1.Name, Id = i.Item1.Id - }).ToArray(); } else @@ -158,7 +164,6 @@ namespace MediaBrowser.Api { Name = i.Item1.Name, Id = i.Item1.Id - }).ToArray(); } diff --git a/MediaBrowser.Api/IHasDtoOptions.cs b/MediaBrowser.Api/IHasDtoOptions.cs index 03d3b3692..33d498e8b 100644 --- a/MediaBrowser.Api/IHasDtoOptions.cs +++ b/MediaBrowser.Api/IHasDtoOptions.cs @@ -3,6 +3,7 @@ namespace MediaBrowser.Api public interface IHasDtoOptions : IHasItemFields { bool? EnableImages { get; set; } + bool? EnableUserData { get; set; } int? ImageTypeLimit { get; set; } diff --git a/MediaBrowser.Api/IHasItemFields.cs b/MediaBrowser.Api/IHasItemFields.cs index 85b4a7e2d..ad4f1b489 100644 --- a/MediaBrowser.Api/IHasItemFields.cs +++ b/MediaBrowser.Api/IHasItemFields.cs @@ -5,7 +5,7 @@ using MediaBrowser.Model.Querying; namespace MediaBrowser.Api { /// <summary> - /// Interface IHasItemFields + /// Interface IHasItemFields. /// </summary> public interface IHasItemFields { @@ -43,7 +43,6 @@ namespace MediaBrowser.Api } return null; - }).Where(i => i.HasValue).Select(i => i.Value).ToArray(); } } diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs index 45b7d0c10..2d405ac3d 100644 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ b/MediaBrowser.Api/Images/ImageByNameService.cs @@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Images { /// <summary> - /// Class GetGeneralImage + /// Class GetGeneralImage. /// </summary> [Route("/Images/General/{Name}/{Type}", "GET", Summary = "Gets a general image by name")] public class GetGeneralImage @@ -33,7 +33,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class GetRatingImage + /// Class GetRatingImage. /// </summary> [Route("/Images/Ratings/{Theme}/{Name}", "GET", Summary = "Gets a rating image by name")] public class GetRatingImage @@ -54,7 +54,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class GetMediaInfoImage + /// Class GetMediaInfoImage. /// </summary> [Route("/Images/MediaInfo/{Theme}/{Name}", "GET", Summary = "Gets a media info image by name")] public class GetMediaInfoImage @@ -93,12 +93,12 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class ImageByNameService + /// Class ImageByNameService. /// </summary> public class ImageByNameService : BaseApiService { /// <summary> - /// The _app paths + /// The _app paths. /// </summary> private readonly IServerApplicationPaths _appPaths; diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs index 71ff09b63..0f3455548 100644 --- a/MediaBrowser.Api/Images/ImageRequest.cs +++ b/MediaBrowser.Api/Images/ImageRequest.cs @@ -4,30 +4,30 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Api.Images { /// <summary> - /// Class ImageRequest + /// Class ImageRequest. /// </summary> public class ImageRequest : DeleteImageRequest { /// <summary> - /// The max width + /// The max width. /// </summary> [ApiMember(Name = "MaxWidth", Description = "The maximum image width to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? MaxWidth { get; set; } /// <summary> - /// The max height + /// The max height. /// </summary> [ApiMember(Name = "MaxHeight", Description = "The maximum image height to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? MaxHeight { get; set; } /// <summary> - /// The width + /// The width. /// </summary> [ApiMember(Name = "Width", Description = "The fixed image width to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Width { get; set; } /// <summary> - /// The height + /// The height. /// </summary> [ApiMember(Name = "Height", Description = "The fixed image height to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Height { get; set; } @@ -79,7 +79,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class DeleteImageRequest + /// Class DeleteImageRequest. /// </summary> public class DeleteImageRequest { diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 2e9b3e6cb..8426a9a4f 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; @@ -17,9 +18,11 @@ using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; +using User = Jellyfin.Data.Entities.User; namespace MediaBrowser.Api.Images { @@ -55,7 +58,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class UpdateItemImageIndex + /// Class UpdateItemImageIndex. /// </summary> [Route("/Items/{Id}/Images/{Type}/{Index}/Index", "POST", Summary = "Updates the index for an item image")] [Authenticated(Roles = "admin")] @@ -91,7 +94,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class GetPersonImage + /// Class GetPersonImage. /// </summary> [Route("/Artists/{Name}/Images/{Type}", "GET")] [Route("/Artists/{Name}/Images/{Type}/{Index}", "GET")] @@ -128,7 +131,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class GetUserImage + /// Class GetUserImage. /// </summary> [Route("/Users/{Id}/Images/{Type}", "GET")] [Route("/Users/{Id}/Images/{Type}/{Index}", "GET")] @@ -145,7 +148,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class DeleteItemImage + /// Class DeleteItemImage. /// </summary> [Route("/Items/{Id}/Images/{Type}", "DELETE")] [Route("/Items/{Id}/Images/{Type}/{Index}", "DELETE")] @@ -161,7 +164,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class DeleteUserImage + /// Class DeleteUserImage. /// </summary> [Route("/Users/{Id}/Images/{Type}", "DELETE")] [Route("/Users/{Id}/Images/{Type}/{Index}", "DELETE")] @@ -177,7 +180,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class PostUserImage + /// Class PostUserImage. /// </summary> [Route("/Users/{Id}/Images/{Type}", "POST")] [Route("/Users/{Id}/Images/{Type}/{Index}", "POST")] @@ -192,14 +195,14 @@ namespace MediaBrowser.Api.Images public string Id { get; set; } /// <summary> - /// The raw Http Request Input Stream + /// The raw Http Request Input Stream. /// </summary> /// <value>The request stream.</value> public Stream RequestStream { get; set; } } /// <summary> - /// Class PostItemImage + /// Class PostItemImage. /// </summary> [Route("/Items/{Id}/Images/{Type}", "POST")] [Route("/Items/{Id}/Images/{Type}/{Index}", "POST")] @@ -214,14 +217,14 @@ namespace MediaBrowser.Api.Images public string Id { get; set; } /// <summary> - /// The raw Http Request Input Stream + /// The raw Http Request Input Stream. /// </summary> /// <value>The request stream.</value> public Stream RequestStream { get; set; } } /// <summary> - /// Class ImageService + /// Class ImageService. /// </summary> public class ImageService : BaseApiService { @@ -280,9 +283,16 @@ namespace MediaBrowser.Api.Images public List<ImageInfo> GetItemImageInfos(BaseItem item) { var list = new List<ImageInfo>(); - var itemImages = item.ImageInfos; + if (itemImages.Length == 0) + { + // short-circuit + return list; + } + + _libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct + foreach (var image in itemImages) { if (!item.AllowsMultipleImages(image.Type)) @@ -323,6 +333,7 @@ namespace MediaBrowser.Api.Images { int? width = null; int? height = null; + string blurhash = null; long length = 0; try @@ -332,10 +343,9 @@ namespace MediaBrowser.Api.Images var fileInfo = _fileSystem.GetFileInfo(info.Path); length = fileInfo.Length; - ImageDimensions size = _imageProcessor.GetImageDimensions(item, info); - _libraryManager.UpdateImages(item); - width = size.Width; - height = size.Height; + blurhash = info.BlurHash; + width = info.Width; + height = info.Height; if (width <= 0 || height <= 0) { @@ -358,6 +368,7 @@ namespace MediaBrowser.Api.Images ImageType = info.Type, ImageTag = _imageProcessor.GetImageCacheTag(item, info), Size = length, + BlurHash = blurhash, Width = width, Height = height }; @@ -399,14 +410,14 @@ namespace MediaBrowser.Api.Images { var item = _userManager.GetUserById(request.Id); - return GetImage(request, Guid.Empty, item, false); + return GetImage(request, item, false); } public object Head(GetUserImage request) { var item = _userManager.GetUserById(request.Id); - return GetImage(request, Guid.Empty, item, true); + return GetImage(request, item, true); } public object Get(GetItemByNameImage request) @@ -439,9 +450,9 @@ namespace MediaBrowser.Api.Images request.Type = Enum.Parse<ImageType>(GetPathValue(3).ToString(), true); - var item = _userManager.GetUserById(id); + var user = _userManager.GetUserById(id); - return PostImage(item, request.RequestStream, request.Type, Request.ContentType); + return PostImage(user, request.RequestStream, Request.ContentType); } /// <summary> @@ -468,9 +479,17 @@ namespace MediaBrowser.Api.Images var userId = request.Id; AssertCanUpdateUser(_authContext, _userManager, userId, true); - var item = _userManager.GetUserById(userId); + var user = _userManager.GetUserById(userId); + try + { + File.Delete(user.ProfileImage.Path); + } + catch (IOException e) + { + Logger.LogError(e, "Error deleting user profile image:"); + } - item.DeleteImage(request.Type, request.Index ?? 0); + _userManager.ClearProfileImage(user); } /// <summary> @@ -555,18 +574,17 @@ namespace MediaBrowser.Api.Images var imageInfo = GetImageInfo(request, item); if (imageInfo == null) { - var displayText = item == null ? itemId.ToString() : item.Name; - throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type)); + throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type)); } - bool cropwhitespace; + bool cropWhitespace; if (request.CropWhitespace.HasValue) { - cropwhitespace = request.CropWhitespace.Value; + cropWhitespace = request.CropWhitespace.Value; } else { - cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art; + cropWhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art; } var outputFormats = GetOutputFormats(request); @@ -589,13 +607,90 @@ namespace MediaBrowser.Api.Images itemId, request, imageInfo, - cropwhitespace, + cropWhitespace, outputFormats, cacheDuration, responseHeaders, isHeadRequest); } + public Task<object> GetImage(ImageRequest request, User user, bool isHeadRequest) + { + var imageInfo = GetImageInfo(request, user); + + TimeSpan? cacheDuration = null; + + if (!string.IsNullOrEmpty(request.Tag)) + { + cacheDuration = TimeSpan.FromDays(365); + } + + var responseHeaders = new Dictionary<string, string> + { + {"transferMode.dlna.org", "Interactive"}, + {"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"} + }; + + var outputFormats = GetOutputFormats(request); + + return GetImageResult(user.Id, + request, + imageInfo, + outputFormats, + cacheDuration, + responseHeaders, + isHeadRequest); + } + + private async Task<object> GetImageResult( + Guid itemId, + ImageRequest request, + ItemImageInfo info, + IReadOnlyCollection<ImageFormat> supportedFormats, + TimeSpan? cacheDuration, + IDictionary<string, string> headers, + bool isHeadRequest) + { + info.Type = ImageType.Profile; + var options = new ImageProcessingOptions + { + CropWhiteSpace = true, + Height = request.Height, + ImageIndex = request.Index ?? 0, + Image = info, + Item = null, // Hack alert + ItemId = itemId, + MaxHeight = request.MaxHeight, + MaxWidth = request.MaxWidth, + Quality = request.Quality ?? 100, + Width = request.Width, + AddPlayedIndicator = request.AddPlayedIndicator, + PercentPlayed = 0, + UnplayedCount = request.UnplayedCount, + Blur = request.Blur, + BackgroundColor = request.BackgroundColor, + ForegroundLayer = request.ForegroundLayer, + SupportedOutputFormats = supportedFormats + }; + + var imageResult = await _imageProcessor.ProcessImage(options).ConfigureAwait(false); + + headers[HeaderNames.Vary] = HeaderNames.Accept; + + return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions + { + CacheDuration = cacheDuration, + ResponseHeaders = headers, + ContentType = imageResult.Item2, + DateLastModified = imageResult.Item3, + IsHeadRequest = isHeadRequest, + Path = imageResult.Item1, + + FileShare = FileShare.Read + + }).ConfigureAwait(false); + } + private async Task<object> GetImageResult( BaseItem item, Guid itemId, @@ -648,7 +743,6 @@ namespace MediaBrowser.Api.Images Path = imageResult.Item1, FileShare = FileShare.Read - }).ConfigureAwait(false); } @@ -733,13 +827,35 @@ namespace MediaBrowser.Api.Images /// <param name="request">The request.</param> /// <param name="item">The item.</param> /// <returns>System.String.</returns> - private ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item) + private static ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item) { var index = request.Index ?? 0; return item.GetImageInfo(request.Type, index); } + private static ItemImageInfo GetImageInfo(ImageRequest request, User user) + { + var info = new ItemImageInfo + { + Path = user.ProfileImage.Path, + Type = ImageType.Primary, + DateModified = user.ProfileImage.LastModified, + }; + + if (request.Width.HasValue) + { + info.Width = request.Width.Value; + } + + if (request.Height.HasValue) + { + info.Height = request.Height.Value; + } + + return info; + } + /// <summary> /// Posts the image. /// </summary> @@ -750,22 +866,41 @@ namespace MediaBrowser.Api.Images /// <returns>Task.</returns> public async Task PostImage(BaseItem entity, Stream inputStream, ImageType imageType, string mimeType) { + var memoryStream = await GetMemoryStream(inputStream); + + // Handle image/png; charset=utf-8 + mimeType = mimeType.Split(';').FirstOrDefault(); + + await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false); + + entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + } + + private static async Task<MemoryStream> GetMemoryStream(Stream inputStream) + { using var reader = new StreamReader(inputStream); var text = await reader.ReadToEndAsync().ConfigureAwait(false); var bytes = Convert.FromBase64String(text); - - var memoryStream = new MemoryStream(bytes) + return new MemoryStream(bytes) { Position = 0 }; + } + + private async Task PostImage(User user, Stream inputStream, string mimeType) + { + var memoryStream = await GetMemoryStream(inputStream); // Handle image/png; charset=utf-8 mimeType = mimeType.Split(';').FirstOrDefault(); + var userDataPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username); + user.ProfileImage = new Jellyfin.Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType))); - await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false); - - entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); + await _providerManager + .SaveImage(user, memoryStream, mimeType, user.ProfileImage.Path) + .ConfigureAwait(false); + await _userManager.UpdateUserAsync(user); } } } diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index 23bf54712..86464b4b9 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Api.Images public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -147,13 +147,11 @@ namespace MediaBrowser.Api.Images { var item = _libraryManager.GetItemById(request.Id); - var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery + var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery(request.ProviderName) { - ProviderName = request.ProviderName, IncludeAllLanguages = request.IncludeAllLanguages, IncludeDisabledProviders = true, ImageType = request.Type - }, CancellationToken.None).ConfigureAwait(false); var imagesList = images.ToArray(); diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index 68e3dfa59..862411209 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -220,7 +220,7 @@ namespace MediaBrowser.Api { var item = _libraryManager.GetItemById(new Guid(request.Id)); - //foreach (var key in request.ProviderIds) + // foreach (var key in request.ProviderIds) //{ // var value = key.Value; @@ -233,8 +233,8 @@ namespace MediaBrowser.Api // Since the refresh process won't erase provider Ids, we need to set this explicitly now. item.ProviderIds = request.ProviderIds; - //item.ProductionYear = request.ProductionYear; - //item.Name = request.Name; + // item.ProductionYear = request.ProductionYear; + // item.Name = request.Name; return _providerManager.RefreshFullItem( item, diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index c0146dfee..6555864dc 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -6,6 +6,7 @@ using System.Net; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Entities; using MediaBrowser.Api.Movies; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; @@ -14,7 +15,6 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; @@ -27,6 +27,12 @@ using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; +using Book = MediaBrowser.Controller.Entities.Book; +using Episode = MediaBrowser.Controller.Entities.TV.Episode; +using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; +using Movie = MediaBrowser.Controller.Entities.Movies.Movie; +using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; +using Series = MediaBrowser.Controller.Entities.TV.Series; namespace MediaBrowser.Api.Library { @@ -43,7 +49,7 @@ namespace MediaBrowser.Api.Library } /// <summary> - /// Class GetCriticReviews + /// Class GetCriticReviews. /// </summary> [Route("/Items/{Id}/CriticReviews", "GET", Summary = "Gets critic reviews for an item")] [Authenticated] @@ -64,7 +70,7 @@ namespace MediaBrowser.Api.Library public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -72,7 +78,7 @@ namespace MediaBrowser.Api.Library } /// <summary> - /// Class GetThemeSongs + /// Class GetThemeSongs. /// </summary> [Route("/Items/{Id}/ThemeSongs", "GET", Summary = "Gets theme songs for an item")] [Authenticated] @@ -97,7 +103,7 @@ namespace MediaBrowser.Api.Library } /// <summary> - /// Class GetThemeVideos + /// Class GetThemeVideos. /// </summary> [Route("/Items/{Id}/ThemeVideos", "GET", Summary = "Gets theme videos for an item")] [Authenticated] @@ -122,7 +128,7 @@ namespace MediaBrowser.Api.Library } /// <summary> - /// Class GetThemeVideos + /// Class GetThemeVideos. /// </summary> [Route("/Items/{Id}/ThemeMedia", "GET", Summary = "Gets theme videos and songs for an item")] [Authenticated] @@ -199,7 +205,7 @@ namespace MediaBrowser.Api.Library } /// <summary> - /// Class GetPhyscialPaths + /// Class GetPhyscialPaths. /// </summary> [Route("/Library/PhysicalPaths", "GET", Summary = "Gets a list of physical paths from virtual folders")] [Authenticated(Roles = "Admin")] @@ -279,34 +285,43 @@ namespace MediaBrowser.Api.Library public class GetLibraryOptionsInfo : IReturn<LibraryOptionsResult> { public string LibraryContentType { get; set; } + public bool IsNewLibrary { get; set; } } public class LibraryOptionInfo { public string Name { get; set; } + public bool DefaultEnabled { get; set; } } public class LibraryOptionsResult { public LibraryOptionInfo[] MetadataSavers { get; set; } + public LibraryOptionInfo[] MetadataReaders { get; set; } + public LibraryOptionInfo[] SubtitleFetchers { get; set; } + public LibraryTypeOptions[] TypeOptions { get; set; } } public class LibraryTypeOptions { public string Type { get; set; } + public LibraryOptionInfo[] MetadataFetchers { get; set; } + public LibraryOptionInfo[] ImageFetchers { get; set; } + public ImageType[] SupportedImageTypes { get; set; } + public ImageOption[] DefaultImageOptions { get; set; } } /// <summary> - /// Class LibraryService + /// Class LibraryService. /// </summary> public class LibraryService : BaseApiService { @@ -350,6 +365,7 @@ namespace MediaBrowser.Api.Library _moviesServiceLogger = moviesServiceLogger; } + // Content Types available for each Library private string[] GetRepresentativeItemTypes(string contentType) { return contentType switch @@ -359,7 +375,7 @@ namespace MediaBrowser.Api.Library CollectionType.Movies => new[] {"Movie"}, CollectionType.TvShows => new[] {"Series", "Season", "Episode"}, CollectionType.Books => new[] {"Book"}, - CollectionType.Music => new[] {"MusicAlbum", "MusicArtist", "Audio", "MusicVideo"}, + CollectionType.Music => new[] {"MusicArtist", "MusicAlbum", "Audio", "MusicVideo"}, CollectionType.HomeVideos => new[] {"Video", "Photo"}, CollectionType.Photos => new[] {"Video", "Photo"}, CollectionType.MusicVideos => new[] {"MusicVideo"}, @@ -425,7 +441,6 @@ namespace MediaBrowser.Api.Library return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "Screen Grabber", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase) - || string.Equals(name, "Emby Designs", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "Image Extractor", StringComparison.OrdinalIgnoreCase); } @@ -556,7 +571,6 @@ namespace MediaBrowser.Api.Library _authContext) { Request = Request, - }.GetSimilarItemsResult(request); } @@ -654,8 +668,7 @@ namespace MediaBrowser.Api.Library { EnableImages = false } - - }).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray(); + }).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProvider.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray(); foreach (var item in series) { @@ -683,16 +696,15 @@ namespace MediaBrowser.Api.Library { EnableImages = false } - }); if (!string.IsNullOrWhiteSpace(request.ImdbId)) { - movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).ToList(); + movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProvider.Imdb), StringComparison.OrdinalIgnoreCase)).ToList(); } else if (!string.IsNullOrWhiteSpace(request.TmdbId)) { - movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)).ToList(); + movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProvider.Tmdb), StringComparison.OrdinalIgnoreCase)).ToList(); } else { @@ -763,8 +775,8 @@ namespace MediaBrowser.Api.Library { try { - _activityManager.Create(new Jellyfin.Data.Entities.ActivityLog( - string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name), + _activityManager.Create(new ActivityLog( + string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name), "UserDownloadingContent", auth.UserId) { @@ -1033,6 +1045,7 @@ namespace MediaBrowser.Api.Library { break; } + item = parent; } @@ -1090,6 +1103,7 @@ namespace MediaBrowser.Api.Library { break; } + item = parent; } diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index 1e300814f..b69550ed1 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -19,7 +19,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Library { /// <summary> - /// Class GetDefaultVirtualFolders + /// Class GetDefaultVirtualFolders. /// </summary> [Route("/Library/VirtualFolders", "GET")] public class GetVirtualFolders : IReturn<List<VirtualFolderInfo>> @@ -166,18 +166,18 @@ namespace MediaBrowser.Api.Library } /// <summary> - /// Class LibraryStructureService + /// Class LibraryStructureService. /// </summary> [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)] public class LibraryStructureService : BaseApiService { /// <summary> - /// The _app paths + /// The _app paths. /// </summary> private readonly IServerApplicationPaths _appPaths; /// <summary> - /// The _library manager + /// The _library manager. /// </summary> private readonly ILibraryManager _libraryManager; private readonly ILibraryMonitor _libraryMonitor; diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 5fe4c0cca..830372dd8 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -7,6 +7,7 @@ using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using MediaBrowser.Api.UserLibrary; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; @@ -30,7 +31,7 @@ using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.LiveTv { /// <summary> - /// This is insecure right now to avoid windows phone refactoring + /// This is insecure right now to avoid windows phone refactoring. /// </summary> [Route("/LiveTv/Info", "GET", Summary = "Gets available live tv services.")] [Authenticated] @@ -71,7 +72,7 @@ namespace MediaBrowser.Api.LiveTv public bool? IsSports { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -99,7 +100,7 @@ namespace MediaBrowser.Api.LiveTv public string EnableImageTypes { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -187,7 +188,7 @@ namespace MediaBrowser.Api.LiveTv public string EnableImageTypes { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -199,10 +200,15 @@ namespace MediaBrowser.Api.LiveTv public bool? EnableUserData { get; set; } public bool? IsMovie { get; set; } + public bool? IsSeries { get; set; } + public bool? IsKids { get; set; } + public bool? IsSports { get; set; } + public bool? IsNews { get; set; } + public bool? IsLibraryItem { get; set; } public GetRecordings() @@ -249,7 +255,7 @@ namespace MediaBrowser.Api.LiveTv public string EnableImageTypes { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -347,6 +353,7 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? HasAired { get; set; } + public bool? IsAiring { get; set; } [ApiMember(Name = "MaxStartDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] @@ -406,10 +413,11 @@ namespace MediaBrowser.Api.LiveTv public bool? EnableUserData { get; set; } public string SeriesTimerId { get; set; } + public Guid LibrarySeriesId { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -472,7 +480,7 @@ namespace MediaBrowser.Api.LiveTv public string GenreIds { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -600,7 +608,9 @@ namespace MediaBrowser.Api.LiveTv public class AddListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo> { public bool ValidateLogin { get; set; } + public bool ValidateListings { get; set; } + public string Pw { get; set; } } @@ -649,15 +659,20 @@ namespace MediaBrowser.Api.LiveTv { [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")] public string ProviderId { get; set; } + public string TunerChannelId { get; set; } + public string ProviderChannelId { get; set; } } public class ChannelMappingOptions { public List<TunerChannelMapping> TunerChannels { get; set; } + public List<NameIdPair> ProviderChannels { get; set; } + public NameValuePair[] Mappings { get; set; } + public string ProviderName { get; set; } } @@ -665,6 +680,7 @@ namespace MediaBrowser.Api.LiveTv public class GetLiveStreamFile { public string Id { get; set; } + public string Container { get; set; } } @@ -678,7 +694,6 @@ namespace MediaBrowser.Api.LiveTv [Authenticated] public class GetTunerHostTypes : IReturn<List<NameIdPair>> { - } [Route("/LiveTv/Tuners/Discvover", "GET")] @@ -825,7 +840,6 @@ namespace MediaBrowser.Api.LiveTv { Name = i.Name, Id = i.Id - }).ToList(), Mappings = mappings, @@ -844,7 +858,6 @@ namespace MediaBrowser.Api.LiveTv { Url = "https://json.schedulesdirect.org/20141201/available/countries", BufferContent = false - }).ConfigureAwait(false); return ResultFactory.GetResult(Request, response, "application/json"); @@ -859,7 +872,7 @@ namespace MediaBrowser.Api.LiveTv throw new SecurityException("Anonymous live tv management is not allowed."); } - if (!user.Policy.EnableLiveTvManagement) + if (!user.HasPermission(PermissionKind.EnableLiveTvManagement)) { throw new SecurityException("The current user does not have permission to manage live tv."); } @@ -957,7 +970,6 @@ namespace MediaBrowser.Api.LiveTv SortBy = request.GetOrderBy(), SortOrder = request.SortOrder ?? SortOrder.Ascending, AddCurrentProgram = request.AddCurrentProgram - }, options, CancellationToken.None); var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); @@ -1112,7 +1124,6 @@ namespace MediaBrowser.Api.LiveTv Fields = request.GetItemFields(), ImageTypeLimit = request.ImageTypeLimit, EnableImages = request.EnableImages - }, options); return ToOptimizedResult(result); @@ -1151,7 +1162,6 @@ namespace MediaBrowser.Api.LiveTv SeriesTimerId = request.SeriesTimerId, IsActive = request.IsActive, IsScheduled = request.IsScheduled - }, CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); @@ -1187,7 +1197,6 @@ namespace MediaBrowser.Api.LiveTv { SortOrder = request.SortOrder, SortBy = request.SortBy - }, CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); diff --git a/MediaBrowser.Api/LocalizationService.cs b/MediaBrowser.Api/LocalizationService.cs index 6a69d2656..d6b5f5195 100644 --- a/MediaBrowser.Api/LocalizationService.cs +++ b/MediaBrowser.Api/LocalizationService.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class GetCultures + /// Class GetCultures. /// </summary> [Route("/Localization/Cultures", "GET", Summary = "Gets known cultures")] public class GetCultures : IReturn<CultureDto[]> @@ -16,7 +16,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class GetCountries + /// Class GetCountries. /// </summary> [Route("/Localization/Countries", "GET", Summary = "Gets known countries")] public class GetCountries : IReturn<CountryInfo[]> @@ -24,7 +24,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class ParentalRatings + /// Class ParentalRatings. /// </summary> [Route("/Localization/ParentalRatings", "GET", Summary = "Gets known parental ratings")] public class GetParentalRatings : IReturn<ParentalRating[]> @@ -32,7 +32,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class ParentalRatings + /// Class ParentalRatings. /// </summary> [Route("/Localization/Options", "GET", Summary = "Gets localization options")] public class GetLocalizationOptions : IReturn<LocalizationOption[]> @@ -40,13 +40,13 @@ namespace MediaBrowser.Api } /// <summary> - /// Class CulturesService + /// Class CulturesService. /// </summary> [Authenticated(AllowBeforeStartupWizard = true)] public class LocalizationService : BaseApiService { /// <summary> - /// The _localization + /// The _localization. /// </summary> private readonly ILocalizationManager _localization; diff --git a/MediaBrowser.Api/Movies/CollectionService.cs b/MediaBrowser.Api/Movies/CollectionService.cs index 95a37dfc5..e9629439d 100644 --- a/MediaBrowser.Api/Movies/CollectionService.cs +++ b/MediaBrowser.Api/Movies/CollectionService.cs @@ -79,7 +79,6 @@ namespace MediaBrowser.Api.Movies ParentId = parentId, ItemIdList = SplitValue(request.Ids, ','), UserIds = new[] { userId } - }); var dtoOptions = GetDtoOptions(_authContext, request); diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index cdd027634..34cccffa3 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -2,11 +2,11 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using Jellyfin.Data.Entities; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Net; @@ -15,6 +15,8 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; +using Movie = MediaBrowser.Controller.Entities.Movies.Movie; namespace MediaBrowser.Api.Movies { @@ -63,13 +65,13 @@ namespace MediaBrowser.Api.Movies } /// <summary> - /// Class MoviesService + /// Class MoviesService. /// </summary> [Authenticated] public class MoviesService : BaseApiService { /// <summary> - /// The _user manager + /// The _user manager. /// </summary> private readonly IUserManager _userManager; @@ -159,8 +161,8 @@ namespace MediaBrowser.Api.Movies IncludeItemTypes = new[] { typeof(Movie).Name, - //typeof(Trailer).Name, - //typeof(LiveTvProgram).Name + // typeof(Trailer).Name, + // typeof(LiveTvProgram).Name }, // IsMovie = true OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), @@ -192,7 +194,6 @@ namespace MediaBrowser.Api.Movies ParentId = parentIdGuid, Recursive = true, DtoOptions = dtoOptions - }); var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList(); @@ -251,7 +252,12 @@ namespace MediaBrowser.Api.Movies return categories.OrderBy(i => i.RecommendationType); } - private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) + private IEnumerable<RecommendationDto> GetWithDirector( + User user, + IEnumerable<string> names, + int itemLimit, + DtoOptions dtoOptions, + RecommendationType type) { var itemTypes = new List<string> { typeof(Movie).Name }; if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions) @@ -272,8 +278,7 @@ namespace MediaBrowser.Api.Movies IsMovie = true, EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - - }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) + }).GroupBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Select(x => x.First()) .Take(itemLimit) .ToList(); @@ -313,8 +318,7 @@ namespace MediaBrowser.Api.Movies IsMovie = true, EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - - }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) + }).GroupBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Select(x => x.First()) .Take(itemLimit) .ToList(); @@ -353,7 +357,6 @@ namespace MediaBrowser.Api.Movies SimilarTo = item, EnableGroupByMetadataKey = true, DtoOptions = dtoOptions - }); if (similar.Count > 0) diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs index 0b5334235..ca9f9d03b 100644 --- a/MediaBrowser.Api/Movies/TrailersService.cs +++ b/MediaBrowser.Api/Movies/TrailersService.cs @@ -18,18 +18,18 @@ namespace MediaBrowser.Api.Movies } /// <summary> - /// Class TrailersService + /// Class TrailersService. /// </summary> [Authenticated] public class TrailersService : BaseApiService { /// <summary> - /// The _user manager + /// The _user manager. /// </summary> private readonly IUserManager _userManager; /// <summary> - /// The _library manager + /// The _library manager. /// </summary> private readonly ILibraryManager _libraryManager; @@ -82,7 +82,6 @@ namespace MediaBrowser.Api.Movies _authContext) { Request = Request, - }.Get(getItems); } } diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs index 58c95d053..74d3cce12 100644 --- a/MediaBrowser.Api/Music/AlbumsService.cs +++ b/MediaBrowser.Api/Music/AlbumsService.cs @@ -27,16 +27,16 @@ namespace MediaBrowser.Api.Music public class AlbumsService : BaseApiService { /// <summary> - /// The _user manager + /// The _user manager. /// </summary> private readonly IUserManager _userManager; /// <summary> - /// The _user data repository + /// The _user data repository. /// </summary> private readonly IUserDataManager _userDataRepository; /// <summary> - /// The _library manager + /// The _library manager. /// </summary> private readonly ILibraryManager _libraryManager; private readonly IItemRepository _itemRepo; @@ -67,12 +67,13 @@ namespace MediaBrowser.Api.Music { var dtoOptions = GetDtoOptions(_authContext, request); - var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager, + var result = SimilarItemsHelper.GetSimilarItemsResult( + dtoOptions, + _userManager, _itemRepo, _libraryManager, _userDataRepository, _dtoService, - Logger, request, new[] { typeof(MusicArtist) }, SimilarItemsHelper.GetSimiliarityScore); @@ -88,12 +89,13 @@ namespace MediaBrowser.Api.Music { var dtoOptions = GetDtoOptions(_authContext, request); - var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager, + var result = SimilarItemsHelper.GetSimilarItemsResult( + dtoOptions, + _userManager, _itemRepo, _libraryManager, _userDataRepository, _dtoService, - Logger, request, new[] { typeof(MusicAlbum) }, GetAlbumSimilarityScore); diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index cacec8d64..ebd3eb64a 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -191,6 +192,5 @@ namespace MediaBrowser.Api.Music return result; } - } } diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index 444354a99..a84556fcc 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -13,8 +13,20 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { + [Route("/Repositories", "GET", Summary = "Gets all package repositories")] + [Authenticated] + public class GetRepositories : IReturnVoid + { + } + + [Route("/Repositories", "POST", Summary = "Sets the enabled and existing package repositories")] + [Authenticated] + public class SetRepositories : List<RepositoryInfo>, IReturnVoid + { + } + /// <summary> - /// Class GetPackage + /// Class GetPackage. /// </summary> [Route("/Packages/{Name}", "GET", Summary = "Gets a package, by name or assembly guid")] [Authenticated] @@ -36,7 +48,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class GetPackages + /// Class GetPackages. /// </summary> [Route("/Packages", "GET", Summary = "Gets available packages")] [Authenticated] @@ -45,7 +57,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class InstallPackage + /// Class InstallPackage. /// </summary> [Route("/Packages/Installed/{Name}", "POST", Summary = "Installs a package")] [Authenticated(Roles = "Admin")] @@ -74,7 +86,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class CancelPackageInstallation + /// Class CancelPackageInstallation. /// </summary> [Route("/Packages/Installing/{Id}", "DELETE", Summary = "Cancels a package installation")] [Authenticated(Roles = "Admin")] @@ -89,11 +101,12 @@ namespace MediaBrowser.Api } /// <summary> - /// Class PackageService + /// Class PackageService. /// </summary> public class PackageService : BaseApiService { private readonly IInstallationManager _installationManager; + private readonly IServerConfigurationManager _serverConfigurationManager; public PackageService( ILogger<PackageService> logger, @@ -103,6 +116,19 @@ namespace MediaBrowser.Api : base(logger, serverConfigurationManager, httpResultFactory) { _installationManager = installationManager; + _serverConfigurationManager = serverConfigurationManager; + } + + public object Get(GetRepositories request) + { + var result = _serverConfigurationManager.Configuration.PluginRepositories; + return ToOptimizedResult(result); + } + + public void Post(SetRepositories request) + { + _serverConfigurationManager.Configuration.PluginRepositories = request; + _serverConfigurationManager.SaveConfiguration(); } /// <summary> diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index f796aa486..84ed5dcac 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; @@ -27,7 +28,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback { /// <summary> - /// Class BaseStreamingService + /// Class BaseStreamingService. /// </summary> public abstract class BaseStreamingService : BaseApiService { @@ -193,10 +194,10 @@ namespace MediaBrowser.Api.Playback await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false); - if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (state.VideoRequest != null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { var auth = AuthorizationContext.GetAuthorizationInfo(Request); - if (auth.User != null && !auth.User.Policy.EnableVideoPlaybackTranscoding) + if (auth.User != null && !auth.User.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)) { ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state); @@ -215,7 +216,7 @@ namespace MediaBrowser.Api.Playback UseShellExecute = false, // Must consume both stdout and stderr or deadlocks may occur - //RedirectStandardOutput = true, + // RedirectStandardOutput = true, RedirectStandardError = true, RedirectStandardInput = true, @@ -243,9 +244,9 @@ namespace MediaBrowser.Api.Playback var logFilePrefix = "ffmpeg-transcode"; if (state.VideoRequest != null - && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + && EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { - logFilePrefix = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase) + logFilePrefix = EncodingHelper.IsCopyCodec(state.OutputAudioCodec) ? "ffmpeg-remux" : "ffmpeg-directstream"; } @@ -302,6 +303,7 @@ namespace MediaBrowser.Api.Playback { StartThrottler(state, transcodingJob); } + Logger.LogDebug("StartFfMpeg() finished successfully"); return transcodingJob; @@ -328,7 +330,7 @@ namespace MediaBrowser.Api.Playback state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo && state.VideoType == VideoType.VideoFile && - !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase); + !EncodingHelper.IsCopyCodec(state.OutputVideoCodec); } return false; @@ -607,6 +609,7 @@ namespace MediaBrowser.Api.Playback { throw new ArgumentException("Invalid timeseek header"); } + int index = value.IndexOf('-'); value = index == -1 ? value.Substring(Npt.Length) @@ -638,8 +641,10 @@ namespace MediaBrowser.Api.Playback { throw new ArgumentException("Invalid timeseek header"); } + timeFactor /= 60; } + return TimeSpan.FromSeconds(secondsSum).Ticks; } @@ -684,7 +689,7 @@ namespace MediaBrowser.Api.Playback state.User = UserManager.GetUserById(auth.UserId); } - //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || + // if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1) //{ @@ -715,9 +720,9 @@ namespace MediaBrowser.Api.Playback state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - //var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ?? + // var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ?? // item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null); - //if (primaryImage != null) + // if (primaryImage != null) //{ // state.AlbumCoverPath = primaryImage.Path; //} @@ -791,7 +796,7 @@ namespace MediaBrowser.Api.Playback EncodingHelper.TryStreamCopy(state); } - if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (state.OutputVideoBitrate.HasValue && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { var resolution = ResolutionNormalizer.Normalize( state.VideoStream?.BitRate, @@ -884,7 +889,7 @@ namespace MediaBrowser.Api.Playback if (transcodingProfile != null) { state.EstimateContentLength = transcodingProfile.EstimateContentLength; - //state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; + // state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; if (state.VideoRequest != null) diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 627421aac..418cd92b3 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback.Hls { /// <summary> - /// Class BaseHlsService + /// Class BaseHlsService. /// </summary> public abstract class BaseHlsService : BaseStreamingService { @@ -146,6 +146,7 @@ namespace MediaBrowser.Api.Playback.Hls { ApiEntryPoint.Instance.OnTranscodeEndRequest(job); } + return ResultFactory.GetResult(GetLivePlaylistText(playlist, state.SegmentLength), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } @@ -178,7 +179,7 @@ namespace MediaBrowser.Api.Playback.Hls var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(CultureInfo.InvariantCulture); text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength - 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase); - //text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase); + // text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase); return text; } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index b8ea9c6da..fe5f980b1 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -234,6 +234,7 @@ namespace MediaBrowser.Api.Playback.Hls Logger.LogDebug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", requestedIndex - currentTranscodingIndex.Value, segmentGapRequiringTranscodingChange, requestedIndex); startTranscoding = true; } + if (startTranscoding) { // If the playlist doesn't already exist, startup ffmpeg @@ -257,7 +258,7 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - //await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false); + // await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false); } else { @@ -277,8 +278,8 @@ namespace MediaBrowser.Api.Playback.Hls } } - //Logger.LogInformation("waiting for {0}", segmentPath); - //while (!File.Exists(segmentPath)) + // Logger.LogInformation("waiting for {0}", segmentPath); + // while (!File.Exists(segmentPath)) //{ // await Task.Delay(50, cancellationToken).ConfigureAwait(false); //} @@ -518,6 +519,7 @@ namespace MediaBrowser.Api.Playback.Hls { Logger.LogDebug("serving {0} as it's on disk and transcoding stopped", segmentPath); } + cancellationToken.ThrowIfCancellationRequested(); } else @@ -700,12 +702,12 @@ namespace MediaBrowser.Api.Playback.Hls return false; } - if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { return false; } - if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(state.OutputAudioCodec)) { return false; } @@ -717,7 +719,7 @@ namespace MediaBrowser.Api.Playback.Hls // Having problems in android return false; - //return state.VideoRequest.VideoBitRate.HasValue; + // return state.VideoRequest.VideoBitRate.HasValue; } /// <summary> @@ -728,7 +730,7 @@ namespace MediaBrowser.Api.Playback.Hls private int? GetOutputVideoCodecLevel(StreamState state) { string levelString; - if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) + if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec) && state.VideoStream.Level.HasValue) { levelString = state.VideoStream?.Level.ToString(); @@ -872,9 +874,8 @@ namespace MediaBrowser.Api.Playback.Hls if (framerate.HasValue) { - builder.Append(",FRAME-RATE=\"") - .Append(framerate.Value) - .Append('"'); + builder.Append(",FRAME-RATE=") + .Append(framerate.Value); } } @@ -888,11 +889,10 @@ namespace MediaBrowser.Api.Playback.Hls { if (state.OutputWidth.HasValue && state.OutputHeight.HasValue) { - builder.Append(",RESOLUTION=\"") + builder.Append(",RESOLUTION=") .Append(state.OutputWidth.GetValueOrDefault()) .Append('x') - .Append(state.OutputHeight.GetValueOrDefault()) - .Append('"'); + .Append(state.OutputHeight.GetValueOrDefault()); } } @@ -974,7 +974,7 @@ namespace MediaBrowser.Api.Playback.Hls var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); - //if ((Request.UserAgent ?? string.Empty).IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1) + // if ((Request.UserAgent ?? string.Empty).IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1) //{ // queryString = string.Empty; //} @@ -1008,7 +1008,7 @@ namespace MediaBrowser.Api.Playback.Hls if (!state.IsOutputVideo) { - if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(audioCodec)) { return "-acodec copy"; } @@ -1036,11 +1036,11 @@ namespace MediaBrowser.Api.Playback.Hls return string.Join(" ", audioTranscodeParams.ToArray()); } - if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(audioCodec)) { var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions); - if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.EnableBreakOnNonKeyFrames(videoCodec)) + if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec)) { return "-codec:a:0 copy -copypriorss:a:0 0"; } @@ -1091,7 +1091,7 @@ namespace MediaBrowser.Api.Playback.Hls // } // See if we can save come cpu cycles by avoiding encoding - if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(codec)) { if (state.VideoStream != null && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase)) { @@ -1102,7 +1102,7 @@ namespace MediaBrowser.Api.Playback.Hls } } - //args += " -flags -global_header"; + // args += " -flags -global_header"; } else { @@ -1144,7 +1144,7 @@ namespace MediaBrowser.Api.Playback.Hls args += " " + keyFrameArg + gopArg; } - //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; + // args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; @@ -1166,7 +1166,7 @@ namespace MediaBrowser.Api.Playback.Hls args += " -start_at_zero"; } - //args += " -flags -global_header"; + // args += " -flags -global_header"; } if (!string.IsNullOrEmpty(state.OutputVideoSync)) diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 87ccde2e0..8a3d00283 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback.Hls { /// <summary> - /// Class GetHlsAudioSegment + /// Class GetHlsAudioSegment. /// </summary> // Can't require authentication just yet due to seeing some requests come from Chrome without full query string //[Authenticated] @@ -37,7 +37,7 @@ namespace MediaBrowser.Api.Playback.Hls } /// <summary> - /// Class GetHlsVideoSegment + /// Class GetHlsVideoSegment. /// </summary> [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")] [Authenticated] @@ -66,7 +66,7 @@ namespace MediaBrowser.Api.Playback.Hls } /// <summary> - /// Class GetHlsVideoSegment + /// Class GetHlsVideoSegment. /// </summary> // Can't require authentication just yet due to seeing some requests come from Chrome without full query string //[Authenticated] diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index d1c53c1c1..9562f9953 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Api.Playback.Hls } /// <summary> - /// Class VideoHlsService + /// Class VideoHlsService. /// </summary> [Authenticated] public class VideoHlsService : BaseHlsService @@ -72,7 +72,7 @@ namespace MediaBrowser.Api.Playback.Hls { var codec = EncodingHelper.GetAudioEncoder(state); - if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(codec)) { return "-codec:a:0 copy"; } diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index e2d771ec6..b7ca1a031 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; @@ -400,21 +401,24 @@ namespace MediaBrowser.Api.Playback if (item is Audio) { - Logger.LogInformation("User policy for {0}. EnableAudioPlaybackTranscoding: {1}", user.Name, user.Policy.EnableAudioPlaybackTranscoding); + Logger.LogInformation( + "User policy for {0}. EnableAudioPlaybackTranscoding: {1}", + user.Username, + user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)); } else { Logger.LogInformation("User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}", - user.Name, - user.Policy.EnablePlaybackRemuxing, - user.Policy.EnableVideoPlaybackTranscoding, - user.Policy.EnableAudioPlaybackTranscoding); + user.Username, + user.HasPermission(PermissionKind.EnablePlaybackRemuxing), + user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding), + user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)); } // Beginning of Playback Determination: Attempt DirectPlay first if (mediaSource.SupportsDirectPlay) { - if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) { mediaSource.SupportsDirectPlay = false; } @@ -428,14 +432,16 @@ namespace MediaBrowser.Api.Playback if (item is Audio) { - if (!user.Policy.EnableAudioPlaybackTranscoding) + if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)) { options.ForceDirectPlay = true; } } else if (item is Video) { - if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing) + if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding) + && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding) + && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing)) { options.ForceDirectPlay = true; } @@ -463,7 +469,7 @@ namespace MediaBrowser.Api.Playback if (mediaSource.SupportsDirectStream) { - if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) { mediaSource.SupportsDirectStream = false; } @@ -473,14 +479,16 @@ namespace MediaBrowser.Api.Playback if (item is Audio) { - if (!user.Policy.EnableAudioPlaybackTranscoding) + if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)) { options.ForceDirectStream = true; } } else if (item is Video) { - if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing) + if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding) + && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding) + && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing)) { options.ForceDirectStream = true; } @@ -512,7 +520,7 @@ namespace MediaBrowser.Api.Playback ? streamBuilder.BuildAudioItem(options) : streamBuilder.BuildVideoItem(options); - if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding) + if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding)) { if (streamInfo != null) { @@ -543,10 +551,12 @@ namespace MediaBrowser.Api.Playback { mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; } + if (!allowAudioStreamCopy) { mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; } + mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; } @@ -576,10 +586,10 @@ namespace MediaBrowser.Api.Playback } } - private long? GetMaxBitrate(long? clientMaxBitrate, User user) + private long? GetMaxBitrate(long? clientMaxBitrate, Jellyfin.Data.Entities.User user) { var maxBitrate = clientMaxBitrate; - var remoteClientMaxBitrate = user?.Policy.RemoteClientBitrateLimit ?? 0; + var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0; if (remoteClientMaxBitrate <= 0) { @@ -638,7 +648,6 @@ namespace MediaBrowser.Api.Playback } return 1; - }).ThenBy(i => { // Let's assume direct streaming a file is just as desirable as direct playing a remote url @@ -648,7 +657,6 @@ namespace MediaBrowser.Api.Playback } return 1; - }).ThenBy(i => { return i.Protocol switch @@ -664,7 +672,6 @@ namespace MediaBrowser.Api.Playback } return 1; - }).ThenBy(originalList.IndexOf) .ToArray(); } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 34c7986ca..d51787df2 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback.Progressive { /// <summary> - /// Class GetAudioStream + /// Class GetAudioStream. /// </summary> [Route("/Audio/{Id}/stream.{Container}", "GET", Summary = "Gets an audio stream")] [Route("/Audio/{Id}/stream", "GET", Summary = "Gets an audio stream")] @@ -26,7 +26,7 @@ namespace MediaBrowser.Api.Playback.Progressive } /// <summary> - /// Class AudioService + /// Class AudioService. /// </summary> // TODO: In order to autheneticate this in the future, Dlna playback will require updating //[Authenticated] diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index c7bf055fb..2ebf0e420 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -21,7 +21,7 @@ using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Playback.Progressive { /// <summary> - /// Class BaseProgressiveStreamingService + /// Class BaseProgressiveStreamingService. /// </summary> public abstract class BaseProgressiveStreamingService : BaseStreamingService { @@ -88,14 +88,17 @@ namespace MediaBrowser.Api.Playback.Progressive { return ".ts"; } + if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase)) { return ".ogv"; } + if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase)) { return ".webm"; } + if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase)) { return ".asf"; @@ -111,14 +114,17 @@ namespace MediaBrowser.Api.Playback.Progressive { return ".aac"; } + if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase)) { return ".mp3"; } + if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase)) { return ".ogg"; } + if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase)) { return ".wma"; @@ -231,7 +237,7 @@ namespace MediaBrowser.Api.Playback.Progressive } //// Not static but transcode cache file exists - //if (isTranscodeCached && state.VideoRequest == null) + // if (isTranscodeCached && state.VideoRequest == null) //{ // var contentType = state.GetMimeType(outputPath); diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index a53b848f9..b70fff128 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -23,6 +23,7 @@ namespace MediaBrowser.Api.Playback.Progressive private long _bytesWritten = 0; public long StartPosition { get; set; } + public bool AllowEndOfFile = true; private readonly IDirectStreamProvider _directStreamProvider; @@ -96,8 +97,8 @@ namespace MediaBrowser.Api.Playback.Progressive bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false); } - //var position = fs.Position; - //_logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); + // var position = fs.Position; + // _logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); if (bytesRead == 0) { @@ -105,6 +106,7 @@ namespace MediaBrowser.Api.Playback.Progressive { eofCount++; } + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } else diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 4de81655c..c3f6b905c 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback.Progressive { /// <summary> - /// Class GetVideoStream + /// Class GetVideoStream. /// </summary> [Route("/Videos/{Id}/stream.mpegts", "GET")] [Route("/Videos/{Id}/stream.ts", "GET")] @@ -59,11 +59,10 @@ namespace MediaBrowser.Api.Playback.Progressive [Route("/Videos/{Id}/stream", "HEAD")] public class GetVideoStream : VideoStreamRequest { - } /// <summary> - /// Class VideoService + /// Class VideoService. /// </summary> // TODO: In order to autheneticate this in the future, Dlna playback will require updating //[Authenticated] diff --git a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs index 3b8b29995..7e2e337ad 100644 --- a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs +++ b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs @@ -8,17 +8,17 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Api.Playback { /// <summary> - /// Class StaticRemoteStreamWriter + /// Class StaticRemoteStreamWriter. /// </summary> public class StaticRemoteStreamWriter : IAsyncStreamWriter, IHasHeaders { /// <summary> - /// The _input stream + /// The _input stream. /// </summary> private readonly HttpResponseInfo _response; /// <summary> - /// The _options + /// The _options. /// </summary> private readonly IDictionary<string, string> _options = new Dictionary<string, string>(); diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 9ba8eda91..67c334e48 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -4,7 +4,7 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Api.Playback { /// <summary> - /// Class StreamRequest + /// Class StreamRequest. /// </summary> public class StreamRequest : BaseEncodingJobOptions { @@ -12,11 +12,15 @@ namespace MediaBrowser.Api.Playback public string DeviceProfileId { get; set; } public string Params { get; set; } + public string PlaySessionId { get; set; } + public string Tag { get; set; } + public string SegmentContainer { get; set; } public int? SegmentLength { get; set; } + public int? MinSegments { get; set; } } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index d5d2f58c0..c244b0033 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback return Request.SegmentLength.Value; } - if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) + if (EncodingHelper.IsCopyCodec(OutputVideoCodec)) { var userAgent = UserAgent ?? string.Empty; diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index a3b319d44..d5d78cf37 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -37,10 +37,13 @@ namespace MediaBrowser.Api.Playback public string DeviceId { get; set; } public Guid UserId { get; set; } + public string AudioCodec { get; set; } + public string Container { get; set; } public int? MaxAudioChannels { get; set; } + public int? TranscodingAudioChannels { get; set; } public long? MaxStreamingBitrate { get; set; } @@ -49,12 +52,17 @@ namespace MediaBrowser.Api.Playback public long? StartTimeTicks { get; set; } public string TranscodingContainer { get; set; } + public string TranscodingProtocol { get; set; } + public int? MaxAudioSampleRate { get; set; } + public int? MaxAudioBitDepth { get; set; } public bool EnableRedirection { get; set; } + public bool EnableRemoteMedia { get; set; } + public bool BreakOnNonKeyFrames { get; set; } public BaseUniversalRequest() @@ -114,16 +122,27 @@ namespace MediaBrowser.Api.Playback } protected IHttpClient HttpClient { get; private set; } + protected IUserManager UserManager { get; private set; } + protected ILibraryManager LibraryManager { get; private set; } + protected IIsoManager IsoManager { get; private set; } + protected IMediaEncoder MediaEncoder { get; private set; } + protected IFileSystem FileSystem { get; private set; } + protected IDlnaManager DlnaManager { get; private set; } + protected IDeviceManager DeviceManager { get; private set; } + protected IMediaSourceManager MediaSourceManager { get; private set; } + protected IJsonSerializer JsonSerializer { get; private set; } + protected IAuthorizationContext AuthorizationContext { get; private set; } + protected INetworkManager NetworkManager { get; private set; } public Task<object> Get(GetUniversalAudioStream request) @@ -259,7 +278,6 @@ namespace MediaBrowser.Api.Playback UserId = request.UserId, DeviceProfile = deviceProfile, MediaSourceId = request.MediaSourceId - }).ConfigureAwait(false); var mediaSource = playbackInfoResult.MediaSources[0]; @@ -329,6 +347,7 @@ namespace MediaBrowser.Api.Playback { return await service.Head(newRequest).ConfigureAwait(false); } + return await service.Get(newRequest).ConfigureAwait(false); } else diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs index 953b00e35..5513c0892 100644 --- a/MediaBrowser.Api/PlaylistService.cs +++ b/MediaBrowser.Api/PlaylistService.cs @@ -95,14 +95,14 @@ namespace MediaBrowser.Api public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -161,7 +161,6 @@ namespace MediaBrowser.Api ItemIdList = GetGuids(request.Ids), UserId = request.UserId, MediaType = request.MediaType - }).ConfigureAwait(false); return ToOptimizedResult(result); diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs index 7f74511ee..7d976ceaa 100644 --- a/MediaBrowser.Api/PluginService.cs +++ b/MediaBrowser.Api/PluginService.cs @@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class Plugins + /// Class Plugins. /// </summary> [Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")] [Authenticated] @@ -25,7 +25,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UninstallPlugin + /// Class UninstallPlugin. /// </summary> [Route("/Plugins/{Id}", "DELETE", Summary = "Uninstalls a plugin")] [Authenticated(Roles = "Admin")] @@ -40,7 +40,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class GetPluginConfiguration + /// Class GetPluginConfiguration. /// </summary> [Route("/Plugins/{Id}/Configuration", "GET", Summary = "Gets a plugin's configuration")] [Authenticated] @@ -55,7 +55,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UpdatePluginConfiguration + /// Class UpdatePluginConfiguration. /// </summary> [Route("/Plugins/{Id}/Configuration", "POST", Summary = "Updates a plugin's configuration")] [Authenticated] @@ -69,13 +69,13 @@ namespace MediaBrowser.Api public string Id { get; set; } /// <summary> - /// The raw Http Request Input Stream + /// The raw Http Request Input Stream. /// </summary> /// <value>The request stream.</value> public Stream RequestStream { get; set; } } - //TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins, + // TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins, // delete all these registration endpoints. They are only kept for compatibility. [Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)] [Authenticated] @@ -86,7 +86,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class GetPluginSecurityInfo + /// Class GetPluginSecurityInfo. /// </summary> [Route("/Plugins/SecurityInfo", "GET", Summary = "Gets plugin registration information", IsHidden = true)] [Authenticated] @@ -95,7 +95,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UpdatePluginSecurityInfo + /// Class UpdatePluginSecurityInfo. /// </summary> [Route("/Plugins/SecurityInfo", "POST", Summary = "Updates plugin registration information", IsHidden = true)] [Authenticated(Roles = "Admin")] @@ -115,38 +115,47 @@ namespace MediaBrowser.Api public class RegistrationInfo { public string Name { get; set; } + public DateTime ExpirationDate { get; set; } + public bool IsTrial { get; set; } + public bool IsRegistered { get; set; } } public class MBRegistrationRecord { public DateTime ExpirationDate { get; set; } + public bool IsRegistered { get; set; } + public bool RegChecked { get; set; } + public bool RegError { get; set; } + public bool TrialVersion { get; set; } + public bool IsValid { get; set; } } public class PluginSecurityInfo { public string SupporterKey { get; set; } + public bool IsMBSupporter { get; set; } } /// <summary> - /// Class PluginsService + /// Class PluginsService. /// </summary> public class PluginService : BaseApiService { /// <summary> - /// The _json serializer + /// The _json serializer. /// </summary> private readonly IJsonSerializer _jsonSerializer; /// <summary> - /// The _app host + /// The _app host. /// </summary> private readonly IApplicationHost _appHost; private readonly IInstallationManager _installationManager; diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs index e08a8482e..86b00316a 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs @@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.ScheduledTasks { /// <summary> - /// Class GetScheduledTask + /// Class GetScheduledTask. /// </summary> [Route("/ScheduledTasks/{Id}", "GET", Summary = "Gets a scheduled task, by Id")] public class GetScheduledTask : IReturn<TaskInfo> @@ -25,7 +25,7 @@ namespace MediaBrowser.Api.ScheduledTasks } /// <summary> - /// Class GetScheduledTasks + /// Class GetScheduledTasks. /// </summary> [Route("/ScheduledTasks", "GET", Summary = "Gets scheduled tasks")] public class GetScheduledTasks : IReturn<TaskInfo[]> @@ -38,7 +38,7 @@ namespace MediaBrowser.Api.ScheduledTasks } /// <summary> - /// Class StartScheduledTask + /// Class StartScheduledTask. /// </summary> [Route("/ScheduledTasks/Running/{Id}", "POST", Summary = "Starts a scheduled task")] public class StartScheduledTask : IReturnVoid @@ -52,7 +52,7 @@ namespace MediaBrowser.Api.ScheduledTasks } /// <summary> - /// Class StopScheduledTask + /// Class StopScheduledTask. /// </summary> [Route("/ScheduledTasks/Running/{Id}", "DELETE", Summary = "Stops a scheduled task")] public class StopScheduledTask : IReturnVoid @@ -66,7 +66,7 @@ namespace MediaBrowser.Api.ScheduledTasks } /// <summary> - /// Class UpdateScheduledTaskTriggers + /// Class UpdateScheduledTaskTriggers. /// </summary> [Route("/ScheduledTasks/{Id}/Triggers", "POST", Summary = "Updates the triggers for a scheduled task")] public class UpdateScheduledTaskTriggers : List<TaskTriggerInfo>, IReturnVoid @@ -80,7 +80,7 @@ namespace MediaBrowser.Api.ScheduledTasks } /// <summary> - /// Class ScheduledTasksService + /// Class ScheduledTasksService. /// </summary> [Authenticated(Roles = "Admin")] public class ScheduledTaskService : BaseApiService diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index 14b9b3618..25dd39f2d 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.ScheduledTasks { /// <summary> - /// Class ScheduledTasksWebSocketListener + /// Class ScheduledTasksWebSocketListener. /// </summary> public class ScheduledTasksWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<TaskInfo>, WebSocketListenerState> { diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index e9d339c6e..64ee69300 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -18,7 +18,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class GetSearchHints + /// Class GetSearchHints. /// </summary> [Route("/Search/Hints", "GET", Summary = "Gets search hints based on a search term")] public class GetSearchHints : IReturn<SearchHintResult> @@ -31,7 +31,7 @@ namespace MediaBrowser.Api public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -45,7 +45,7 @@ namespace MediaBrowser.Api public Guid UserId { get; set; } /// <summary> - /// Search characters used to find items + /// Search characters used to find items. /// </summary> /// <value>The index by.</value> [ApiMember(Name = "SearchTerm", Description = "The search term to filter on", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -104,13 +104,13 @@ namespace MediaBrowser.Api } /// <summary> - /// Class SearchService + /// Class SearchService. /// </summary> [Authenticated] public class SearchService : BaseApiService { /// <summary> - /// The _search engine + /// The _search engine. /// </summary> private readonly ISearchEngine _searchEngine; private readonly ILibraryManager _libraryManager; @@ -180,7 +180,6 @@ namespace MediaBrowser.Api IsNews = request.IsNews, IsSeries = request.IsSeries, IsSports = request.IsSports - }); return new SearchHintResult diff --git a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs index 0e74c9267..2400d6def 100644 --- a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Sessions { /// <summary> - /// Class SessionInfoWebSocketListener + /// Class SessionInfoWebSocketListener. /// </summary> public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState> { @@ -19,7 +19,7 @@ namespace MediaBrowser.Api.Sessions protected override string Name => "Sessions"; /// <summary> - /// The _kernel + /// The _kernel. /// </summary> private readonly ISessionManager _sessionManager; @@ -40,39 +40,39 @@ namespace MediaBrowser.Api.Sessions _sessionManager.SessionActivity += OnSessionManagerSessionActivity; } - private void OnSessionManagerSessionActivity(object sender, SessionEventArgs e) + private async void OnSessionManagerSessionActivity(object sender, SessionEventArgs e) { - SendData(false); + await SendData(false).ConfigureAwait(false); } - private void OnSessionManagerCapabilitiesChanged(object sender, SessionEventArgs e) + private async void OnSessionManagerCapabilitiesChanged(object sender, SessionEventArgs e) { - SendData(true); + await SendData(true).ConfigureAwait(false); } - private void OnSessionManagerPlaybackProgress(object sender, PlaybackProgressEventArgs e) + private async void OnSessionManagerPlaybackProgress(object sender, PlaybackProgressEventArgs e) { - SendData(!e.IsAutomated); + await SendData(!e.IsAutomated).ConfigureAwait(false); } - private void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e) + private async void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e) { - SendData(true); + await SendData(true).ConfigureAwait(false); } - private void OnSessionManagerPlaybackStart(object sender, PlaybackProgressEventArgs e) + private async void OnSessionManagerPlaybackStart(object sender, PlaybackProgressEventArgs e) { - SendData(true); + await SendData(true).ConfigureAwait(false); } - private void OnSessionManagerSessionEnded(object sender, SessionEventArgs e) + private async void OnSessionManagerSessionEnded(object sender, SessionEventArgs e) { - SendData(true); + await SendData(true).ConfigureAwait(false); } - private void OnSessionManagerSessionStarted(object sender, SessionEventArgs e) + private async void OnSessionManagerSessionStarted(object sender, SessionEventArgs e) { - SendData(true); + await SendData(true).ConfigureAwait(false); } /// <summary> diff --git a/MediaBrowser.Api/Sessions/SessionService.cs b/MediaBrowser.Api/Sessions/SessionService.cs index 020bb5042..50adc5698 100644 --- a/MediaBrowser.Api/Sessions/SessionService.cs +++ b/MediaBrowser.Api/Sessions/SessionService.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; @@ -45,14 +46,14 @@ namespace MediaBrowser.Api.Sessions public string Id { get; set; } /// <summary> - /// Artist, Genre, Studio, Person, or any kind of BaseItem + /// Artist, Genre, Studio, Person, or any kind of BaseItem. /// </summary> /// <value>The type of the item.</value> [ApiMember(Name = "ItemType", Description = "The type of item to browse to.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string ItemType { get; set; } /// <summary> - /// Artist name, genre name, item Id, etc + /// Artist name, genre name, item Id, etc. /// </summary> /// <value>The item identifier.</value> [ApiMember(Name = "ItemId", Description = "The Id of the item.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] @@ -326,12 +327,12 @@ namespace MediaBrowser.Api.Sessions var user = _userManager.GetUserById(request.ControllableByUserId); - if (!user.Policy.EnableRemoteControlOfOtherUsers) + if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers)) { result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(request.ControllableByUserId)); } - if (!user.Policy.EnableSharedDeviceControl) + if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl)) { result = result.Where(i => !i.UserId.Equals(Guid.Empty)); } diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 44bb24ef2..84abf7b8d 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class BaseGetSimilarItemsFromItem + /// Class BaseGetSimilarItemsFromItem. /// </summary> public class BaseGetSimilarItemsFromItem : BaseGetSimilarItems { @@ -50,14 +50,14 @@ namespace MediaBrowser.Api public Guid UserId { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -65,11 +65,11 @@ namespace MediaBrowser.Api } /// <summary> - /// Class SimilarItemsHelper + /// Class SimilarItemsHelper. /// </summary> public static class SimilarItemsHelper { - internal static QueryResult<BaseItemDto> GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore) + internal static QueryResult<BaseItemDto> GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore) { var user = !request.UserId.Equals(Guid.Empty) ? userManager.GetUserById(request.UserId) : null; @@ -179,18 +179,22 @@ namespace MediaBrowser.Api { return 5; } + if (string.Equals(i.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) { return 3; } + if (string.Equals(i.Type, PersonType.Composer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Composer, StringComparison.OrdinalIgnoreCase)) { return 3; } + if (string.Equals(i.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) { return 3; } + if (string.Equals(i.Type, PersonType.Writer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) { return 2; @@ -218,6 +222,5 @@ namespace MediaBrowser.Api return points; } - } } diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index f2968c6b5..a70da8e56 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -97,6 +97,7 @@ namespace MediaBrowser.Api.Subtitles [ApiMember(Name = "CopyTimestamps", Description = "CopyTimestamps", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool CopyTimestamps { get; set; } + public bool AddVttTimeMap { get; set; } } @@ -214,6 +215,7 @@ namespace MediaBrowser.Api.Subtitles { request.Format = "json"; } + if (string.IsNullOrEmpty(request.Format)) { var item = (Video)_libraryManager.GetItemById(request.Id); diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs index 91f85db6f..17afa8e79 100644 --- a/MediaBrowser.Api/SuggestionsService.cs +++ b/MediaBrowser.Api/SuggestionsService.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -17,10 +18,15 @@ namespace MediaBrowser.Api public class GetSuggestedItems : IReturn<QueryResult<BaseItemDto>> { public string MediaType { get; set; } + public string Type { get; set; } + public Guid UserId { get; set; } + public bool EnableTotalRecordCount { get; set; } + public int? StartIndex { get; set; } + public int? Limit { get; set; } public string[] GetMediaTypes() diff --git a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs index 1e14ea552..daa1b521f 100644 --- a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs +++ b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs @@ -11,93 +11,66 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.SyncPlay { - [Route("/SyncPlay/{SessionId}/NewGroup", "POST", Summary = "Create a new SyncPlay group")] + [Route("/SyncPlay/New", "POST", Summary = "Create a new SyncPlay group")] [Authenticated] - public class SyncPlayNewGroup : IReturnVoid + public class SyncPlayNew : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")] + [Route("/SyncPlay/Join", "POST", Summary = "Join an existing SyncPlay group")] [Authenticated] - public class SyncPlayJoinGroup : IReturnVoid + public class SyncPlayJoin : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the Group id. /// </summary> /// <value>The Group id to join.</value> [ApiMember(Name = "GroupId", Description = "Group Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string GroupId { get; set; } - - /// <summary> - /// Gets or sets the playing item id. - /// </summary> - /// <value>The client's currently playing item id.</value> - [ApiMember(Name = "PlayingItemId", Description = "Client's playing item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string PlayingItemId { get; set; } } - [Route("/SyncPlay/{SessionId}/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")] + [Route("/SyncPlay/Leave", "POST", Summary = "Leave joined SyncPlay group")] [Authenticated] - public class SyncPlayLeaveGroup : IReturnVoid + public class SyncPlayLeave : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/ListGroups", "POST", Summary = "List SyncPlay groups")] + [Route("/SyncPlay/List", "GET", Summary = "List SyncPlay groups")] [Authenticated] - public class SyncPlayListGroups : IReturnVoid + public class SyncPlayList : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the filter item id. /// </summary> /// <value>The filter item id.</value> - [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string FilterItemId { get; set; } } - [Route("/SyncPlay/{SessionId}/PlayRequest", "POST", Summary = "Request play in SyncPlay group")] + [Route("/SyncPlay/Play", "POST", Summary = "Request play in SyncPlay group")] [Authenticated] - public class SyncPlayPlayRequest : IReturnVoid + public class SyncPlayPlay : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")] + [Route("/SyncPlay/Pause", "POST", Summary = "Request pause in SyncPlay group")] [Authenticated] - public class SyncPlayPauseRequest : IReturnVoid + public class SyncPlayPause : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")] + [Route("/SyncPlay/Seek", "POST", Summary = "Request seek in SyncPlay group")] [Authenticated] - public class SyncPlaySeekRequest : IReturnVoid + public class SyncPlaySeek : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")] public long PositionTicks { get; set; } } - [Route("/SyncPlay/{SessionId}/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")] + [Route("/SyncPlay/Buffering", "POST", Summary = "Request group wait in SyncPlay group while buffering")] [Authenticated] - public class SyncPlayBufferingRequest : IReturnVoid + public class SyncPlayBuffering : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the date used to pin PositionTicks in time. /// </summary> @@ -109,20 +82,17 @@ namespace MediaBrowser.Api.SyncPlay public long PositionTicks { get; set; } /// <summary> - /// Gets or sets whether this is a buffering or a buffering-done request. + /// Gets or sets whether this is a buffering or a ready request. /// </summary> /// <value><c>true</c> if buffering is complete; <c>false</c> otherwise.</value> [ApiMember(Name = "BufferingDone", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool BufferingDone { get; set; } } - [Route("/SyncPlay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")] + [Route("/SyncPlay/Ping", "POST", Summary = "Update session ping")] [Authenticated] - public class SyncPlayUpdatePing : IReturnVoid + public class SyncPlayPing : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - [ApiMember(Name = "Ping", IsRequired = true, DataType = "double", ParameterType = "query", Verb = "POST")] public double Ping { get; set; } } @@ -158,7 +128,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayNewGroup request) + public void Post(SyncPlayNew request) { var currentSession = GetSession(_sessionContext); _syncPlayManager.NewGroup(currentSession, CancellationToken.None); @@ -168,30 +138,20 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayJoinGroup request) + public void Post(SyncPlayJoin request) { var currentSession = GetSession(_sessionContext); Guid groupId; - Guid playingItemId = Guid.Empty; - if (!Guid.TryParse(request.GroupId, out groupId)) { Logger.LogError("JoinGroup: {0} is not a valid format for GroupId. Ignoring request.", request.GroupId); return; } - // Both null and empty strings mean that client isn't playing anything - if (!string.IsNullOrEmpty(request.PlayingItemId) && !Guid.TryParse(request.PlayingItemId, out playingItemId)) - { - Logger.LogError("JoinGroup: {0} is not a valid format for PlayingItemId. Ignoring request.", request.PlayingItemId); - return; - } - var joinRequest = new JoinGroupRequest() { - GroupId = groupId, - PlayingItemId = playingItemId + GroupId = groupId }; _syncPlayManager.JoinGroup(currentSession, groupId, joinRequest, CancellationToken.None); @@ -201,7 +161,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayLeaveGroup request) + public void Post(SyncPlayLeave request) { var currentSession = GetSession(_sessionContext); _syncPlayManager.LeaveGroup(currentSession, CancellationToken.None); @@ -212,7 +172,7 @@ namespace MediaBrowser.Api.SyncPlay /// </summary> /// <param name="request">The request.</param> /// <value>The requested list of groups.</value> - public List<GroupInfoView> Post(SyncPlayListGroups request) + public List<GroupInfoView> Get(SyncPlayList request) { var currentSession = GetSession(_sessionContext); var filterItemId = Guid.Empty; @@ -229,7 +189,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayPlayRequest request) + public void Post(SyncPlayPlay request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() @@ -243,7 +203,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayPauseRequest request) + public void Post(SyncPlayPause request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() @@ -257,7 +217,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlaySeekRequest request) + public void Post(SyncPlaySeek request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() @@ -272,12 +232,12 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayBufferingRequest request) + public void Post(SyncPlayBuffering request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() { - Type = request.BufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering, + Type = request.BufferingDone ? PlaybackRequestType.Ready : PlaybackRequestType.Buffer, When = DateTime.Parse(request.When), PositionTicks = request.PositionTicks }; @@ -288,12 +248,12 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayUpdatePing request) + public void Post(SyncPlayPing request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() { - Type = PlaybackRequestType.UpdatePing, + Type = PlaybackRequestType.Ping, Ping = Convert.ToInt64(request.Ping) }; _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs index a6bacad4f..7ca31c21a 100644 --- a/MediaBrowser.Api/System/ActivityLogService.cs +++ b/MediaBrowser.Api/System/ActivityLogService.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Api.System public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index 8e4860be4..39976371a 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.System { /// <summary> - /// Class SessionInfoWebSocketListener + /// Class SessionInfoWebSocketListener. /// </summary> public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<ActivityLogEntry[], WebSocketListenerState> { @@ -19,7 +19,7 @@ namespace MediaBrowser.Api.System protected override string Name => "ActivityLogEntry"; /// <summary> - /// The _kernel + /// The _kernel. /// </summary> private readonly IActivityManager _activityManager; diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index c57cc93d5..e0e20d828 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/MediaBrowser.Api/System/SystemService.cs @@ -18,30 +18,27 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.System { /// <summary> - /// Class GetSystemInfo + /// Class GetSystemInfo. /// </summary> [Route("/System/Info", "GET", Summary = "Gets information about the server")] [Authenticated(EscapeParentalControl = true, AllowBeforeStartupWizard = true)] public class GetSystemInfo : IReturn<SystemInfo> { - } [Route("/System/Info/Public", "GET", Summary = "Gets public information about the server")] public class GetPublicSystemInfo : IReturn<PublicSystemInfo> { - } [Route("/System/Ping", "POST")] [Route("/System/Ping", "GET")] public class PingSystem : IReturnVoid { - } /// <summary> - /// Class RestartApplication + /// Class RestartApplication. /// </summary> [Route("/System/Restart", "POST", Summary = "Restarts the application, if needed")] [Authenticated(Roles = "Admin", AllowLocal = true)] @@ -83,16 +80,15 @@ namespace MediaBrowser.Api.System [Authenticated] public class GetWakeOnLanInfo : IReturn<WakeOnLanInfo[]> { - } /// <summary> - /// Class SystemInfoService + /// Class SystemInfoService. /// </summary> public class SystemService : BaseApiService { /// <summary> - /// The _app host + /// The _app host. /// </summary> private readonly IServerApplicationHost _appHost; private readonly IApplicationPaths _appPaths; @@ -153,7 +149,6 @@ namespace MediaBrowser.Api.System DateModified = _fileSystem.GetLastWriteTimeUtc(i), Name = i.Name, Size = i.Length - }).OrderByDescending(i => i.DateModified) .ThenByDescending(i => i.DateCreated) .ThenBy(i => i.Name) diff --git a/MediaBrowser.Api/TranscodingJob.cs b/MediaBrowser.Api/TranscodingJob.cs index 8c24e3ce1..bfc311a27 100644 --- a/MediaBrowser.Api/TranscodingJob.cs +++ b/MediaBrowser.Api/TranscodingJob.cs @@ -32,6 +32,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The path.</value> public MediaSourceInfo MediaSource { get; set; } + public string Path { get; set; } /// <summary> /// Gets or sets the type. @@ -43,6 +44,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The process.</value> public Process Process { get; set; } + public ILogger Logger { get; private set; } /// <summary> /// Gets or sets the active request count. @@ -62,18 +64,23 @@ namespace MediaBrowser.Api public object ProcessLock = new object(); public bool HasExited { get; set; } + public bool IsUserPaused { get; set; } public string Id { get; set; } public float? Framerate { get; set; } + public double? CompletionPercentage { get; set; } public long? BytesDownloaded { get; set; } + public long? BytesTranscoded { get; set; } + public int? BitRate { get; set; } public long? TranscodingPositionTicks { get; set; } + public long? DownloadPositionTicks { get; set; } public TranscodingThrottler TranscodingThrottler { get; set; } @@ -81,6 +88,7 @@ namespace MediaBrowser.Api private readonly object _timerLock = new object(); public DateTime LastPingDate { get; set; } + public int PingTimeout { get; set; } public TranscodingJob(ILogger logger) diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index cd8e8dfbe..165abd613 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -19,7 +19,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class GetNextUpEpisodes + /// Class GetNextUpEpisodes. /// </summary> [Route("/Shows/NextUp", "GET", Summary = "Gets a list of next up episodes")] public class GetNextUpEpisodes : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions @@ -39,14 +39,14 @@ namespace MediaBrowser.Api public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -73,6 +73,7 @@ namespace MediaBrowser.Api [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableUserData { get; set; } + public bool EnableTotalRecordCount { get; set; } public GetNextUpEpisodes() @@ -99,14 +100,14 @@ namespace MediaBrowser.Api public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -143,7 +144,7 @@ namespace MediaBrowser.Api public Guid UserId { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -175,7 +176,7 @@ namespace MediaBrowser.Api public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -211,7 +212,7 @@ namespace MediaBrowser.Api public Guid UserId { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -243,18 +244,18 @@ namespace MediaBrowser.Api } /// <summary> - /// Class TvShowsService + /// Class TvShowsService. /// </summary> [Authenticated] public class TvShowsService : BaseApiService { /// <summary> - /// The _user manager + /// The _user manager. /// </summary> private readonly IUserManager _userManager; /// <summary> - /// The _library manager + /// The _library manager. /// </summary> private readonly ILibraryManager _libraryManager; @@ -306,7 +307,6 @@ namespace MediaBrowser.Api ParentId = parentIdGuid, Recursive = true, DtoOptions = options - }); var returnItems = _dtoService.GetBaseItemDtos(itemsResult, options, user); @@ -378,7 +378,7 @@ namespace MediaBrowser.Api { var user = _userManager.GetUserById(request.UserId); - var series = GetSeries(request.Id, user); + var series = GetSeries(request.Id); if (series == null) { @@ -390,7 +390,6 @@ namespace MediaBrowser.Api IsMissing = request.IsMissing, IsSpecialSeason = request.IsSpecialSeason, AdjacentTo = request.AdjacentTo - }); var dtoOptions = GetDtoOptions(_authContext, request); @@ -404,7 +403,7 @@ namespace MediaBrowser.Api }; } - private Series GetSeries(string seriesId, User user) + private Series GetSeries(string seriesId) { if (!string.IsNullOrWhiteSpace(seriesId)) { @@ -433,7 +432,7 @@ namespace MediaBrowser.Api } else if (request.Season.HasValue) { - var series = GetSeries(request.Id, user); + var series = GetSeries(request.Id); if (series == null) { @@ -446,7 +445,7 @@ namespace MediaBrowser.Api } else { - var series = GetSeries(request.Id, user); + var series = GetSeries(request.Id); if (series == null) { diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index bef91d54d..9875e0208 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class GetArtists + /// Class GetArtists. /// </summary> [Route("/Artists", "GET", Summary = "Gets all artists from a given item, folder, or the entire library")] public class GetArtists : GetItemsByName @@ -45,7 +45,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class ArtistsService + /// Class ArtistsService. /// </summary> [Authenticated] public class ArtistsService : BaseItemsByNameService<MusicArtist> diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index 559082ff4..fd639caf1 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -14,7 +15,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class BaseItemsByNameService + /// Class BaseItemsByNameService. /// </summary> /// <typeparam name="TItemType">The type of the T item type.</typeparam> public abstract class BaseItemsByNameService<TItemType> : BaseApiService @@ -51,7 +52,7 @@ namespace MediaBrowser.Api.UserLibrary protected IUserManager UserManager { get; } /// <summary> - /// Gets the library manager + /// Gets the library manager. /// </summary> protected ILibraryManager LibraryManager { get; } @@ -209,6 +210,7 @@ namespace MediaBrowser.Api.UserLibrary { SetItemCounts(dto, i.Item2); } + return dto; }); @@ -321,7 +323,6 @@ namespace MediaBrowser.Api.UserLibrary { ibnItems = ibnItems.Take(request.Limit.Value); } - } var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>())); @@ -375,7 +376,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetItemsByName + /// Class GetItemsByName. /// </summary> public class GetItemsByName : BaseItemsRequest, IReturn<QueryResult<BaseItemDto>> { diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 7561b5c89..344861a49 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -111,14 +111,14 @@ namespace MediaBrowser.Api.UserLibrary public int? StartIndex { get; set; } /// <summary> - /// The maximum number of items to return + /// The maximum number of items to return. /// </summary> /// <value>The limit.</value> [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } /// <summary> - /// Whether or not to perform the query recursively + /// Whether or not to perform the query recursively. /// </summary> /// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value> [ApiMember(Name = "Recursive", Description = "When searching within folders, this determines whether or not the search will be recursive. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] @@ -141,7 +141,7 @@ namespace MediaBrowser.Api.UserLibrary public string ParentId { get; set; } /// <summary> - /// Fields to return within the items, in addition to basic information + /// Fields to return within the items, in addition to basic information. /// </summary> /// <value>The fields.</value> [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -162,14 +162,14 @@ namespace MediaBrowser.Api.UserLibrary public string IncludeItemTypes { get; set; } /// <summary> - /// Filters to apply to the results + /// Filters to apply to the results. /// </summary> /// <value>The filters.</value> [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Filters { get; set; } /// <summary> - /// Gets or sets the Isfavorite option + /// Gets or sets the Isfavorite option. /// </summary> /// <value>IsFavorite</value> [ApiMember(Name = "IsFavorite", Description = "Optional filter by items that are marked as favorite, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] @@ -190,7 +190,7 @@ namespace MediaBrowser.Api.UserLibrary public string ImageTypes { get; set; } /// <summary> - /// What to sort the results by + /// What to sort the results by. /// </summary> /// <value>The sort by.</value> [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -200,7 +200,7 @@ namespace MediaBrowser.Api.UserLibrary public bool? IsPlayed { get; set; } /// <summary> - /// Limit results to items containing specific genres + /// Limit results to items containing specific genres. /// </summary> /// <value>The genres.</value> [ApiMember(Name = "Genres", Description = "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -215,7 +215,7 @@ namespace MediaBrowser.Api.UserLibrary public string Tags { get; set; } /// <summary> - /// Limit results to items containing specific years + /// Limit results to items containing specific years. /// </summary> /// <value>The years.</value> [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)] @@ -234,7 +234,7 @@ namespace MediaBrowser.Api.UserLibrary public string EnableImageTypes { get; set; } /// <summary> - /// Limit results to items containing a specific person + /// Limit results to items containing a specific person. /// </summary> /// <value>The person.</value> [ApiMember(Name = "Person", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -244,14 +244,14 @@ namespace MediaBrowser.Api.UserLibrary public string PersonIds { get; set; } /// <summary> - /// If the Person filter is used, this can also be used to restrict to a specific person type + /// If the Person filter is used, this can also be used to restrict to a specific person type. /// </summary> /// <value>The type of the person.</value> [ApiMember(Name = "PersonTypes", Description = "Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string PersonTypes { get; set; } /// <summary> - /// Limit results to items containing specific studios + /// Limit results to items containing specific studios. /// </summary> /// <value>The studios.</value> [ApiMember(Name = "Studios", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] @@ -322,8 +322,11 @@ namespace MediaBrowser.Api.UserLibrary public bool? CollapseBoxSetItems { get; set; } public int? MinWidth { get; set; } + public int? MinHeight { get; set; } + public int? MaxWidth { get; set; } + public int? MaxHeight { get; set; } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index 1fa272a5f..7bdfbac98 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class GetGenres + /// Class GetGenres. /// </summary> [Route("/Genres", "GET", Summary = "Gets all genres from a given item, folder, or the entire library")] public class GetGenres : GetItemsByName @@ -22,7 +22,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetGenre + /// Class GetGenre. /// </summary> [Route("/Genres/{Name}", "GET", Summary = "Gets a genre, by name")] public class GetGenre : IReturn<BaseItemDto> @@ -43,7 +43,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GenresService + /// Class GenresService. /// </summary> [Authenticated] public class GenresService : BaseItemsByNameService<Genre> diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index f3c0441e1..7efe0552c 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -2,10 +2,11 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dto; @@ -14,11 +15,12 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class GetItems + /// Class GetItems. /// </summary> [Route("/Items", "GET", Summary = "Gets items based on a query.")] [Route("/Users/{UserId}/Items", "GET", Summary = "Gets items based on a query.")] @@ -32,18 +34,18 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class ItemsService + /// Class ItemsService. /// </summary> [Authenticated] public class ItemsService : BaseApiService { /// <summary> - /// The _user manager + /// The _user manager. /// </summary> private readonly IUserManager _userManager; /// <summary> - /// The _library manager + /// The _library manager. /// </summary> private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; @@ -86,7 +88,7 @@ namespace MediaBrowser.Api.UserLibrary var ancestorIds = Array.Empty<Guid>(); - var excludeFolderIds = user.Configuration.LatestItemsExcludes; + var excludeFolderIds = user.GetPreference(PreferenceKind.LatestItemExcludes); if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0) { ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) @@ -211,14 +213,14 @@ namespace MediaBrowser.Api.UserLibrary request.IncludeItemTypes = "Playlist"; } - bool isInEnabledFolder = user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id) + bool isInEnabledFolder = user.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id) // Assume all folders inside an EnabledChannel are enabled - || user.Policy.EnabledChannels.Any(i => new Guid(i) == item.Id); + || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id); var collectionFolders = _libraryManager.GetCollectionFolders(item); foreach (var collectionFolder in collectionFolders) { - if (user.Policy.EnabledFolders.Contains( + if (user.GetPreference(PreferenceKind.EnabledFolders).Contains( collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) { @@ -226,9 +228,12 @@ namespace MediaBrowser.Api.UserLibrary } } - if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !isInEnabledFolder && !user.Policy.EnableAllChannels) + if (!(item is UserRootFolder) + && !isInEnabledFolder + && !user.HasPermission(PermissionKind.EnableAllFolders) + && !user.HasPermission(PermissionKind.EnableAllChannels)) { - Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name); + Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name); return new QueryResult<BaseItem> { Items = Array.Empty<BaseItem>(), @@ -491,7 +496,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class DateCreatedComparer + /// Class DateCreatedComparer. /// </summary> public class DateCreatedComparer : IComparer<BaseItem> { diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index 3204e5219..7924339ed 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class GetPersons + /// Class GetPersons. /// </summary> [Route("/Persons", "GET", Summary = "Gets all persons from a given item, folder, or the entire library")] public class GetPersons : GetItemsByName @@ -22,7 +22,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetPerson + /// Class GetPerson. /// </summary> [Route("/Persons/{Name}", "GET", Summary = "Gets a person, by name")] public class GetPerson : IReturn<BaseItemDto> @@ -43,7 +43,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class PersonsService + /// Class PersonsService. /// </summary> [Authenticated] public class PersonsService : BaseItemsByNameService<Person> diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs index d0faca163..d809cc2e7 100644 --- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs +++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs @@ -1,8 +1,8 @@ using System; using System.Globalization; using System.Threading.Tasks; +using Jellyfin.Data.Entities; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; @@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class MarkPlayedItem + /// Class MarkPlayedItem. /// </summary> [Route("/Users/{UserId}/PlayedItems/{Id}", "POST", Summary = "Marks an item as played")] public class MarkPlayedItem : IReturn<UserItemDataDto> @@ -38,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class MarkUnplayedItem + /// Class MarkUnplayedItem. /// </summary> [Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE", Summary = "Marks an item as unplayed")] public class MarkUnplayedItem : IReturn<UserItemDataDto> @@ -81,7 +81,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class OnPlaybackStart + /// Class OnPlaybackStart. /// </summary> [Route("/Users/{UserId}/PlayingItems/{Id}", "POST", Summary = "Reports that a user has begun playing an item")] public class OnPlaybackStart : IReturnVoid @@ -123,7 +123,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class OnPlaybackProgress + /// Class OnPlaybackProgress. /// </summary> [Route("/Users/{UserId}/PlayingItems/{Id}/Progress", "POST", Summary = "Reports a user's playback progress")] public class OnPlaybackProgress : IReturnVoid @@ -181,7 +181,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class OnPlaybackStopped + /// Class OnPlaybackStopped. /// </summary> [Route("/Users/{UserId}/PlayingItems/{Id}", "DELETE", Summary = "Reports that a user has stopped playing an item")] public class OnPlaybackStopped : IReturnVoid diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index 683ce5d09..66350955f 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class GetStudios + /// Class GetStudios. /// </summary> [Route("/Studios", "GET", Summary = "Gets all studios from a given item, folder, or the entire library")] public class GetStudios : GetItemsByName @@ -21,7 +21,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetStudio + /// Class GetStudio. /// </summary> [Route("/Studios/{Name}", "GET", Summary = "Gets a studio, by name")] public class GetStudio : IReturn<BaseItemDto> @@ -42,7 +42,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class StudiosService + /// Class StudiosService. /// </summary> [Authenticated] public class StudiosService : BaseItemsByNameService<Studio> diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 7fa750adb..f9cbba410 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class GetItem + /// Class GetItem. /// </summary> [Route("/Users/{UserId}/Items/{Id}", "GET", Summary = "Gets an item from a user's library")] public class GetItem : IReturn<BaseItemDto> @@ -41,7 +41,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetItem + /// Class GetItem. /// </summary> [Route("/Users/{UserId}/Items/Root", "GET", Summary = "Gets the root folder from a user's library")] public class GetRootFolder : IReturn<BaseItemDto> @@ -55,7 +55,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetIntros + /// Class GetIntros. /// </summary> [Route("/Users/{UserId}/Items/{Id}/Intros", "GET", Summary = "Gets intros to play before the main media item plays")] public class GetIntros : IReturn<QueryResult<BaseItemDto>> @@ -76,7 +76,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class MarkFavoriteItem + /// Class MarkFavoriteItem. /// </summary> [Route("/Users/{UserId}/FavoriteItems/{Id}", "POST", Summary = "Marks an item as a favorite")] public class MarkFavoriteItem : IReturn<UserItemDataDto> @@ -97,7 +97,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class UnmarkFavoriteItem + /// Class UnmarkFavoriteItem. /// </summary> [Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE", Summary = "Unmarks an item as a favorite")] public class UnmarkFavoriteItem : IReturn<UserItemDataDto> @@ -118,7 +118,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class ClearUserItemRating + /// Class ClearUserItemRating. /// </summary> [Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE", Summary = "Deletes a user's saved personal rating for an item")] public class DeleteUserItemRating : IReturn<UserItemDataDto> @@ -139,7 +139,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class UpdateUserItemRating + /// Class UpdateUserItemRating. /// </summary> [Route("/Users/{UserId}/Items/{Id}/Rating", "POST", Summary = "Updates a user's rating for an item")] public class UpdateUserItemRating : IReturn<UserItemDataDto> @@ -167,7 +167,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetLocalTrailers + /// Class GetLocalTrailers. /// </summary> [Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET", Summary = "Gets local trailers for an item")] public class GetLocalTrailers : IReturn<BaseItemDto[]> @@ -188,7 +188,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetSpecialFeatures + /// Class GetSpecialFeatures. /// </summary> [Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET", Summary = "Gets special features for an item")] public class GetSpecialFeatures : IReturn<BaseItemDto[]> @@ -259,7 +259,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class UserLibraryService + /// Class UserLibraryService. /// </summary> [Authenticated] public class UserLibraryService : BaseApiService @@ -312,7 +312,7 @@ namespace MediaBrowser.Api.UserLibrary if (!request.IsPlayed.HasValue) { - if (user.Configuration.HidePlayedInLatest) + if (user.HidePlayedInLatest) { request.IsPlayed = false; } diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index 0fffb0622..6f1620ddd 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -27,6 +27,7 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? IncludeExternalContent { get; set; } + public bool IncludeHidden { get; set; } public string PresetViews { get; set; } @@ -80,6 +81,7 @@ namespace MediaBrowser.Api.UserLibrary { query.IncludeExternalContent = request.IncludeExternalContent.Value; } + query.IncludeHidden = request.IncludeHidden; if (!string.IsNullOrWhiteSpace(request.PresetViews)) @@ -129,7 +131,6 @@ namespace MediaBrowser.Api.UserLibrary { Name = i.Name, Id = i.Id.ToString("N", CultureInfo.InvariantCulture) - }) .OrderBy(i => i.Name) .ToArray(); @@ -141,6 +142,7 @@ namespace MediaBrowser.Api.UserLibrary class SpecialViewOption { public string Name { get; set; } + public string Id { get; set; } } } diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs index d023ee90a..0523f89fa 100644 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ b/MediaBrowser.Api/UserLibrary/YearsService.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.UserLibrary { /// <summary> - /// Class GetYears + /// Class GetYears. /// </summary> [Route("/Years", "GET", Summary = "Gets all years from a given item, folder, or the entire library")] public class GetYears : GetItemsByName @@ -21,7 +21,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class GetYear + /// Class GetYear. /// </summary> [Route("/Years/{Year}", "GET", Summary = "Gets a year")] public class GetYear : IReturn<BaseItemDto> @@ -42,7 +42,7 @@ namespace MediaBrowser.Api.UserLibrary } /// <summary> - /// Class YearsService + /// Class YearsService. /// </summary> [Authenticated] public class YearsService : BaseItemsByNameService<Year> diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 78fc6c694..6e9d788dc 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; @@ -18,7 +19,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class GetUsers + /// Class GetUsers. /// </summary> [Route("/Users", "GET", Summary = "Gets a list of users")] [Authenticated] @@ -40,7 +41,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class GetUser + /// Class GetUser. /// </summary> [Route("/Users/{Id}", "GET", Summary = "Gets a user by Id")] [Authenticated(EscapeParentalControl = true)] @@ -55,7 +56,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class DeleteUser + /// Class DeleteUser. /// </summary> [Route("/Users/{Id}", "DELETE", Summary = "Deletes a user")] [Authenticated(Roles = "Admin")] @@ -70,7 +71,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class AuthenticateUser + /// Class AuthenticateUser. /// </summary> [Route("/Users/{Id}/Authenticate", "POST", Summary = "Authenticates a user")] public class AuthenticateUser : IReturn<AuthenticationResult> @@ -94,7 +95,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class AuthenticateUser + /// Class AuthenticateUser. /// </summary> [Route("/Users/AuthenticateByName", "POST", Summary = "Authenticates a user")] public class AuthenticateUserByName : IReturn<AuthenticationResult> @@ -118,7 +119,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UpdateUserPassword + /// Class UpdateUserPassword. /// </summary> [Route("/Users/{Id}/Password", "POST", Summary = "Updates a user's password")] [Authenticated] @@ -148,7 +149,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UpdateUserEasyPassword + /// Class UpdateUserEasyPassword. /// </summary> [Route("/Users/{Id}/EasyPassword", "POST", Summary = "Updates a user's easy password")] [Authenticated] @@ -176,7 +177,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UpdateUser + /// Class UpdateUser. /// </summary> [Route("/Users/{Id}", "POST", Summary = "Updates a user")] [Authenticated] @@ -185,7 +186,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UpdateUser + /// Class UpdateUser. /// </summary> [Route("/Users/{Id}/Policy", "POST", Summary = "Updates a user policy")] [Authenticated(Roles = "admin")] @@ -196,7 +197,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UpdateUser + /// Class UpdateUser. /// </summary> [Route("/Users/{Id}/Configuration", "POST", Summary = "Updates a user configuration")] [Authenticated] @@ -207,7 +208,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class CreateUser + /// Class CreateUser. /// </summary> [Route("/Users/New", "POST", Summary = "Creates a user")] [Authenticated(Roles = "Admin")] @@ -235,7 +236,7 @@ namespace MediaBrowser.Api } /// <summary> - /// Class UsersService + /// Class UsersService. /// </summary> public class UserService : BaseApiService { @@ -300,12 +301,12 @@ namespace MediaBrowser.Api if (request.IsDisabled.HasValue) { - users = users.Where(i => i.Policy.IsDisabled == request.IsDisabled.Value); + users = users.Where(i => i.HasPermission(PermissionKind.IsDisabled) == request.IsDisabled.Value); } if (request.IsHidden.HasValue) { - users = users.Where(i => i.Policy.IsHidden == request.IsHidden.Value); + users = users.Where(i => i.HasPermission(PermissionKind.IsHidden) == request.IsHidden.Value); } if (filterByDevice) @@ -322,12 +323,12 @@ namespace MediaBrowser.Api { if (!_networkManager.IsInLocalNetwork(Request.RemoteIp)) { - users = users.Where(i => i.Policy.EnableRemoteAccess); + users = users.Where(i => i.HasPermission(PermissionKind.EnableRemoteAccess)); } } var result = users - .OrderBy(u => u.Name) + .OrderBy(u => u.Username) .Select(i => _userManager.GetUserDto(i, Request.RemoteIp)) .ToArray(); @@ -364,15 +365,8 @@ namespace MediaBrowser.Api public Task DeleteAsync(DeleteUser request) { - var user = _userManager.GetUserById(request.Id); - - if (user == null) - { - throw new ResourceNotFoundException("User not found"); - } - - _sessionMananger.RevokeUserTokens(user.Id, null); - _userManager.DeleteUser(user); + _userManager.DeleteUser(request.Id); + _sessionMananger.RevokeUserTokens(request.Id, null); return Task.CompletedTask; } @@ -397,7 +391,7 @@ namespace MediaBrowser.Api // Password should always be null return Post(new AuthenticateUserByName { - Username = user.Name, + Username = user.Username, Password = null, Pw = request.Pw }); @@ -456,7 +450,12 @@ namespace MediaBrowser.Api } else { - var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, Request.RemoteIp, false).ConfigureAwait(false); + var success = await _userManager.AuthenticateUser( + user.Username, + request.CurrentPw, + request.CurrentPassword, + Request.RemoteIp, + false).ConfigureAwait(false); if (success == null) { @@ -506,10 +505,10 @@ namespace MediaBrowser.Api var user = _userManager.GetUserById(id); - if (string.Equals(user.Name, dtoUser.Name, StringComparison.Ordinal)) + if (string.Equals(user.Username, dtoUser.Name, StringComparison.Ordinal)) { - _userManager.UpdateUser(user); - _userManager.UpdateConfiguration(user, dtoUser.Configuration); + await _userManager.UpdateUserAsync(user); + _userManager.UpdateConfiguration(user.Id, dtoUser.Configuration); } else { @@ -560,7 +559,6 @@ namespace MediaBrowser.Api AssertCanUpdateUser(_authContext, _userManager, request.Id, false); _userManager.UpdateConfiguration(request.Id, request); - } public void Post(UpdateUserPolicy request) @@ -568,24 +566,24 @@ namespace MediaBrowser.Api var user = _userManager.GetUserById(request.Id); // If removing admin access - if (!request.IsAdministrator && user.Policy.IsAdministrator) + if (!request.IsAdministrator && user.HasPermission(PermissionKind.IsAdministrator)) { - if (_userManager.Users.Count(i => i.Policy.IsAdministrator) == 1) + if (_userManager.Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1) { throw new ArgumentException("There must be at least one user in the system with administrative access."); } } // If disabling - if (request.IsDisabled && user.Policy.IsAdministrator) + if (request.IsDisabled && user.HasPermission(PermissionKind.IsAdministrator)) { throw new ArgumentException("Administrators cannot be disabled."); } // If disabling - if (request.IsDisabled && !user.Policy.IsDisabled) + if (request.IsDisabled && !user.HasPermission(PermissionKind.IsDisabled)) { - if (_userManager.Users.Count(i => !i.Policy.IsDisabled) == 1) + if (_userManager.Users.Count(i => !i.HasPermission(PermissionKind.IsDisabled)) == 1) { throw new ArgumentException("There must be at least one enabled user in the system."); } @@ -594,7 +592,7 @@ namespace MediaBrowser.Api _sessionMananger.RevokeUserTokens(user.Id, currentToken); } - _userManager.UpdateUserPolicy(request.Id, request); + _userManager.UpdatePolicy(request.Id, request); } } } |
