diff options
| author | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
|---|---|---|
| committer | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
| commit | 48facb797ed912e4ea6b04b17d1ff190ac2daac4 (patch) | |
| tree | 8dae77a31670a888d733484cb17dd4077d5444e8 /MediaBrowser.Api | |
| parent | c32d8656382a0eacb301692e0084377fc433ae9b (diff) | |
Update to 3.5.2 and .net core 2.1
Diffstat (limited to 'MediaBrowser.Api')
62 files changed, 1512 insertions, 2560 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 04cef60bfc..9676a2c2af 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -70,7 +70,7 @@ namespace MediaBrowser.Api { if (string.IsNullOrWhiteSpace(value)) { - return new string[] { }; + return Array.Empty<string>(); } if (removeEmpty) @@ -88,7 +88,6 @@ namespace MediaBrowser.Api public void Dispose() { - GC.SuppressFinalize(this); } } } diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 1629d49b49..db6af0c4bd 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -55,7 +55,7 @@ namespace MediaBrowser.Api return Request.Headers[name]; } - private static readonly string[] EmptyStringArray = new string[] { }; + private static readonly string[] EmptyStringArray = Array.Empty<string>(); public static string[] SplitValue(string value, char delim) { if (string.IsNullOrWhiteSpace(value)) @@ -65,7 +65,13 @@ namespace MediaBrowser.Api return value.Split(new[] { delim }, StringSplitOptions.RemoveEmptyEntries); } - + + public static Guid[] GetGuids(string value) + { + // Unfortunately filtermenu.js was using |. This can be deprecated after a while. + return (value ?? string.Empty).Split(new[] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries).Select(i => new Guid(i)).ToArray(); + } + /// <summary> /// To the optimized result. /// </summary> @@ -75,17 +81,17 @@ namespace MediaBrowser.Api protected object ToOptimizedResult<T>(T result) where T : class { - return ResultFactory.GetOptimizedResult(Request, result); + return ResultFactory.GetResult(Request, result); } - protected void AssertCanUpdateUser(IAuthorizationContext authContext, IUserManager userManager, string userId, bool restrictUserPreferences) + protected void AssertCanUpdateUser(IAuthorizationContext authContext, IUserManager userManager, Guid userId, bool restrictUserPreferences) { var auth = authContext.GetAuthorizationInfo(Request); - var authenticatedUser = userManager.GetUserById(auth.UserId); + var authenticatedUser = auth.User; // If they're going to update the record of another user, they must be an administrator - if (!string.Equals(userId, auth.UserId, StringComparison.OrdinalIgnoreCase)) + if (!userId.Equals(auth.UserId)) { if (!authenticatedUser.Policy.IsAdministrator) { @@ -102,24 +108,12 @@ namespace MediaBrowser.Api } /// <summary> - /// To the optimized serialized result using cache. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="result">The result.</param> - /// <returns>System.Object.</returns> - protected object ToOptimizedSerializedResultUsingCache<T>(T result) - where T : class - { - return ToOptimizedResult(result); - } - - /// <summary> /// Gets the session. /// </summary> /// <returns>SessionInfo.</returns> - protected async Task<SessionInfo> GetSession(ISessionContext sessionContext) + protected SessionInfo GetSession(ISessionContext sessionContext) { - var session = await sessionContext.GetSession(Request).ConfigureAwait(false); + var session = sessionContext.GetSession(Request); if (session == null) { @@ -133,38 +127,37 @@ namespace MediaBrowser.Api { var options = new DtoOptions(); - var authInfo = authContext.GetAuthorizationInfo(Request); - - options.DeviceId = authInfo.DeviceId; - var hasFields = request as IHasItemFields; if (hasFields != null) { options.Fields = hasFields.GetItemFields(); } - var client = authInfo.Client ?? string.Empty; - if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1) + if (!options.ContainsField(Model.Querying.ItemFields.RecursiveItemCount) || !options.ContainsField(Model.Querying.ItemFields.ChildCount)) { - var list = options.Fields.ToList(); - list.Add(Model.Querying.ItemFields.RecursiveItemCount); - options.Fields = list.ToArray(list.Count); - } + var client = authContext.GetAuthorizationInfo(Request).Client ?? string.Empty; + if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1) + { + var list = options.Fields.ToList(); + list.Add(Model.Querying.ItemFields.RecursiveItemCount); + options.Fields = list.ToArray(list.Count); + } - if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1) - { - var list = options.Fields.ToList(); - list.Add(Model.Querying.ItemFields.ChildCount); - options.Fields = list.ToArray(list.Count); + if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("wmc", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1) + { + var list = options.Fields.ToList(); + list.Add(Model.Querying.ItemFields.ChildCount); + options.Fields = list.ToArray(list.Count); + } } var hasDtoOptions = request as IHasDtoOptions; @@ -289,7 +282,7 @@ namespace MediaBrowser.Api IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<Person>().FirstOrDefault(); + }).OfType<T>().FirstOrDefault(); if (result == null) { @@ -299,7 +292,7 @@ namespace MediaBrowser.Api IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<Person>().FirstOrDefault(); + }).OfType<T>().FirstOrDefault(); } if (result == null) @@ -310,10 +303,10 @@ namespace MediaBrowser.Api IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<Person>().FirstOrDefault(); + }).OfType<T>().FirstOrDefault(); } - return result as T; + return result; } protected string GetPathValue(int index) @@ -331,7 +324,7 @@ namespace MediaBrowser.Api return pathInfo[index]; } - private static List<string> Parse(string pathUri) + private List<string> Parse(string pathUri) { var actionParts = pathUri.Split(new[] { "://" }, StringSplitOptions.None); diff --git a/MediaBrowser.Api/BrandingService.cs b/MediaBrowser.Api/BrandingService.cs index 4602f56ed9..4eb69678a3 100644 --- a/MediaBrowser.Api/BrandingService.cs +++ b/MediaBrowser.Api/BrandingService.cs @@ -26,17 +26,15 @@ namespace MediaBrowser.Api public object Get(GetBrandingOptions request) { - var result = _config.GetConfiguration<BrandingOptions>("branding"); - - return ToOptimizedResult(result); + return _config.GetConfiguration<BrandingOptions>("branding"); } public object Get(GetBrandingCss request) { var result = _config.GetConfiguration<BrandingOptions>("branding"); - // When null this throws a 405 error under Mono OSX, so default to empty string - return ResultFactory.GetResult(result.CustomCss ?? string.Empty, "text/css"); + // When null this throws a 405 error under Mono OSX, so default to empty string + return ResultFactory.GetResult(Request, result.CustomCss ?? string.Empty, "text/css"); } } } diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs index 2e8eb9e07d..0b9d5efa9f 100644 --- a/MediaBrowser.Api/ChannelService.cs +++ b/MediaBrowser.Api/ChannelService.cs @@ -11,6 +11,8 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Api.UserLibrary; using MediaBrowser.Model.Services; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Api { @@ -21,8 +23,8 @@ namespace MediaBrowser.Api /// Gets or sets the user id. /// </summary> /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid UserId { get; set; } /// <summary> /// Skips over a given number of items within the results. Use for paging. @@ -41,6 +43,8 @@ namespace MediaBrowser.Api [ApiMember(Name = "SupportsLatestItems", Description = "Optional. Filter by channels that support getting latest items.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? SupportsLatestItems { get; set; } + public bool? SupportsMediaDeletion { get; set; } + /// <summary> /// Gets or sets a value indicating whether this instance is favorite. /// </summary> @@ -74,7 +78,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Skips over a given number of items within the results. Use for paging. @@ -122,7 +126,7 @@ namespace MediaBrowser.Api /// Gets the order by. /// </summary> /// <returns>IEnumerable{ItemSortBy}.</returns> - public Tuple<string, SortOrder>[] GetOrderBy() + public ValueTuple<string, SortOrder>[] GetOrderBy() { return BaseItemsRequest.GetOrderBy(SortBy, SortOrder); } @@ -136,7 +140,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Skips over a given number of items within the results. Use for paging. @@ -178,21 +182,16 @@ namespace MediaBrowser.Api } } - [Route("/Channels/Folder", "GET", Summary = "Gets the users channel folder, along with configured images")] - public class GetChannelFolder : IReturn<BaseItemDto> - { - [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } - } - [Authenticated] public class ChannelService : BaseApiService { private readonly IChannelManager _channelManager; + private IUserManager _userManager; - public ChannelService(IChannelManager channelManager) + public ChannelService(IChannelManager channelManager, IUserManager userManager) { _channelManager = channelManager; + _userManager = userManager; } public object Get(GetAllChannelFeatures request) @@ -209,56 +208,132 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } - public object Get(GetChannelFolder request) + public object Get(GetChannels request) { - return ToOptimizedResult(_channelManager.GetChannelFolder(request.UserId, CancellationToken.None)); - } - - public async Task<object> Get(GetChannels request) - { - var result = await _channelManager.GetChannels(new ChannelQuery + var result = _channelManager.GetChannels(new ChannelQuery { Limit = request.Limit, StartIndex = request.StartIndex, UserId = request.UserId, SupportsLatestItems = request.SupportsLatestItems, + SupportsMediaDeletion = request.SupportsMediaDeletion, IsFavorite = request.IsFavorite - - }, CancellationToken.None).ConfigureAwait(false); + }); return ToOptimizedResult(result); } public async Task<object> Get(GetChannelItems request) { - var result = await _channelManager.GetChannelItems(new ChannelItemQuery + var user = request.UserId.Equals(Guid.Empty) + ? null + : _userManager.GetUserById(request.UserId); + + var query = new InternalItemsQuery(user) { Limit = request.Limit, StartIndex = request.StartIndex, - UserId = request.UserId, - ChannelId = request.Id, - FolderId = request.FolderId, + ChannelIds = new Guid[] { new Guid(request.Id) }, + ParentId = string.IsNullOrWhiteSpace(request.FolderId) ? Guid.Empty : new Guid(request.FolderId), OrderBy = request.GetOrderBy(), - Filters = request.GetFilters().ToArray(), - Fields = request.GetItemFields() + DtoOptions = new Controller.Dto.DtoOptions + { + Fields = request.GetItemFields() + } + + }; + + foreach (var filter in request.GetFilters()) + { + switch (filter) + { + case ItemFilter.Dislikes: + query.IsLiked = false; + break; + case ItemFilter.IsFavorite: + query.IsFavorite = true; + break; + case ItemFilter.IsFavoriteOrLikes: + query.IsFavoriteOrLiked = true; + break; + case ItemFilter.IsFolder: + query.IsFolder = true; + break; + case ItemFilter.IsNotFolder: + query.IsFolder = false; + break; + case ItemFilter.IsPlayed: + query.IsPlayed = true; + break; + case ItemFilter.IsResumable: + query.IsResumable = true; + break; + case ItemFilter.IsUnplayed: + query.IsPlayed = false; + break; + case ItemFilter.Likes: + query.IsLiked = true; + break; + } + } - }, CancellationToken.None).ConfigureAwait(false); + var result = await _channelManager.GetChannelItems(query, CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); } public async Task<object> Get(GetLatestChannelItems request) { - var result = await _channelManager.GetLatestChannelItems(new AllChannelMediaQuery + var user = request.UserId.Equals(Guid.Empty) + ? null + : _userManager.GetUserById(request.UserId); + + var query = new InternalItemsQuery(user) { Limit = request.Limit, StartIndex = request.StartIndex, - ChannelIds = (request.ChannelIds ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(), - UserId = request.UserId, - Filters = request.GetFilters().ToArray(), - Fields = request.GetItemFields() + ChannelIds = (request.ChannelIds ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToArray(), + DtoOptions = new Controller.Dto.DtoOptions + { + Fields = request.GetItemFields() + } + }; + + foreach (var filter in request.GetFilters()) + { + switch (filter) + { + case ItemFilter.Dislikes: + query.IsLiked = false; + break; + case ItemFilter.IsFavorite: + query.IsFavorite = true; + break; + case ItemFilter.IsFavoriteOrLikes: + query.IsFavoriteOrLiked = true; + break; + case ItemFilter.IsFolder: + query.IsFolder = true; + break; + case ItemFilter.IsNotFolder: + query.IsFolder = false; + break; + case ItemFilter.IsPlayed: + query.IsPlayed = true; + break; + case ItemFilter.IsResumable: + query.IsResumable = true; + break; + case ItemFilter.IsUnplayed: + query.IsPlayed = false; + break; + case ItemFilter.Likes: + query.IsLiked = true; + break; + } + } - }, CancellationToken.None).ConfigureAwait(false); + var result = await _channelManager.GetLatestChannelItems(query, CancellationToken.None).ConfigureAwait(false); return ToOptimizedResult(result); } diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs index 0023c13d78..9d14558e3b 100644 --- a/MediaBrowser.Api/ConfigurationService.cs +++ b/MediaBrowser.Api/ConfigurationService.cs @@ -4,14 +4,11 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Serialization; -using System.Collections.Generic; using System.IO; -using System.Threading.Tasks; - -using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Services; +using System.Threading.Tasks; namespace MediaBrowser.Api { @@ -59,27 +56,13 @@ namespace MediaBrowser.Api } - [Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")] - [Authenticated(Roles = "Admin")] - public class GetMetadataPlugins : IReturn<MetadataPluginSummary[]> - { - - } - - [Route("/System/Configuration/MetadataPlugins/Autoset", "POST")] - [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)] - public class AutoSetMetadataOptions : IReturnVoid - { - - } - [Route("/System/MediaEncoder/Path", "POST", Summary = "Updates the path to the media encoder")] [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)] public class UpdateMediaEncoderPath : IReturnVoid { - [ApiMember(Name = "Path", Description = "Path", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "Path", Description = "Path", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Path { get; set; } - [ApiMember(Name = "PathType", Description = "PathType", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "PathType", Description = "PathType", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string PathType { get; set; } } @@ -132,10 +115,6 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } - public void Post(AutoSetMetadataOptions request) - { - } - /// <summary> /// Posts the specified configuraiton. /// </summary> @@ -150,24 +129,19 @@ namespace MediaBrowser.Api _configurationManager.ReplaceConfiguration(config); } - public void Post(UpdateNamedConfiguration request) + public async Task Post(UpdateNamedConfiguration request) { var key = GetPathValue(2); var configurationType = _configurationManager.GetConfigurationType(key); - var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType); + var configuration = await _jsonSerializer.DeserializeFromStreamAsync(request.RequestStream, configurationType).ConfigureAwait(false); _configurationManager.SaveConfiguration(key, configuration); } public object Get(GetDefaultMetadataOptions request) { - return ToOptimizedSerializedResultUsingCache(new MetadataOptions()); - } - - public object Get(GetMetadataPlugins request) - { - return ToOptimizedSerializedResultUsingCache(_providerManager.GetAllMetadataPlugins()); + return ToOptimizedResult(new MetadataOptions()); } } } diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs index c375e272a7..9cc16b4a1f 100644 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ b/MediaBrowser.Api/Devices/DeviceService.cs @@ -7,6 +7,8 @@ using MediaBrowser.Model.Session; using System.IO; using System.Threading.Tasks; using MediaBrowser.Model.Services; +using MediaBrowser.Controller.Security; +using MediaBrowser.Controller.Session; namespace MediaBrowser.Api.Devices { @@ -16,6 +18,22 @@ namespace MediaBrowser.Api.Devices { } + [Route("/Devices/Info", "GET", Summary = "Gets info for a device")] + [Authenticated(Roles = "Admin")] + public class GetDeviceInfo : IReturn<DeviceInfo> + { + [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string Id { get; set; } + } + + [Route("/Devices/Options", "GET", Summary = "Gets options for a device")] + [Authenticated(Roles = "Admin")] + public class GetDeviceOptions : IReturn<DeviceOptions> + { + [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public string Id { get; set; } + } + [Route("/Devices", "DELETE", Summary = "Deletes a device")] public class DeleteDevice { @@ -35,37 +53,21 @@ namespace MediaBrowser.Api.Devices [Authenticated] public class PostCameraUpload : IRequiresRequestStream, IReturnVoid { - [ApiMember(Name = "DeviceId", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + [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 = "GET")] + [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 = "GET")] + [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 = "GET")] + [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/Info", "GET", Summary = "Gets device info")] - [Authenticated] - public class GetDeviceInfo : IReturn<DeviceInfo> - { - [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string Id { get; set; } - } - - [Route("/Devices/Capabilities", "GET", Summary = "Gets device capabilities")] - [Authenticated] - public class GetDeviceCapabilities : IReturn<ClientCapabilities> - { - [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string Id { get; set; } - } - [Route("/Devices/Options", "POST", Summary = "Updates device options")] [Authenticated(Roles = "Admin")] public class PostDeviceOptions : DeviceOptions, IReturnVoid @@ -77,34 +79,34 @@ namespace MediaBrowser.Api.Devices public class DeviceService : BaseApiService { private readonly IDeviceManager _deviceManager; + private readonly IAuthenticationRepository _authRepo; + private readonly ISessionManager _sessionManager; - public DeviceService(IDeviceManager deviceManager) + public DeviceService(IDeviceManager deviceManager, IAuthenticationRepository authRepo, ISessionManager sessionManager) { _deviceManager = deviceManager; + _authRepo = authRepo; + _sessionManager = sessionManager; } public void Post(PostDeviceOptions request) { - _deviceManager.UpdateDeviceInfo(request.Id, new DeviceOptions - { - CustomName = request.CustomName, - CameraUploadPath = request.CameraUploadPath - }); + _deviceManager.UpdateDeviceOptions(request.Id, request); } - public object Get(GetDeviceInfo request) + public object Get(GetDevices request) { - return ToOptimizedResult(_deviceManager.GetDevice(request.Id)); + return ToOptimizedResult(_deviceManager.GetDevices(request)); } - public object Get(GetDeviceCapabilities request) + public object Get(GetDeviceInfo request) { - return ToOptimizedResult(_deviceManager.GetCapabilities(request.Id)); + return _deviceManager.GetDevice(request.Id); } - public object Get(GetDevices request) + public object Get(GetDeviceOptions request) { - return ToOptimizedResult(_deviceManager.GetDevices(request)); + return _deviceManager.GetDeviceOptions(request.Id); } public object Get(GetCameraUploads request) @@ -114,10 +116,19 @@ namespace MediaBrowser.Api.Devices public void Delete(DeleteDevice request) { - _deviceManager.DeleteDevice(request.Id); + var sessions = _authRepo.Get(new AuthenticationInfoQuery + { + DeviceId = request.Id + + }).Items; + + foreach (var session in sessions) + { + _sessionManager.Logout(session); + } } - public void Post(PostCameraUpload request) + public Task Post(PostCameraUpload request) { var deviceId = Request.QueryString["DeviceId"]; var album = Request.QueryString["Album"]; @@ -126,29 +137,25 @@ namespace MediaBrowser.Api.Devices if (Request.ContentType.IndexOf("multi", StringComparison.OrdinalIgnoreCase) == -1) { - var task = _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo + return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo { MimeType = Request.ContentType, Album = album, Name = name, Id = id }); - - Task.WaitAll(task); } else { var file = Request.Files.Length == 0 ? null : Request.Files[0]; - var task = _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo + return _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo { MimeType = file.ContentType, Album = album, Name = name, Id = id }); - - Task.WaitAll(task); } } } diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs index 4f8cc52558..6bc2f7e4af 100644 --- a/MediaBrowser.Api/DisplayPreferencesService.cs +++ b/MediaBrowser.Api/DisplayPreferencesService.cs @@ -76,7 +76,7 @@ namespace MediaBrowser.Api { var result = _displayPreferencesManager.GetDisplayPreferences(request.Id, request.UserId, request.Client); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs deleted file mode 100644 index 6a0cea4df9..0000000000 --- a/MediaBrowser.Api/Dlna/DlnaServerService.cs +++ /dev/null @@ -1,263 +0,0 @@ -using MediaBrowser.Controller.Dlna; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Threading.Tasks; - -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Api.Dlna -{ - [Route("/Dlna/{UuId}/description.xml", "GET", Summary = "Gets dlna server info")] - [Route("/Dlna/{UuId}/description", "GET", Summary = "Gets dlna server info")] - public class GetDescriptionXml - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UuId { get; set; } - } - - [Route("/Dlna/{UuId}/contentdirectory/contentdirectory.xml", "GET", Summary = "Gets dlna content directory xml")] - [Route("/Dlna/{UuId}/contentdirectory/contentdirectory", "GET", Summary = "Gets dlna content directory xml")] - public class GetContentDirectory - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UuId { get; set; } - } - - [Route("/Dlna/{UuId}/connectionmanager/connectionmanager.xml", "GET", Summary = "Gets dlna connection manager xml")] - [Route("/Dlna/{UuId}/connectionmanager/connectionmanager", "GET", Summary = "Gets dlna connection manager xml")] - public class GetConnnectionManager - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UuId { get; set; } - } - - [Route("/Dlna/{UuId}/mediareceiverregistrar/mediareceiverregistrar.xml", "GET", Summary = "Gets dlna mediareceiverregistrar xml")] - [Route("/Dlna/{UuId}/mediareceiverregistrar/mediareceiverregistrar", "GET", Summary = "Gets dlna mediareceiverregistrar xml")] - public class GetMediaReceiverRegistrar - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UuId { get; set; } - } - - [Route("/Dlna/{UuId}/contentdirectory/control", "POST", Summary = "Processes a control request")] - public class ProcessContentDirectoryControlRequest : IRequiresRequestStream - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UuId { get; set; } - - public Stream RequestStream { get; set; } - } - - [Route("/Dlna/{UuId}/connectionmanager/control", "POST", Summary = "Processes a control request")] - public class ProcessConnectionManagerControlRequest : IRequiresRequestStream - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UuId { get; set; } - - public Stream RequestStream { get; set; } - } - - [Route("/Dlna/{UuId}/mediareceiverregistrar/control", "POST", Summary = "Processes a control request")] - public class ProcessMediaReceiverRegistrarControlRequest : IRequiresRequestStream - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UuId { get; set; } - - public Stream RequestStream { get; set; } - } - - [Route("/Dlna/{UuId}/mediareceiverregistrar/events", "SUBSCRIBE", Summary = "Processes an event subscription request")] - [Route("/Dlna/{UuId}/mediareceiverregistrar/events", "UNSUBSCRIBE", Summary = "Processes an event subscription request")] - public class ProcessMediaReceiverRegistrarEventRequest - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,UNSUBSCRIBE")] - public string UuId { get; set; } - } - - [Route("/Dlna/{UuId}/contentdirectory/events", "SUBSCRIBE", Summary = "Processes an event subscription request")] - [Route("/Dlna/{UuId}/contentdirectory/events", "UNSUBSCRIBE", Summary = "Processes an event subscription request")] - public class ProcessContentDirectoryEventRequest - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,UNSUBSCRIBE")] - public string UuId { get; set; } - } - - [Route("/Dlna/{UuId}/connectionmanager/events", "SUBSCRIBE", Summary = "Processes an event subscription request")] - [Route("/Dlna/{UuId}/connectionmanager/events", "UNSUBSCRIBE", Summary = "Processes an event subscription request")] - public class ProcessConnectionManagerEventRequest - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "SUBSCRIBE,UNSUBSCRIBE")] - public string UuId { get; set; } - } - - [Route("/Dlna/{UuId}/icons/{Filename}", "GET", Summary = "Gets a server icon")] - [Route("/Dlna/icons/{Filename}", "GET", Summary = "Gets a server icon")] - public class GetIcon - { - [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UuId { get; set; } - - [ApiMember(Name = "Filename", Description = "The icon filename", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Filename { get; set; } - } - - public class DlnaServerService : BaseApiService - { - private readonly IDlnaManager _dlnaManager; - private readonly IContentDirectory _contentDirectory; - private readonly IConnectionManager _connectionManager; - private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar; - - private const string XMLContentType = "text/xml; charset=UTF-8"; - private readonly IMemoryStreamFactory _memoryStreamProvider; - - public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IConnectionManager connectionManager, IMediaReceiverRegistrar mediaReceiverRegistrar, IMemoryStreamFactory memoryStreamProvider) - { - _dlnaManager = dlnaManager; - _contentDirectory = contentDirectory; - _connectionManager = connectionManager; - _mediaReceiverRegistrar = mediaReceiverRegistrar; - _memoryStreamProvider = memoryStreamProvider; - } - - public object Get(GetDescriptionXml request) - { - var url = Request.AbsoluteUri; - var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); - var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers.ToDictionary(), request.UuId, serverAddress); - - return ResultFactory.GetResult(xml, XMLContentType); - } - - public object Get(GetContentDirectory request) - { - var xml = _contentDirectory.GetServiceXml(Request.Headers.ToDictionary()); - - return ResultFactory.GetResult(xml, XMLContentType); - } - - public object Get(GetMediaReceiverRegistrar request) - { - var xml = _mediaReceiverRegistrar.GetServiceXml(Request.Headers.ToDictionary()); - - return ResultFactory.GetResult(xml, XMLContentType); - } - - public object Get(GetConnnectionManager request) - { - var xml = _connectionManager.GetServiceXml(Request.Headers.ToDictionary()); - - return ResultFactory.GetResult(xml, XMLContentType); - } - - public object Post(ProcessMediaReceiverRegistrarControlRequest request) - { - var response = PostAsync(request.RequestStream, _mediaReceiverRegistrar); - - return ResultFactory.GetResult(response.Xml, XMLContentType); - } - - public object Post(ProcessContentDirectoryControlRequest request) - { - var response = PostAsync(request.RequestStream, _contentDirectory); - - return ResultFactory.GetResult(response.Xml, XMLContentType); - } - - public object Post(ProcessConnectionManagerControlRequest request) - { - var response = PostAsync(request.RequestStream, _connectionManager); - - return ResultFactory.GetResult(response.Xml, XMLContentType); - } - - private ControlResponse PostAsync(Stream requestStream, IUpnpService service) - { - var id = GetPathValue(2); - - return service.ProcessControlRequest(new ControlRequest - { - Headers = Request.Headers.ToDictionary(), - InputXml = requestStream, - TargetServerUuId = id, - RequestedUrl = Request.AbsoluteUri - }); - } - - public object Get(GetIcon request) - { - using (var response = _dlnaManager.GetIcon(request.Filename)) - { - using (var ms = _memoryStreamProvider.CreateNew()) - { - response.Stream.CopyTo(ms); - - ms.Position = 0; - var bytes = ms.ToArray(); - return ResultFactory.GetResult(bytes, "image/" + response.Format.ToString().ToLower()); - } - } - } - - public object Subscribe(ProcessContentDirectoryEventRequest request) - { - return ProcessEventRequest(_contentDirectory); - } - - public object Subscribe(ProcessConnectionManagerEventRequest request) - { - return ProcessEventRequest(_connectionManager); - } - - public object Subscribe(ProcessMediaReceiverRegistrarEventRequest request) - { - return ProcessEventRequest(_mediaReceiverRegistrar); - } - - public object Unsubscribe(ProcessContentDirectoryEventRequest request) - { - return ProcessEventRequest(_contentDirectory); - } - - public object Unsubscribe(ProcessConnectionManagerEventRequest request) - { - return ProcessEventRequest(_connectionManager); - } - - public object Unsubscribe(ProcessMediaReceiverRegistrarEventRequest request) - { - return ProcessEventRequest(_mediaReceiverRegistrar); - } - - private object ProcessEventRequest(IEventManager eventManager) - { - var subscriptionId = GetHeader("SID"); - - if (string.Equals(Request.Verb, "SUBSCRIBE", StringComparison.OrdinalIgnoreCase)) - { - var notificationType = GetHeader("NT"); - - var callback = GetHeader("CALLBACK"); - var timeoutString = GetHeader("TIMEOUT"); - - if (string.IsNullOrEmpty(notificationType)) - { - return GetSubscriptionResponse(eventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callback)); - } - - return GetSubscriptionResponse(eventManager.CreateEventSubscription(notificationType, timeoutString, callback)); - } - - return GetSubscriptionResponse(eventManager.CancelEventSubscription(subscriptionId)); - } - - private object GetSubscriptionResponse(EventSubscriptionResponse response) - { - return ResultFactory.GetResult(response.Content, response.ContentType, response.Headers); - } - } -} diff --git a/MediaBrowser.Api/Dlna/DlnaService.cs b/MediaBrowser.Api/Dlna/DlnaService.cs deleted file mode 100644 index 4dd71f4463..0000000000 --- a/MediaBrowser.Api/Dlna/DlnaService.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Linq; -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Api.Dlna -{ - [Route("/Dlna/ProfileInfos", "GET", Summary = "Gets a list of profiles")] - public class GetProfileInfos : IReturn<DeviceProfileInfo[]> - { - } - - [Route("/Dlna/Profiles/{Id}", "DELETE", Summary = "Deletes a profile")] - public class DeleteProfile : IReturnVoid - { - [ApiMember(Name = "Id", Description = "Profile Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } - } - - [Route("/Dlna/Profiles/Default", "GET", Summary = "Gets the default profile")] - public class GetDefaultProfile : IReturn<DeviceProfile> - { - } - - [Route("/Dlna/Profiles/{Id}", "GET", Summary = "Gets a single profile")] - public class GetProfile : IReturn<DeviceProfile> - { - [ApiMember(Name = "Id", Description = "Profile Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/Dlna/Profiles/{Id}", "POST", Summary = "Updates a profile")] - public class UpdateProfile : DeviceProfile, IReturnVoid - { - } - - [Route("/Dlna/Profiles", "POST", Summary = "Creates a profile")] - public class CreateProfile : DeviceProfile, IReturnVoid - { - } - - [Authenticated(Roles = "Admin")] - public class DlnaService : BaseApiService - { - private readonly IDlnaManager _dlnaManager; - - public DlnaService(IDlnaManager dlnaManager) - { - _dlnaManager = dlnaManager; - } - - public object Get(GetProfileInfos request) - { - var result = _dlnaManager.GetProfileInfos().ToArray(); - - return ToOptimizedResult(result); - } - - public object Get(GetProfile request) - { - var result = _dlnaManager.GetProfile(request.Id); - - return ToOptimizedResult(result); - } - - public object Get(GetDefaultProfile request) - { - var result = _dlnaManager.GetDefaultProfile(); - - return ToOptimizedResult(result); - } - - public void Delete(DeleteProfile request) - { - _dlnaManager.DeleteProfile(request.Id); - } - - public void Post(UpdateProfile request) - { - _dlnaManager.UpdateProfile(request); - } - - public void Post(CreateProfile request) - { - _dlnaManager.CreateProfile(request); - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index 400169ac55..ea3920d38d 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -36,18 +36,6 @@ namespace MediaBrowser.Api /// <value><c>true</c> if [include directories]; otherwise, <c>false</c>.</value> [ApiMember(Name = "IncludeDirectories", Description = "An optional filter to include or exclude folders from the results. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool IncludeDirectories { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether [include hidden]. - /// </summary> - /// <value><c>true</c> if [include hidden]; otherwise, <c>false</c>.</value> - [ApiMember(Name = "IncludeHidden", Description = "An optional filter to include or exclude hidden files and folders. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool IncludeHidden { get; set; } - - public GetDirectoryContents() - { - IncludeHidden = true; - } } [Route("/Environment/ValidatePath", "POST", Summary = "Gets the contents of a given directory in the file system")] @@ -189,18 +177,7 @@ namespace MediaBrowser.Api { var result = new DefaultDirectoryBrowserInfo(); - try - { - var qnap = "/share/CACHEDEV1_DATA"; - if (_fileSystem.DirectoryExists(qnap)) - { - result.Path = qnap; - } - } - catch - { - - } + result.Path = _fileSystem.DefaultDirectory; return ToOptimizedResult(result); } @@ -223,10 +200,10 @@ namespace MediaBrowser.Api if (path.StartsWith(networkPrefix, StringComparison.OrdinalIgnoreCase) && path.LastIndexOf(UncSeparator) == 1) { - return ToOptimizedSerializedResultUsingCache(GetNetworkShares(path).OrderBy(i => i.Path).ToList()); + return ToOptimizedResult(GetNetworkShares(path).OrderBy(i => i.Path).ToList()); } - return ToOptimizedSerializedResultUsingCache(GetFileSystemEntries(request).ToList()); + return ToOptimizedResult(GetFileSystemEntries(request).ToList()); } public object Get(GetNetworkShares request) @@ -235,7 +212,7 @@ namespace MediaBrowser.Api var shares = GetNetworkShares(path).OrderBy(i => i.Path).ToList(); - return ToOptimizedSerializedResultUsingCache(shares); + return ToOptimizedResult(shares); } /// <summary> @@ -247,7 +224,7 @@ namespace MediaBrowser.Api { var result = GetDrives().ToList(); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -273,7 +250,7 @@ namespace MediaBrowser.Api { var result = _networkManager.GetNetworkDevices().ToList(); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -300,11 +277,6 @@ namespace MediaBrowser.Api { var entries = _fileSystem.GetFileSystemEntries(request.Path).OrderBy(i => i.FullName).Where(i => { - if (!request.IncludeHidden && i.IsHidden) - { - return false; - } - var isDirectory = i.IsDirectory; if (!request.IncludeFiles && !isDirectory) diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs index 585e9c49bc..59e203b7f7 100644 --- a/MediaBrowser.Api/FilterService.cs +++ b/MediaBrowser.Api/FilterService.cs @@ -19,7 +19,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } [ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string ParentId { get; set; } @@ -49,7 +49,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } [ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string ParentId { get; set; } @@ -76,6 +76,7 @@ namespace MediaBrowser.Api public bool? IsKids { get; set; } public bool? IsNews { get; set; } public bool? IsSeries { get; set; } + public bool? Recursive { get; set; } } [Authenticated] @@ -93,7 +94,7 @@ namespace MediaBrowser.Api public object Get(GetQueryFilters request) { var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId); - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase) || string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) || @@ -107,7 +108,6 @@ namespace MediaBrowser.Api var genreQuery = new InternalItemsQuery(user) { - AncestorIds = parentItem == null ? new string[] { } : new string[] { parentItem.Id.ToString("N") }, IncludeItemTypes = request.GetIncludeItemTypes(), DtoOptions = new Controller.Dto.DtoOptions { @@ -123,34 +123,44 @@ namespace MediaBrowser.Api IsSeries = request.IsSeries }; + // Non recursive not yet supported for library folders + if ((request.Recursive ?? true) || parentItem is UserView || parentItem is ICollectionFolder) + { + genreQuery.AncestorIds = parentItem == null ? Array.Empty<Guid>() : new Guid[] { parentItem.Id }; + } + else + { + genreQuery.Parent = parentItem; + } + if (string.Equals(request.IncludeItemTypes, "MusicAlbum", StringComparison.OrdinalIgnoreCase) || string.Equals(request.IncludeItemTypes, "MusicVideo", StringComparison.OrdinalIgnoreCase) || string.Equals(request.IncludeItemTypes, "MusicArtist", StringComparison.OrdinalIgnoreCase) || string.Equals(request.IncludeItemTypes, "Audio", StringComparison.OrdinalIgnoreCase)) { - filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameIdPair + filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameGuidPair { Name = i.Item1.Name, - Id = i.Item1.Id.ToString("N") + Id = i.Item1.Id }).ToArray(); } else if (string.Equals(request.IncludeItemTypes, "Game", StringComparison.OrdinalIgnoreCase) || string.Equals(request.IncludeItemTypes, "GameSystem", StringComparison.OrdinalIgnoreCase)) { - filters.Genres = _libraryManager.GetGameGenres(genreQuery).Items.Select(i => new NameIdPair + filters.Genres = _libraryManager.GetGameGenres(genreQuery).Items.Select(i => new NameGuidPair { Name = i.Item1.Name, - Id = i.Item1.Id.ToString("N") + Id = i.Item1.Id }).ToArray(); } else { - filters.Genres = _libraryManager.GetGenres(genreQuery).Items.Select(i => new NameIdPair + filters.Genres = _libraryManager.GetGenres(genreQuery).Items.Select(i => new NameGuidPair { Name = i.Item1.Name, - Id = i.Item1.Id.ToString("N") + Id = i.Item1.Id }).ToArray(); } @@ -161,7 +171,7 @@ namespace MediaBrowser.Api public object Get(GetQueryFiltersLegacy request) { var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId); - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase) || string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) || @@ -172,7 +182,7 @@ namespace MediaBrowser.Api } var item = string.IsNullOrEmpty(request.ParentId) ? - user == null ? _libraryManager.RootFolder : user.RootFolder : + user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder() : parentItem; var result = ((Folder)item).GetItemList(GetItemsQuery(request, user)); diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs index 6c48b732f6..3e4e205067 100644 --- a/MediaBrowser.Api/GamesService.cs +++ b/MediaBrowser.Api/GamesService.cs @@ -17,14 +17,6 @@ using MediaBrowser.Model.Extensions; namespace MediaBrowser.Api { /// <summary> - /// Class GetSimilarGames - /// </summary> - [Route("/Games/{Id}/Similar", "GET", Summary = "Finds games similar to a given game.")] - public class GetSimilarGames : BaseGetSimilarItemsFromItem - { - } - - /// <summary> /// Class GetGameSystemSummaries /// </summary> [Route("/Games/SystemSummaries", "GET", Summary = "Finds games similar to a given game.")] @@ -35,7 +27,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } /// <summary> @@ -109,11 +101,9 @@ namespace MediaBrowser.Api .Select(i => GetSummary(i, user)) .ToArray(); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - /// <summary> /// Gets the summary. /// </summary> @@ -151,51 +141,5 @@ namespace MediaBrowser.Api return summary; } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetSimilarGames request) - { - var result = GetSimilarItemsResult(request); - - return ToOptimizedSerializedResultUsingCache(result); - } - - private QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request) - { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - - var item = string.IsNullOrEmpty(request.Id) ? - (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : - _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - Limit = request.Limit, - IncludeItemTypes = new[] - { - typeof(Game).Name - }, - SimilarTo = item, - DtoOptions = dtoOptions - - }); - - var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user); - - var result = new QueryResult<BaseItemDto> - { - Items = returnList, - - TotalRecordCount = itemsResult.Count - }; - - return result; - } } } diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs index b61c81972c..d4c01fdb02 100644 --- a/MediaBrowser.Api/Images/ImageRequest.cs +++ b/MediaBrowser.Api/Images/ImageRequest.cs @@ -87,18 +87,14 @@ namespace MediaBrowser.Api.Images /// Gets or sets the type of the image. /// </summary> /// <value>The type of the image.</value> - [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + [ApiMember(Name = "Type", Description = "Image Type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET,POST,DELETE")] public ImageType Type { get; set; } /// <summary> /// Gets or sets the index. /// </summary> /// <value>The index.</value> - [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "DELETE")] + [ApiMember(Name = "Index", Description = "Image Index", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET,POST,DELETE")] public int? Index { get; set; } } } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 2b8ac1a667..c3b2e82e7c 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -50,8 +50,8 @@ namespace MediaBrowser.Api.Images /// Gets or sets the id. /// </summary> /// <value>The id.</value> - [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path")] + public Guid Id { get; set; } } /// <summary> @@ -65,7 +65,7 @@ namespace MediaBrowser.Api.Images /// Gets or sets the id. /// </summary> /// <value>The id.</value> - [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } /// <summary> @@ -86,7 +86,7 @@ namespace MediaBrowser.Api.Images /// Gets or sets the new index. /// </summary> /// <value>The new index.</value> - [ApiMember(Name = "NewIndex", Description = "The new image index", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "NewIndex", Description = "The new image index", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public int NewIndex { get; set; } } @@ -105,8 +105,8 @@ namespace MediaBrowser.Api.Images [Route("/Persons/{Name}/Images/{Type}/{Index}", "GET")] [Route("/Studios/{Name}/Images/{Type}", "GET")] [Route("/Studios/{Name}/Images/{Type}/{Index}", "GET")] - [Route("/Years/{Year}/Images/{Type}", "GET")] - [Route("/Years/{Year}/Images/{Type}/{Index}", "GET")] + ////[Route("/Years/{Year}/Images/{Type}", "GET")] + ////[Route("/Years/{Year}/Images/{Type}/{Index}", "GET")] [Route("/Artists/{Name}/Images/{Type}", "HEAD")] [Route("/Artists/{Name}/Images/{Type}/{Index}", "HEAD")] [Route("/Genres/{Name}/Images/{Type}", "HEAD")] @@ -119,8 +119,8 @@ namespace MediaBrowser.Api.Images [Route("/Persons/{Name}/Images/{Type}/{Index}", "HEAD")] [Route("/Studios/{Name}/Images/{Type}", "HEAD")] [Route("/Studios/{Name}/Images/{Type}/{Index}", "HEAD")] - [Route("/Years/{Year}/Images/{Type}", "HEAD")] - [Route("/Years/{Year}/Images/{Type}/{Index}", "HEAD")] + ////[Route("/Years/{Year}/Images/{Type}", "HEAD")] + ////[Route("/Years/{Year}/Images/{Type}/{Index}", "HEAD")] public class GetItemByNameImage : ImageRequest { /// <summary> @@ -145,7 +145,7 @@ namespace MediaBrowser.Api.Images /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -177,7 +177,7 @@ namespace MediaBrowser.Api.Images /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -265,7 +265,7 @@ namespace MediaBrowser.Api.Images var result = GetItemImageInfos(item); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -373,11 +373,7 @@ namespace MediaBrowser.Api.Images /// <returns>System.Object.</returns> public object Get(GetItemImage request) { - var item = string.IsNullOrEmpty(request.Id) ? - _libraryManager.RootFolder : - _libraryManager.GetItemById(request.Id); - - return GetImage(request, item, false); + return GetImage(request, request.Id, null, false); } /// <summary> @@ -387,11 +383,7 @@ namespace MediaBrowser.Api.Images /// <returns>System.Object.</returns> public object Head(GetItemImage request) { - var item = string.IsNullOrEmpty(request.Id) ? - _libraryManager.RootFolder : - _libraryManager.GetItemById(request.Id); - - return GetImage(request, item, true); + return GetImage(request, request.Id, null, true); } /// <summary> @@ -403,14 +395,14 @@ namespace MediaBrowser.Api.Images { var item = _userManager.GetUserById(request.Id); - return GetImage(request, item, false); + return GetImage(request, Guid.Empty, item, false); } public object Head(GetUserImage request) { var item = _userManager.GetUserById(request.Id); - return GetImage(request, item, true); + return GetImage(request, Guid.Empty, item, true); } public object Get(GetItemByNameImage request) @@ -419,7 +411,7 @@ namespace MediaBrowser.Api.Images var item = GetItemByName(request.Name, type, _libraryManager, new DtoOptions(false)); - return GetImage(request, item, false); + return GetImage(request, item.Id, item, false); } public object Head(GetItemByNameImage request) @@ -428,32 +420,30 @@ namespace MediaBrowser.Api.Images var item = GetItemByName(request.Name, type, _libraryManager, new DtoOptions(false)); - return GetImage(request, item, true); + return GetImage(request, item.Id, item, true); } /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(PostUserImage request) + public Task Post(PostUserImage request) { var userId = GetPathValue(1); - AssertCanUpdateUser(_authContext, _userManager, userId, true); + AssertCanUpdateUser(_authContext, _userManager, new Guid(userId), true); request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true); var item = _userManager.GetUserById(userId); - var task = PostImage(item, request.RequestStream, request.Type, Request.ContentType); - - Task.WaitAll(task); + return PostImage(item, request.RequestStream, request.Type, Request.ContentType); } /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(PostItemImage request) + public Task Post(PostItemImage request) { var id = GetPathValue(1); @@ -461,9 +451,7 @@ namespace MediaBrowser.Api.Images var item = _libraryManager.GetItemById(id); - var task = PostImage(item, request.RequestStream, request.Type, Request.ContentType); - - Task.WaitAll(task); + return PostImage(item, request.RequestStream, request.Type, Request.ContentType); } /// <summary> @@ -510,7 +498,7 @@ namespace MediaBrowser.Api.Images /// <param name="currentIndex">Index of the current.</param> /// <param name="newIndex">The new index.</param> /// <returns>Task.</returns> - private void UpdateItemIndex(IHasMetadata item, ImageType type, int currentIndex, int newIndex) + private void UpdateItemIndex(BaseItem item, ImageType type, int currentIndex, int newIndex) { item.SwapImages(type, currentIndex, newIndex); } @@ -523,7 +511,7 @@ namespace MediaBrowser.Api.Images /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <returns>System.Object.</returns> /// <exception cref="ResourceNotFoundException"></exception> - public Task<object> GetImage(ImageRequest request, IHasMetadata item, bool isHeadRequest) + public Task<object> GetImage(ImageRequest request, Guid itemId, BaseItem item, bool isHeadRequest) { if (request.PercentPlayed.HasValue) { @@ -549,18 +537,42 @@ namespace MediaBrowser.Api.Images } } + if (item == null) + { + item = _libraryManager.GetItemById(itemId); + + if (item == null) + { + throw new ResourceNotFoundException(string.Format("Item {0} not found.", itemId.ToString("N"))); + } + } + var imageInfo = GetImageInfo(request, item); if (imageInfo == null) { - throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type)); + 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)); } - var supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type) : new List<IImageEnhancer>(); + IImageEnhancer[] supportedImageEnhancers; + + if (_imageProcessor.ImageEnhancers.Length > 0) + { + if (item == null) + { + item = _libraryManager.GetItemById(itemId); + } + + supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type) : Array.Empty<IImageEnhancer>(); + } + else + { + supportedImageEnhancers = Array.Empty<IImageEnhancer>(); + } var cropwhitespace = request.Type == ImageType.Logo || - request.Type == ImageType.Art - || (request.Type == ImageType.Primary && item is LiveTvChannel); + request.Type == ImageType.Art; if (request.CropWhitespace.HasValue) { @@ -583,6 +595,7 @@ namespace MediaBrowser.Api.Images }; return GetImageResult(item, + itemId, request, imageInfo, cropwhitespace, @@ -593,12 +606,13 @@ namespace MediaBrowser.Api.Images isHeadRequest); } - private async Task<object> GetImageResult(IHasMetadata item, + private async Task<object> GetImageResult(BaseItem item, + Guid itemId, ImageRequest request, ItemImageInfo image, bool cropwhitespace, ImageFormat[] supportedFormats, - List<IImageEnhancer> enhancers, + IImageEnhancer[] enhancers, TimeSpan? cacheDuration, IDictionary<string, string> headers, bool isHeadRequest) @@ -611,8 +625,7 @@ namespace MediaBrowser.Api.Images ImageIndex = request.Index ?? 0, Image = image, Item = item, - ItemId = item.Id.ToString("N"), - ItemType = item.GetType().Name, + ItemId = itemId, MaxHeight = request.MaxHeight, MaxWidth = request.MaxWidth, Quality = request.Quality ?? 100, @@ -660,21 +673,15 @@ namespace MediaBrowser.Api.Images private ImageFormat[] GetClientSupportedFormats() { - //Logger.Debug("Request types: {0}", string.Join(",", Request.AcceptTypes ?? new string[] { })); - var supportsWebP = (Request.AcceptTypes ?? new string[] { }).Contains("image/webp", StringComparer.OrdinalIgnoreCase); + //Logger.Debug("Request types: {0}", string.Join(",", Request.AcceptTypes ?? Array.Empty<string>())); + var supportedFormats = (Request.AcceptTypes ?? Array.Empty<string>()).Select(i => i.Split(';')[0]).ToArray(); + var acceptParam = Request.QueryString["accept"]; - var userAgent = Request.UserAgent ?? string.Empty; - - if (!supportsWebP) - { - if (string.Equals(Request.QueryString["accept"], "webp", StringComparison.OrdinalIgnoreCase)) - { - supportsWebP = true; - } - } + var supportsWebP = SupportsFormat(supportedFormats, acceptParam, "webp", false); if (!supportsWebP) { + var userAgent = Request.UserAgent ?? string.Empty; if (userAgent.IndexOf("crosswalk", StringComparison.OrdinalIgnoreCase) != -1 && userAgent.IndexOf("android", StringComparison.OrdinalIgnoreCase) != -1) { @@ -682,16 +689,39 @@ namespace MediaBrowser.Api.Images } } + var formats = new List<ImageFormat>(4); + if (supportsWebP) { - // Not displaying properly on iOS - if (userAgent.IndexOf("cfnetwork", StringComparison.OrdinalIgnoreCase) == -1) - { - return new[] { ImageFormat.Webp, ImageFormat.Jpg, ImageFormat.Png }; - } + formats.Add(ImageFormat.Webp); + } + + formats.Add(ImageFormat.Jpg); + formats.Add(ImageFormat.Png); + + if (SupportsFormat(supportedFormats, acceptParam, "gif", true)) + { + formats.Add(ImageFormat.Gif); + } + + return formats.ToArray(); + } + + private bool SupportsFormat(string[] requestAcceptTypes, string acceptParam, string format, bool acceptAll) + { + var mimeType = "image/" + format; + + if (requestAcceptTypes.Contains(mimeType)) + { + return true; + } + + if (acceptAll && requestAcceptTypes.Contains("*/*")) + { + return true; } - return new[] { ImageFormat.Jpg, ImageFormat.Png }; + return string.Equals(Request.QueryString["accept"], format, StringComparison.OrdinalIgnoreCase); } /// <summary> @@ -700,7 +730,7 @@ 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, IHasMetadata item) + private ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item) { var index = request.Index ?? 0; diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index 4782d76dfc..8d75ec10c7 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -73,13 +73,13 @@ namespace MediaBrowser.Api.Images public class BaseDownloadRemoteImage : IReturnVoid { - [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public ImageType Type { get; set; } - [ApiMember(Name = "ProviderName", Description = "The image provider", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "ProviderName", Description = "The image provider", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string ProviderName { get; set; } - [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string ImageUrl { get; set; } } @@ -91,7 +91,7 @@ namespace MediaBrowser.Api.Images /// Gets or sets the id. /// </summary> /// <value>The id.</value> - [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string Id { get; set; } } @@ -129,7 +129,7 @@ namespace MediaBrowser.Api.Images var result = GetImageProviders(item); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } private List<ImageProviderInfo> GetImageProviders(BaseItem item) @@ -188,13 +188,11 @@ namespace MediaBrowser.Api.Images /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(DownloadRemoteImage request) + public Task Post(DownloadRemoteImage request) { var item = _libraryManager.GetItemById(request.Id); - var task = DownloadRemoteImage(item, request); - - Task.WaitAll(task); + return DownloadRemoteImage(item, request); } /// <summary> @@ -215,12 +213,7 @@ namespace MediaBrowser.Api.Images /// </summary> /// <param name="request">The request.</param> /// <returns>System.Object.</returns> - public object Get(GetRemoteImage request) - { - return GetAsync(request).Result; - } - - public async Task<object> GetAsync(GetRemoteImage request) + public async Task<object> Get(GetRemoteImage request) { var urlHash = request.ImageUrl.GetMD5(); var pointerCachePath = GetFullCachePath(urlHash.ToString()); diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index a454642a42..39a7904866 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } + public Guid Id { get; set; } } [Route("/Items/RemoteSearch/Movie", "POST")] @@ -44,9 +44,9 @@ namespace MediaBrowser.Api { } - [Route("/Items/RemoteSearch/AdultVideo", "POST")] + [Route("/Items/RemoteSearch/MusicVideo", "POST")] [Authenticated] - public class GetAdultVideoRemoteSearchResults : RemoteSearchQuery<ItemLookupInfo>, IReturn<List<RemoteSearchResult>> + public class GetMusicVideoRemoteSearchResults : RemoteSearchQuery<MusicVideoInfo>, IReturn<List<RemoteSearchResult>> { } @@ -186,6 +186,13 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } + public async Task<object> Post(GetMusicVideoRemoteSearchResults request) + { + var result = await _providerManager.GetRemoteSearchResults<MusicVideo, MusicVideoInfo>(request, CancellationToken.None).ConfigureAwait(false); + + return ToOptimizedResult(result); + } + public async Task<object> Post(GetPersonRemoteSearchResults request) { var result = await _providerManager.GetRemoteSearchResults<Person, PersonLookupInfo>(request, CancellationToken.None).ConfigureAwait(false); @@ -212,7 +219,7 @@ namespace MediaBrowser.Api return GetRemoteImage(request); } - public void Post(ApplySearchCriteria request) + public Task Post(ApplySearchCriteria request) { var item = _libraryManager.GetItemById(new Guid(request.Id)); @@ -232,17 +239,15 @@ namespace MediaBrowser.Api //item.ProductionYear = request.ProductionYear; //item.Name = request.Name; - var task = _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem) + return _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(_fileSystem) { MetadataRefreshMode = MetadataRefreshMode.FullRefresh, - ImageRefreshMode = ImageRefreshMode.FullRefresh, + ImageRefreshMode = MetadataRefreshMode.FullRefresh, ReplaceAllMetadata = true, ReplaceAllImages = request.ReplaceAllImages, - SearchResult = request, - ForceEnableInternetMetadata = true + SearchResult = request }, CancellationToken.None); - Task.WaitAll(task); } /// <summary> diff --git a/MediaBrowser.Api/ItemRefreshService.cs b/MediaBrowser.Api/ItemRefreshService.cs index d26fb768a3..ab083c207d 100644 --- a/MediaBrowser.Api/ItemRefreshService.cs +++ b/MediaBrowser.Api/ItemRefreshService.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Api public MetadataRefreshMode MetadataRefreshMode { get; set; } [ApiMember(Name = "ImageRefreshMode", Description = "Specifies the image refresh mode", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")] - public ImageRefreshMode ImageRefreshMode { get; set; } + public MetadataRefreshMode ImageRefreshMode { get; set; } [ApiMember(Name = "ReplaceAllMetadata", Description = "Determines if metadata should be replaced. Only applicable if mode is FullRefresh", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")] public bool ReplaceAllMetadata { get; set; } @@ -73,9 +73,8 @@ namespace MediaBrowser.Api ImageRefreshMode = request.ImageRefreshMode, ReplaceAllImages = request.ReplaceAllImages, ReplaceAllMetadata = request.ReplaceAllMetadata, - ForceSave = request.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || request.ImageRefreshMode == ImageRefreshMode.FullRefresh || request.ReplaceAllImages || request.ReplaceAllMetadata, - IsAutomated = false, - ValidateChildren = request.Recursive + ForceSave = request.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || request.ImageRefreshMode == MetadataRefreshMode.FullRefresh || request.ReplaceAllImages || request.ReplaceAllMetadata, + IsAutomated = false }; } } diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index a55741d7dc..22eb7ea099 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -15,6 +15,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Services; +using MediaBrowser.Model.IO; namespace MediaBrowser.Api { @@ -36,7 +37,7 @@ namespace MediaBrowser.Api public class UpdateItemContentType : IReturnVoid { [ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string ItemId { get; set; } + public Guid ItemId { get; set; } [ApiMember(Name = "ContentType", Description = "The content type of the item", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string ContentType { get; set; } @@ -49,13 +50,15 @@ namespace MediaBrowser.Api private readonly IProviderManager _providerManager; private readonly ILocalizationManager _localizationManager; private readonly IServerConfigurationManager _config; + private readonly IFileSystem _fileSystem; - public ItemUpdateService(ILibraryManager libraryManager, IProviderManager providerManager, ILocalizationManager localizationManager, IServerConfigurationManager config) + public ItemUpdateService(IFileSystem fileSystem, ILibraryManager libraryManager, IProviderManager providerManager, ILocalizationManager localizationManager, IServerConfigurationManager config) { _libraryManager = libraryManager; _providerManager = providerManager; _localizationManager = localizationManager; _config = config; + _fileSystem = fileSystem; } public object Get(GetMetadataEditorInfo request) @@ -199,6 +202,9 @@ namespace MediaBrowser.Api var newLockData = request.LockData ?? false; var isLockedChanged = item.IsLocked != newLockData; + var series = item as Series; + var displayOrderChanged = series != null && !string.Equals(series.DisplayOrder ?? string.Empty, request.DisplayOrder ?? string.Empty, StringComparison.OrdinalIgnoreCase); + // Do this first so that metadata savers can pull the updates from the database. if (request.People != null) { @@ -221,6 +227,17 @@ namespace MediaBrowser.Api child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); } } + + if (displayOrderChanged) + { + _providerManager.QueueRefresh(series.Id, new MetadataRefreshOptions(_fileSystem) + { + MetadataRefreshMode = MetadataRefreshMode.FullRefresh, + ImageRefreshMode = MetadataRefreshMode.FullRefresh, + ReplaceAllMetadata = true + + }, RefreshPriority.High); + } } private DateTime NormalizeDateTime(DateTime val) @@ -238,7 +255,6 @@ namespace MediaBrowser.Api item.CriticRating = request.CriticRating; item.CommunityRating = request.CommunityRating; - item.HomePageUrl = request.HomePageUrl; item.IndexNumber = request.IndexNumber; item.ParentIndexNumber = request.ParentIndexNumber; item.Overview = request.Overview; @@ -247,12 +263,9 @@ namespace MediaBrowser.Api var episode = item as Episode; if (episode != null) { - episode.DvdSeasonNumber = request.DvdSeasonNumber; - episode.DvdEpisodeNumber = request.DvdEpisodeNumber; episode.AirsAfterSeasonNumber = request.AirsAfterSeasonNumber; episode.AirsBeforeEpisodeNumber = request.AirsBeforeEpisodeNumber; episode.AirsBeforeSeasonNumber = request.AirsBeforeSeasonNumber; - episode.AbsoluteEpisodeNumber = request.AbsoluteEpisodeNumber; } item.Tags = request.Tags; diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index a036a00a6e..cc8c1251fe 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -30,6 +30,8 @@ using MediaBrowser.Model.Services; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; using MediaBrowser.Model.Extensions; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; namespace MediaBrowser.Api.Library { @@ -50,7 +52,7 @@ namespace MediaBrowser.Api.Library /// </summary> [Route("/Items/{Id}/CriticReviews", "GET", Summary = "Gets critic reviews for an item")] [Authenticated] - public class GetCriticReviews : IReturn<QueryResult<ItemReview>> + public class GetCriticReviews : IReturn<QueryResult<BaseItemDto>> { /// <summary> /// Gets or sets the id. @@ -86,7 +88,7 @@ namespace MediaBrowser.Api.Library /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. @@ -111,7 +113,7 @@ namespace MediaBrowser.Api.Library /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. @@ -136,7 +138,7 @@ namespace MediaBrowser.Api.Library /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. @@ -167,7 +169,7 @@ namespace MediaBrowser.Api.Library [Authenticated] public class DeleteItems : IReturnVoid { - [ApiMember(Name = "Ids", Description = "Ids", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + [ApiMember(Name = "Ids", Description = "Ids", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] public string Ids { get; set; } } @@ -176,7 +178,7 @@ namespace MediaBrowser.Api.Library public class GetItemCounts : IReturn<ItemCounts> { [ApiMember(Name = "UserId", Description = "Optional. Get counts from a specific user's library.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } [ApiMember(Name = "IsFavorite", Description = "Optional. Get counts of favorite items", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsFavorite { get; set; } @@ -191,7 +193,7 @@ namespace MediaBrowser.Api.Library /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. @@ -201,21 +203,6 @@ namespace MediaBrowser.Api.Library public string Id { get; set; } } - [Route("/Items/YearIndex", "GET", Summary = "Gets a year index based on an item query.")] - [Authenticated] - public class GetYearIndex : IReturn<List<ItemIndex>> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } - - [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string IncludeItemTypes { get; set; } - } - /// <summary> /// Class GetPhyscialPaths /// </summary> @@ -238,7 +225,7 @@ namespace MediaBrowser.Api.Library [Authenticated] public class PostUpdatedSeries : IReturnVoid { - [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "POST")] public string TvdbId { get; set; } } @@ -247,12 +234,28 @@ namespace MediaBrowser.Api.Library [Authenticated] public class PostUpdatedMovies : IReturnVoid { - [ApiMember(Name = "TmdbId", Description = "Tmdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "TmdbId", Description = "Tmdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "POST")] public string TmdbId { get; set; } - [ApiMember(Name = "ImdbId", Description = "Imdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "ImdbId", Description = "Imdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "POST")] public string ImdbId { get; set; } } + public class MediaUpdateInfo + { + public string Path { get; set; } + + // Created, Modified, Deleted + public string UpdateType { get; set; } + } + + [Route("/Library/Media/Updated", "POST", Summary = "Reports that new movies have been added by an external source")] + [Authenticated] + public class PostUpdatedMedia : IReturnVoid + { + [ApiMember(Name = "Updates", Description = "A list of updated media paths", IsRequired = false, DataType = "string", ParameterType = "body", Verb = "POST")] + public List<MediaUpdateInfo> Updates { get; set; } + } + [Route("/Items/{Id}/Download", "GET", Summary = "Downloads item media")] [Authenticated(Roles = "download")] public class GetDownload @@ -265,12 +268,49 @@ namespace MediaBrowser.Api.Library public string Id { get; set; } } + [Route("/Games/{Id}/Similar", "GET", Summary = "Finds games similar to a given game.")] + [Route("/Artists/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")] [Route("/Items/{Id}/Similar", "GET", Summary = "Gets similar items")] + [Route("/Albums/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")] + [Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")] + [Route("/Movies/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given movie.")] + [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")] [Authenticated] public class GetSimilarItems : BaseGetSimilarItemsFromItem { } + [Route("/Libraries/AvailableOptions", "GET")] + [Authenticated(AllowBeforeStartupWizard = true)] + 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 /// </summary> @@ -294,11 +334,12 @@ namespace MediaBrowser.Api.Library private readonly ILibraryMonitor _libraryMonitor; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _config; + private readonly IProviderManager _providerManager; /// <summary> /// Initializes a new instance of the <see cref="LibraryService" /> class. /// </summary> - public LibraryService(IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, + public LibraryService(IProviderManager providerManager, IItemRepository itemRepo, ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService, IUserDataManager userDataManager, IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILiveTvManager liveTv, ITVSeriesManager tvManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, IServerConfigurationManager config) { _itemRepo = itemRepo; @@ -314,63 +355,287 @@ namespace MediaBrowser.Api.Library _libraryMonitor = libraryMonitor; _fileSystem = fileSystem; _config = config; + _providerManager = providerManager; } - public object Get(GetSimilarItems request) + private string[] GetRepresentativeItemTypes(string contentType) { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + switch (contentType) + { + case CollectionType.BoxSets: + return new string[] { "BoxSet" }; + case CollectionType.Playlists: + return new string[] { "Playlist" }; + case CollectionType.Movies: + return new string[] { "Movie" }; + case CollectionType.TvShows: + return new string[] { "Series", "Season", "Episode" }; + case CollectionType.Books: + return new string[] { "Book" }; + case CollectionType.Games: + return new string[] { "Game", "GameSystem" }; + case CollectionType.Music: + return new string[] { "MusicAlbum", "MusicArtist", "Audio", "MusicVideo" }; + case CollectionType.HomeVideos: + case CollectionType.Photos: + return new string[] { "Video", "Photo" }; + case CollectionType.MusicVideos: + return new string[] { "MusicVideo" }; + default: + return new string[] { "Series", "Season", "Episode", "Movie" }; + } + } - var item = string.IsNullOrEmpty(request.Id) ? - (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : - _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); + private bool IsSaverEnabledByDefault(string name, string[] itemTypes, bool isNewLibrary) + { + if (isNewLibrary) + { + return false; + } - if (item is Game) + var metadataOptions = _config.Configuration.MetadataOptions + .Where(i => itemTypes.Contains(i.ItemType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + .ToArray(); + + if (metadataOptions.Length == 0) { - return new GamesService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext) - { - Request = Request, + return true; + } - }.Get(new GetSimilarGames + return metadataOptions.Any(i => !i.DisabledMetadataSavers.Contains(name, StringComparer.OrdinalIgnoreCase)); + } + + private bool IsMetadataFetcherEnabledByDefault(string name, string type, bool isNewLibrary) + { + if (isNewLibrary) + { + if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase)) { - Fields = request.Fields, - Id = request.Id, - Limit = request.Limit, - UserId = request.UserId, - ImageTypeLimit = request.ImageTypeLimit - }); + if (string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + if (string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + return true; + } + else if (string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + else if (string.Equals(name, "The Open Movie Database", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + else if (string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + else if (string.Equals(name, "MusicBrainz", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return false; + } + + var metadataOptions = _config.Configuration.MetadataOptions + .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + if (metadataOptions.Length == 0) + { + return true; } - if (item is MusicAlbum) + + return metadataOptions.Any(i => !i.DisabledMetadataFetchers.Contains(name, StringComparer.OrdinalIgnoreCase)); + } + + private bool IsImageFetcherEnabledByDefault(string name, string type, bool isNewLibrary) + { + if (isNewLibrary) { - return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext) + if (string.Equals(name, "TheMovieDb", StringComparison.OrdinalIgnoreCase)) { - Request = Request, - - }.Get(new GetSimilarAlbums + if (string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + return true; + } + else if (string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)) { - Fields = request.Fields, - Id = request.Id, - Limit = request.Limit, - UserId = request.UserId, - ExcludeArtistIds = request.ExcludeArtistIds, - ImageTypeLimit = request.ImageTypeLimit - }); + return true; + } + else if (string.Equals(name, "The Open Movie Database", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + else if (string.Equals(name, "FanArt", StringComparison.OrdinalIgnoreCase)) + { + if (string.Equals(type, "Season", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(type, "MusicVideo", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + return true; + } + else if (string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + else if (string.Equals(name, "Emby Designs", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + else if (string.Equals(name, "Screen Grabber", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + else if (string.Equals(name, "Image Extractor", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return false; } - if (item is MusicArtist) + + var metadataOptions = _config.Configuration.MetadataOptions + .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + if (metadataOptions.Length == 0) { - return new AlbumsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _authContext) + return true; + } + + return metadataOptions.Any(i => !i.DisabledImageFetchers.Contains(name, StringComparer.OrdinalIgnoreCase)); + } + + public object Get(GetLibraryOptionsInfo request) + { + var result = new LibraryOptionsResult(); + + var types = GetRepresentativeItemTypes(request.LibraryContentType); + var isNewLibrary = request.IsNewLibrary; + var typesList = types.ToList(); + + var plugins = _providerManager.GetAllMetadataPlugins() + .Where(i => types.Contains(i.ItemType, StringComparer.OrdinalIgnoreCase)) + .OrderBy(i => typesList.IndexOf(i.ItemType)) + .ToList(); + + result.MetadataSavers = plugins + .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataSaver)) + .Select(i => new LibraryOptionInfo { - Request = Request, + Name = i.Name, + DefaultEnabled = IsSaverEnabledByDefault(i.Name, types, isNewLibrary) + }) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(); - }.Get(new GetSimilarArtists + result.MetadataReaders = plugins + .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LocalMetadataProvider)) + .Select(i => new LibraryOptionInfo { - Fields = request.Fields, - Id = request.Id, - Limit = request.Limit, - UserId = request.UserId, - ImageTypeLimit = request.ImageTypeLimit + Name = i.Name, + DefaultEnabled = true + }) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(); + + result.SubtitleFetchers = plugins + .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.SubtitleFetcher)) + .Select(i => new LibraryOptionInfo + { + Name = i.Name, + DefaultEnabled = true + }) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(); + + var typeOptions = new List<LibraryTypeOptions>(); + + foreach (var type in types) + { + ImageOption[] defaultImageOptions = null; + TypeOptions.DefaultImageOptions.TryGetValue(type, out defaultImageOptions); + + typeOptions.Add(new LibraryTypeOptions + { + Type = type, + + MetadataFetchers = plugins + .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.MetadataFetcher)) + .Select(i => new LibraryOptionInfo + { + Name = i.Name, + DefaultEnabled = IsMetadataFetcherEnabledByDefault(i.Name, type, isNewLibrary) + }) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(), + + ImageFetchers = plugins + .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.ImageFetcher)) + .Select(i => new LibraryOptionInfo + { + Name = i.Name, + DefaultEnabled = IsImageFetcherEnabledByDefault(i.Name, type, isNewLibrary) + }) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(), + + SupportedImageTypes = plugins + .Where(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => i.SupportedImageTypes ?? Array.Empty<ImageType>()) + .Distinct() + .ToArray(), + + DefaultImageOptions = defaultImageOptions ?? Array.Empty<ImageOption>() }); } + result.TypeOptions = typeOptions.ToArray(); + + return result; + } + + public object Get(GetSimilarItems request) + { + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; + + var item = string.IsNullOrEmpty(request.Id) ? + (!request.UserId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : + _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); + var program = item as IHasProgramAttributes; if (item is Movie || (program != null && program.IsMovie) || item is Trailer) @@ -379,33 +644,70 @@ namespace MediaBrowser.Api.Library { Request = Request, - }.Get(new GetSimilarMovies - { - Fields = request.Fields, - Id = request.Id, - Limit = request.Limit, - UserId = request.UserId, - ImageTypeLimit = request.ImageTypeLimit - }); + }.GetSimilarItemsResult(request); } - if (item is Series || (program != null && program.IsSeries)) + if (program != null && program.IsSeries) { - return new TvShowsService(_userManager, _userDataManager, _libraryManager, _itemRepo, _dtoService, _tvManager, _authContext) - { - Request = Request, + return GetSimilarItemsResult(request, new[] { typeof(Series).Name }); + } - }.Get(new GetSimilarShows - { - Fields = request.Fields, - Id = request.Id, - Limit = request.Limit, - UserId = request.UserId, - ImageTypeLimit = request.ImageTypeLimit - }); + if (item is Episode || (item is IItemByName && !(item is MusicArtist))) + { + return new QueryResult<BaseItemDto>(); } - return new QueryResult<BaseItemDto>(); + return GetSimilarItemsResult(request, new[] { item.GetType().Name }); + } + + private QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, string[] includeItemTypes) + { + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; + + var item = string.IsNullOrEmpty(request.Id) ? + (!request.UserId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : + _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); + + var dtoOptions = GetDtoOptions(_authContext, request); + + var query = new InternalItemsQuery(user) + { + Limit = request.Limit, + IncludeItemTypes = includeItemTypes, + SimilarTo = item, + DtoOptions = dtoOptions, + EnableTotalRecordCount = false + }; + + // ExcludeArtistIds + if (!string.IsNullOrEmpty(request.ExcludeArtistIds)) + { + query.ExcludeArtistIds = BaseApiService.GetGuids(request.ExcludeArtistIds); + } + + List<BaseItem> itemsResult; + + if (item is MusicArtist) + { + query.IncludeItemTypes = Array.Empty<string>(); + + itemsResult = _libraryManager.GetArtists(query).Items.Select(i => i.Item1).ToList(); + } + else + { + itemsResult = _libraryManager.GetItemList(query); + } + + var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user); + + var result = new QueryResult<BaseItemDto> + { + Items = returnList, + + TotalRecordCount = itemsResult.Count + }; + + return result; } public object Get(GetMediaFolders request) @@ -428,7 +730,7 @@ namespace MediaBrowser.Api.Library Items = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions)).ToArray() }; - return ToOptimizedResult(result); + return result; } public void Post(PostUpdatedSeries request) @@ -443,17 +745,21 @@ namespace MediaBrowser.Api.Library }).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray(); - if (series.Length > 0) + foreach (var item in series) { - foreach (var item in series) + _libraryMonitor.ReportFileSystemChanged(item.Path); + } + } + + public void Post(PostUpdatedMedia request) + { + if (request.Updates != null) + { + foreach (var item in request.Updates) { _libraryMonitor.ReportFileSystemChanged(item.Path); } } - else - { - Task.Run(() => _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None)); - } } public void Post(PostUpdatedMovies request) @@ -481,16 +787,9 @@ namespace MediaBrowser.Api.Library movies = new List<BaseItem>(); } - if (movies.Count > 0) - { - foreach (var item in movies) - { - _libraryMonitor.ReportFileSystemChanged(item.Path); - } - } - else + foreach (var item in movies) { - Task.Run(() => _libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None)); + _libraryMonitor.ReportFileSystemChanged(item.Path); } } @@ -499,7 +798,7 @@ namespace MediaBrowser.Api.Library var item = _libraryManager.GetItemById(request.Id); var auth = _authContext.GetAuthorizationInfo(Request); - var user = _userManager.GetUserById(auth.UserId); + var user = auth.User; if (user != null) { @@ -561,15 +860,6 @@ namespace MediaBrowser.Api.Library public Task<object> Get(GetFile request) { var item = _libraryManager.GetItemById(request.Id); - var locationType = item.LocationType; - if (locationType == LocationType.Remote || locationType == LocationType.Virtual) - { - throw new ArgumentException("This command cannot be used for remote or virtual items."); - } - if (_fileSystem.DirectoryExists(item.Path)) - { - throw new ArgumentException("This command cannot be used for directories."); - } return ResultFactory.GetStaticFileResult(Request, item.Path); } @@ -585,7 +875,7 @@ namespace MediaBrowser.Api.Library .SelectMany(c => c.PhysicalLocations) .ToList(); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -597,7 +887,7 @@ namespace MediaBrowser.Api.Library { var result = GetAncestors(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -611,7 +901,7 @@ namespace MediaBrowser.Api.Library var baseItemDtos = new List<BaseItemDto>(); - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; var dtoOptions = GetDtoOptions(_authContext, request); @@ -636,7 +926,7 @@ namespace MediaBrowser.Api.Library { if (item.GetParent() is AggregateFolder) { - return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path)); + return _libraryManager.GetUserRootFolder().GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path)); } return item; @@ -649,9 +939,7 @@ namespace MediaBrowser.Api.Library /// <returns>System.Object.</returns> public object Get(GetCriticReviews request) { - var result = GetCriticReviews(request); - - return ToOptimizedSerializedResultUsingCache(result); + return new QueryResult<BaseItemDto>(); } /// <summary> @@ -661,7 +949,7 @@ namespace MediaBrowser.Api.Library /// <returns>System.Object.</returns> public object Get(GetItemCounts request) { - var user = string.IsNullOrWhiteSpace(request.UserId) ? null : _userManager.GetUserById(request.UserId); + var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); var counts = new ItemCounts { @@ -677,7 +965,7 @@ namespace MediaBrowser.Api.Library BookCount = GetCount(typeof(Book), user, request) }; - return ToOptimizedSerializedResultUsingCache(counts); + return ToOptimizedResult(counts); } private int GetCount(Type type, User user, GetItemCounts request) @@ -724,14 +1012,14 @@ namespace MediaBrowser.Api.Library public void Delete(DeleteItems request) { var ids = string.IsNullOrWhiteSpace(request.Ids) - ? new string[] { } + ? Array.Empty<string>() : request.Ids.Split(','); - var tasks = ids.Select(i => + foreach (var i in ids) { var item = _libraryManager.GetItemById(i); var auth = _authContext.GetAuthorizationInfo(Request); - var user = _userManager.GetUserById(auth.UserId); + var user = auth.User; if (!item.CanDelete(user)) { @@ -740,17 +1028,15 @@ namespace MediaBrowser.Api.Library throw new SecurityException("Unauthorized access"); } - return Task.FromResult(true); + continue; } - return item.Delete(new DeleteOptions + _libraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = true - }); - }).ToArray(ids.Length); - - Task.WaitAll(tasks); + }, true); + } } /// <summary> @@ -765,36 +1051,6 @@ namespace MediaBrowser.Api.Library }); } - /// <summary> - /// Gets the critic reviews async. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{ItemReviewsResult}.</returns> - private QueryResult<ItemReview> GetCriticReviews(GetCriticReviews request) - { - var reviews = _itemRepo.GetCriticReviews(new Guid(request.Id)); - - var reviewsArray = reviews.ToArray(reviews.Count); - - var result = new QueryResult<ItemReview> - { - TotalRecordCount = reviewsArray.Length - }; - - if (request.StartIndex.HasValue) - { - reviewsArray = reviewsArray.Skip(request.StartIndex.Value).ToArray(); - } - if (request.Limit.HasValue) - { - reviewsArray = reviewsArray.Take(request.Limit.Value).ToArray(); - } - - result.Items = reviewsArray; - - return result; - } - public object Get(GetThemeMedia request) { var themeSongs = GetThemeSongs(new GetThemeSongs @@ -813,7 +1069,7 @@ namespace MediaBrowser.Api.Library }); - return ToOptimizedSerializedResultUsingCache(new AllThemeMediaResult + return ToOptimizedResult(new AllThemeMediaResult { ThemeSongsResult = themeSongs, ThemeVideosResult = themeVideos, @@ -831,16 +1087,16 @@ namespace MediaBrowser.Api.Library { var result = GetThemeSongs(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } private ThemeMediaResult GetThemeSongs(GetThemeSongs request) { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; var item = string.IsNullOrEmpty(request.Id) - ? (!string.IsNullOrWhiteSpace(request.UserId) - ? user.RootFolder + ? (!request.UserId.Equals(Guid.Empty) + ? _libraryManager.GetUserRootFolder() : (Folder)_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); @@ -849,16 +1105,33 @@ namespace MediaBrowser.Api.Library throw new ResourceNotFoundException("Item not found."); } - while (item.ThemeSongIds.Length == 0 && request.InheritFromParent && item.GetParent() != null) + BaseItem[] themeItems = Array.Empty<BaseItem>(); + + while (true) { - item = item.GetParent(); + themeItems = item.GetThemeSongs().ToArray(); + + if (themeItems.Length > 0) + { + break; + } + + if (!request.InheritFromParent) + { + break; + } + + var parent = item.GetParent(); + if (parent == null) + { + break; + } + item = parent; } var dtoOptions = GetDtoOptions(_authContext, request); - var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById) - .Where(i => i != null) - .OrderBy(i => i.SortName) + var dtos = themeItems .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); var items = dtos.ToArray(); @@ -867,7 +1140,7 @@ namespace MediaBrowser.Api.Library { Items = items, TotalRecordCount = items.Length, - OwnerId = _dtoService.GetDtoId(item) + OwnerId = item.Id }; } @@ -880,16 +1153,16 @@ namespace MediaBrowser.Api.Library { var result = GetThemeVideos(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public ThemeMediaResult GetThemeVideos(GetThemeVideos request) { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; var item = string.IsNullOrEmpty(request.Id) - ? (!string.IsNullOrWhiteSpace(request.UserId) - ? user.RootFolder + ? (!request.UserId.Equals(Guid.Empty) + ? _libraryManager.GetUserRootFolder() : (Folder)_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); @@ -898,16 +1171,33 @@ namespace MediaBrowser.Api.Library throw new ResourceNotFoundException("Item not found."); } - while (item.ThemeVideoIds.Length == 0 && request.InheritFromParent && item.GetParent() != null) + BaseItem[] themeItems = Array.Empty<BaseItem>(); + + while (true) { - item = item.GetParent(); + themeItems = item.GetThemeVideos().ToArray(); + + if (themeItems.Length > 0) + { + break; + } + + if (!request.InheritFromParent) + { + break; + } + + var parent = item.GetParent(); + if (parent == null) + { + break; + } + item = parent; } var dtoOptions = GetDtoOptions(_authContext, request); - var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById) - .Where(i => i != null) - .OrderBy(i => i.SortName) + var dtos = themeItems .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); var items = dtos.ToArray(); @@ -916,43 +1206,8 @@ namespace MediaBrowser.Api.Library { Items = items, TotalRecordCount = items.Length, - OwnerId = _dtoService.GetDtoId(item) + OwnerId = item.Id }; } - - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - public object Get(GetYearIndex request) - { - var includeTypes = string.IsNullOrWhiteSpace(request.IncludeItemTypes) - ? new string[] { } - : request.IncludeItemTypes.Split(','); - - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - - var query = new InternalItemsQuery(user) - { - IncludeItemTypes = includeTypes, - Recursive = true, - DtoOptions = new DtoOptions(false) - { - EnableImages = false - } - }; - - var items = _libraryManager.GetItemList(query); - - var lookup = items - .ToLookup(i => i.ProductionYear ?? -1) - .OrderBy(i => i.Key) - .Select(i => new ItemIndex - { - ItemCount = i.Count(), - Name = i.Key == -1 ? string.Empty : i.Key.ToString(_usCulture) - }) - .ToList(); - - return ToOptimizedSerializedResultUsingCache(lookup); - } } } diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs index ae488f066c..bba89acec6 100644 --- a/MediaBrowser.Api/Library/LibraryStructureService.cs +++ b/MediaBrowser.Api/Library/LibraryStructureService.cs @@ -210,7 +210,7 @@ namespace MediaBrowser.Api.Library { var result = _libraryManager.GetVirtualFolders(true); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public void Post(UpdateLibraryOptions request) @@ -224,7 +224,7 @@ namespace MediaBrowser.Api.Library /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(AddVirtualFolder request) + public Task Post(AddVirtualFolder request) { var libraryOptions = request.LibraryOptions ?? new LibraryOptions(); @@ -233,7 +233,7 @@ namespace MediaBrowser.Api.Library libraryOptions.PathInfos = request.Paths.Select(i => new MediaPathInfo { Path = i }).ToArray(); } - _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary); + return _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary); } /// <summary> @@ -264,27 +264,27 @@ namespace MediaBrowser.Api.Library if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && _fileSystem.DirectoryExists(newPath)) { - throw new ArgumentException("There is already a media collection with the name " + newPath + "."); + throw new ArgumentException("Media library already exists at " + newPath + "."); } _libraryMonitor.Stop(); try { - // Only make a two-phase move when changing capitalization + // Changing capitalization. Handle windows case insensitivity if (string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase)) { - //Create an unique name - var temporaryName = Guid.NewGuid().ToString(); - var temporaryPath = Path.Combine(rootFolderPath, temporaryName); - _fileSystem.MoveDirectory(currentPath, temporaryPath); - currentPath = temporaryPath; + var tempPath = Path.Combine(rootFolderPath, Guid.NewGuid().ToString("N")); + _fileSystem.MoveDirectory(currentPath, tempPath); + currentPath = tempPath; } _fileSystem.MoveDirectory(currentPath, newPath); } finally { + CollectionFolder.OnCollectionFolderChange(); + Task.Run(() => { // No need to start if scanning the library because it will handle it @@ -309,9 +309,9 @@ namespace MediaBrowser.Api.Library /// Deletes the specified request. /// </summary> /// <param name="request">The request.</param> - public void Delete(RemoveVirtualFolder request) + public Task Delete(RemoveVirtualFolder request) { - _libraryManager.RemoveVirtualFolder(request.Name, request.RefreshLibrary); + return _libraryManager.RemoveVirtualFolder(request.Name, request.RefreshLibrary); } /// <summary> diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index fee52ea5ee..510c5f135b 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -17,12 +17,15 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Api.UserLibrary; using MediaBrowser.Model.IO; - +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Cryptography; +using System.Text; +using MediaBrowser.Controller.Entities; namespace MediaBrowser.Api.LiveTv { @@ -37,13 +40,13 @@ namespace MediaBrowser.Api.LiveTv [Route("/LiveTv/Channels", "GET", Summary = "Gets available live tv channels.")] [Authenticated] - public class GetChannels : IReturn<QueryResult<ChannelInfoDto>>, IHasDtoOptions + public class GetChannels : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions { [ApiMember(Name = "Type", Description = "Optional filter by channel type.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public ChannelType? Type { get; set; } [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Skips over a given number of items within the results. Use for paging. @@ -122,7 +125,7 @@ namespace MediaBrowser.Api.LiveTv if (string.IsNullOrEmpty(val)) { - return new string[] { }; + return Array.Empty<string>(); } return val.Split(','); @@ -136,7 +139,7 @@ namespace MediaBrowser.Api.LiveTv [Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")] [Authenticated] - public class GetChannel : IReturn<ChannelInfoDto> + public class GetChannel : IReturn<BaseItemDto> { /// <summary> /// Gets or sets the id. @@ -146,7 +149,7 @@ namespace MediaBrowser.Api.LiveTv public string Id { get; set; } [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } [Route("/LiveTv/Recordings", "GET", Summary = "Gets live tv recordings")] @@ -157,10 +160,7 @@ namespace MediaBrowser.Api.LiveTv public string ChannelId { get; set; } [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } - - [ApiMember(Name = "GroupId", Description = "Optional filter by recording group.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string GroupId { get; set; } + public Guid UserId { get; set; } [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? StartIndex { get; set; } @@ -274,6 +274,14 @@ namespace MediaBrowser.Api.LiveTv public string UserId { get; set; } } + [Route("/LiveTv/Recordings/Folders", "GET", Summary = "Gets recording folders")] + [Authenticated] + public class GetRecordingFolders : IReturn<BaseItemDto[]> + { + [ApiMember(Name = "UserId", Description = "Optional filter by user and attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid UserId { get; set; } + } + [Route("/LiveTv/Recordings/{Id}", "GET", Summary = "Gets a live tv recording")] [Authenticated] public class GetRecording : IReturn<BaseItemDto> @@ -282,7 +290,7 @@ namespace MediaBrowser.Api.LiveTv public string Id { get; set; } [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } [Route("/LiveTv/Tuners/{Id}/Reset", "POST", Summary = "Resets a tv tuner")] @@ -332,13 +340,14 @@ namespace MediaBrowser.Api.LiveTv public string ChannelIds { get; set; } [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string UserId { get; set; } + public Guid UserId { get; set; } [ApiMember(Name = "MinStartDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] public string MinStartDate { get; set; } [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")] public string MaxStartDate { get; set; } @@ -397,7 +406,7 @@ namespace MediaBrowser.Api.LiveTv public bool? EnableUserData { get; set; } public string SeriesTimerId { get; set; } - public string LibrarySeriesId { get; set; } + public Guid LibrarySeriesId { get; set; } /// <summary> /// Fields to return within the items, in addition to basic information @@ -424,7 +433,7 @@ namespace MediaBrowser.Api.LiveTv } [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string UserId { get; set; } + public Guid UserId { get; set; } [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; } @@ -481,7 +490,7 @@ namespace MediaBrowser.Api.LiveTv public string Id { get; set; } [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } @@ -489,15 +498,15 @@ namespace MediaBrowser.Api.LiveTv [Authenticated] public class DeleteRecording : IReturnVoid { - [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } + [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + public Guid Id { get; set; } } [Route("/LiveTv/Timers/{Id}", "DELETE", Summary = "Cancels a live tv timer")] [Authenticated] public class CancelTimer : IReturnVoid { - [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Id { get; set; } } @@ -536,7 +545,7 @@ namespace MediaBrowser.Api.LiveTv [Authenticated] public class CancelSeriesTimer : IReturnVoid { - [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Id { get; set; } } @@ -566,14 +575,6 @@ namespace MediaBrowser.Api.LiveTv { } - [Route("/LiveTv/Folder", "GET", Summary = "Gets the users live tv folder, along with configured images")] - [Authenticated] - public class GetLiveTvFolder : IReturn<BaseItemDto> - { - [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } - } - [Route("/LiveTv/TunerHosts", "POST", Summary = "Adds a tuner host")] [Authenticated] public class AddTunerHost : TunerHostInfo, IReturn<TunerHostInfo> @@ -600,6 +601,7 @@ namespace MediaBrowser.Api.LiveTv { public bool ValidateLogin { get; set; } public bool ValidateListings { get; set; } + public string Pw { get; set; } } [Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")] @@ -637,7 +639,7 @@ namespace MediaBrowser.Api.LiveTv [Authenticated] public class GetChannelMappingOptions { - [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")] public string ProviderId { get; set; } } @@ -645,7 +647,7 @@ namespace MediaBrowser.Api.LiveTv [Authenticated] public class SetChannelMapping { - [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + [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; } @@ -659,14 +661,6 @@ namespace MediaBrowser.Api.LiveTv public string ProviderName { get; set; } } - [Route("/LiveTv/Registration", "GET")] - [Authenticated] - public class GetLiveTvRegistrationInfo : IReturn<MBRegistrationRecord> - { - [ApiMember(Name = "Feature", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Feature { get; set; } - } - [Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")] public class GetLiveStreamFile { @@ -706,8 +700,11 @@ namespace MediaBrowser.Api.LiveTv private readonly IAuthorizationContext _authContext; private readonly ISessionContext _sessionContext; private readonly IEnvironmentInfo _environment; + private ICryptoProvider _cryptographyProvider; + private IStreamHelper _streamHelper; + private IMediaSourceManager _mediaSourceManager; - public LiveTvService(ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext, IEnvironmentInfo environment) + public LiveTvService(ICryptoProvider crypto, IMediaSourceManager mediaSourceManager, IStreamHelper streamHelper, ILiveTvManager liveTvManager, IUserManager userManager, IServerConfigurationManager config, IHttpClient httpClient, ILibraryManager libraryManager, IDtoService dtoService, IFileSystem fileSystem, IAuthorizationContext authContext, ISessionContext sessionContext, IEnvironmentInfo environment) { _liveTvManager = liveTvManager; _userManager = userManager; @@ -719,6 +716,9 @@ namespace MediaBrowser.Api.LiveTv _authContext = authContext; _sessionContext = sessionContext; _environment = environment; + _cryptographyProvider = crypto; + _streamHelper = streamHelper; + _mediaSourceManager = mediaSourceManager; } public object Get(GetTunerHostTypes request) @@ -727,6 +727,22 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(list); } + public object Get(GetRecordingFolders request) + { + var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); + var folders = _liveTvManager.GetRecordingFolders(user); + + var returnArray = _dtoService.GetBaseItemDtos(folders.ToArray(), new DtoOptions(), user); + + var result = new QueryResult<BaseItemDto> + { + Items = returnArray, + TotalRecordCount = returnArray.Length + }; + + return ToOptimizedResult(result); + } + public object Get(GetLiveRecordingFile request) { var path = _liveTvManager.GetEmbyTvActiveRecordingPath(request.Id); @@ -740,7 +756,7 @@ namespace MediaBrowser.Api.LiveTv outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path); - return new ProgressiveFileCopier(_fileSystem, path, outputHeaders, Logger, _environment) + return new ProgressiveFileCopier(_fileSystem, _streamHelper, path, outputHeaders, Logger, _environment) { AllowEndOfFile = false }; @@ -754,12 +770,15 @@ namespace MediaBrowser.Api.LiveTv public async Task<object> Get(GetLiveStreamFile request) { - var directStreamProvider = (await _liveTvManager.GetEmbyTvLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider; + var liveStreamInfo = await _mediaSourceManager.GetDirectStreamProviderByUniqueId(request.Id, CancellationToken.None).ConfigureAwait(false); + + var directStreamProvider = liveStreamInfo; + var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container); - return new ProgressiveFileCopier(directStreamProvider, outputHeaders, Logger, _environment) + return new ProgressiveFileCopier(directStreamProvider, _streamHelper, outputHeaders, Logger, _environment) { AllowEndOfFile = false }; @@ -770,13 +789,6 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(new ListingsProviderInfo()); } - public async Task<object> Get(GetLiveTvRegistrationInfo request) - { - var result = await _liveTvManager.GetRegistrationInfo(request.Feature).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - public async Task<object> Post(SetChannelMapping request) { return await _liveTvManager.SetChannelMapping(request.ProviderId, request.TunerChannelId, request.ProviderChannelId).ConfigureAwait(false); @@ -828,12 +840,12 @@ namespace MediaBrowser.Api.LiveTv }).ConfigureAwait(false); - return ResultFactory.GetResult(response, "application/json"); + return ResultFactory.GetResult(Request, response, "application/json"); } private void AssertUserCanManageLiveTv() { - var user = _sessionContext.GetUser(Request).Result; + var user = _sessionContext.GetUser(Request); if (user == null) { @@ -848,10 +860,26 @@ namespace MediaBrowser.Api.LiveTv public async Task<object> Post(AddListingProvider request) { + if (request.Pw != null) + { + request.Password = GetHashedString(request.Pw); + } + + request.Pw = null; + var result = await _liveTvManager.SaveListingProvider(request, request.ValidateLogin, request.ValidateListings).ConfigureAwait(false); return ToOptimizedResult(result); } + /// <summary> + /// Gets the hashed string. + /// </summary> + private string GetHashedString(string str) + { + // legacy + return BitConverter.ToString(_cryptographyProvider.ComputeSHA1(Encoding.UTF8.GetBytes(str))).Replace("-", string.Empty).ToLower(); + } + public void Delete(DeleteListingProvider request) { _liveTvManager.DeleteListingsProvider(request.Id); @@ -859,8 +887,6 @@ namespace MediaBrowser.Api.LiveTv public async Task<object> Post(AddTunerHost request) { - request.EnableNewHdhrChannelIds = true; - var result = await _liveTvManager.SaveTunerHost(request).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -888,14 +914,14 @@ namespace MediaBrowser.Api.LiveTv { var info = await _liveTvManager.GetLineups(request.Type, request.Id, request.Country, request.Location).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(info); + return ToOptimizedResult(info); } - public async Task<object> Get(GetLiveTvInfo request) + public object Get(GetLiveTvInfo request) { - var info = await _liveTvManager.GetLiveTvInfo(CancellationToken.None).ConfigureAwait(false); + var info = _liveTvManager.GetLiveTvInfo(CancellationToken.None); - return ToOptimizedSerializedResultUsingCache(info); + return ToOptimizedResult(info); } public object Get(GetChannels request) @@ -923,7 +949,7 @@ namespace MediaBrowser.Api.LiveTv }, options, CancellationToken.None); - var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId); + var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); RemoveFields(options); @@ -937,7 +963,7 @@ namespace MediaBrowser.Api.LiveTv TotalRecordCount = channelResult.TotalRecordCount }; - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } private void RemoveFields(DtoOptions options) @@ -955,27 +981,24 @@ namespace MediaBrowser.Api.LiveTv { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); + var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); var dtoOptions = GetDtoOptions(_authContext, request); var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); - return ToOptimizedSerializedResultUsingCache(result); - } - - public object Get(GetLiveTvFolder request) - { - return ToOptimizedResult(_liveTvManager.GetLiveTvFolder(request.UserId, CancellationToken.None)); + return ToOptimizedResult(result); } public async Task<object> Get(GetPrograms request) { - var query = new ProgramQuery + var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); + + var query = new InternalItemsQuery(user) { - ChannelIds = ApiEntryPoint.Split(request.ChannelIds, ',', true), - UserId = request.UserId, + ChannelIds = ApiEntryPoint.Split(request.ChannelIds, ',', true).Select(i => new Guid(i)).ToArray(), HasAired = request.HasAired, + IsAiring = request.IsAiring, EnableTotalRecordCount = request.EnableTotalRecordCount }; @@ -1009,9 +1032,9 @@ namespace MediaBrowser.Api.LiveTv query.IsSports = request.IsSports; query.SeriesTimerId = request.SeriesTimerId; query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - query.GenreIds = (request.GenreIds ?? String.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + query.GenreIds = GetGuids(request.GenreIds); - if (!string.IsNullOrWhiteSpace(request.LibrarySeriesId)) + if (!request.LibrarySeriesId.Equals(Guid.Empty)) { query.IsSeries = true; @@ -1029,9 +1052,10 @@ namespace MediaBrowser.Api.LiveTv public object Get(GetRecommendedPrograms request) { - var query = new RecommendedProgramQuery + var user = _userManager.GetUserById(request.UserId); + + var query = new InternalItemsQuery(user) { - UserId = request.UserId, IsAiring = request.IsAiring, Limit = request.Limit, HasAired = request.HasAired, @@ -1043,7 +1067,7 @@ namespace MediaBrowser.Api.LiveTv EnableTotalRecordCount = request.EnableTotalRecordCount }; - query.GenreIds = (request.GenreIds ?? String.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + query.GenreIds = GetGuids(request.GenreIds); var result = _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None); @@ -1055,16 +1079,14 @@ namespace MediaBrowser.Api.LiveTv return Get(request); } - public async Task<object> Get(GetRecordings request) + public object Get(GetRecordings request) { var options = GetDtoOptions(_authContext, request); - options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; - var result = await _liveTvManager.GetRecordings(new RecordingQuery + var result = _liveTvManager.GetRecordings(new RecordingQuery { ChannelId = request.ChannelId, UserId = request.UserId, - GroupId = request.GroupId, StartIndex = request.StartIndex, Limit = request.Limit, Status = request.Status, @@ -1076,53 +1098,39 @@ namespace MediaBrowser.Api.LiveTv IsSeries = request.IsSeries, IsKids = request.IsKids, IsSports = request.IsSports, - IsLibraryItem = request.IsLibraryItem + IsLibraryItem = request.IsLibraryItem, + Fields = request.GetItemFields(), + ImageTypeLimit = request.ImageTypeLimit, + EnableImages = request.EnableImages - }, options, CancellationToken.None).ConfigureAwait(false); + }, options); return ToOptimizedResult(result); } public object Get(GetRecordingSeries request) { - var options = GetDtoOptions(_authContext, request); - options.DeviceId = _authContext.GetAuthorizationInfo(Request).DeviceId; - - var result = _liveTvManager.GetRecordingSeries(new RecordingQuery - { - ChannelId = request.ChannelId, - UserId = request.UserId, - GroupId = request.GroupId, - StartIndex = request.StartIndex, - Limit = request.Limit, - Status = request.Status, - SeriesTimerId = request.SeriesTimerId, - IsInProgress = request.IsInProgress, - EnableTotalRecordCount = request.EnableTotalRecordCount - - }, options, CancellationToken.None); - - return ToOptimizedResult(result); + return ToOptimizedResult(new QueryResult<BaseItemDto>()); } - public async Task<object> Get(GetRecording request) + public object Get(GetRecording request) { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); + var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); var dtoOptions = GetDtoOptions(_authContext, request); var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public async Task<object> Get(GetTimer request) { var result = await _liveTvManager.GetTimer(request.Id, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public async Task<object> Get(GetTimers request) @@ -1136,34 +1144,31 @@ namespace MediaBrowser.Api.LiveTv }, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public void Delete(DeleteRecording request) { AssertUserCanManageLiveTv(); - var task = _liveTvManager.DeleteRecording(request.Id); - - Task.WaitAll(task); + _libraryManager.DeleteItem(_libraryManager.GetItemById(request.Id), new DeleteOptions + { + DeleteFileLocation = false + }); } - public void Delete(CancelTimer request) + public Task Delete(CancelTimer request) { AssertUserCanManageLiveTv(); - var task = _liveTvManager.CancelTimer(request.Id); - - Task.WaitAll(task); + return _liveTvManager.CancelTimer(request.Id); } - public void Post(UpdateTimer request) + public Task Post(UpdateTimer request) { AssertUserCanManageLiveTv(); - var task = _liveTvManager.UpdateTimer(request, CancellationToken.None); - - Task.WaitAll(task); + return _liveTvManager.UpdateTimer(request, CancellationToken.None); } public async Task<object> Get(GetSeriesTimers request) @@ -1175,32 +1180,28 @@ namespace MediaBrowser.Api.LiveTv }, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public async Task<object> Get(GetSeriesTimer request) { var result = await _liveTvManager.GetSeriesTimer(request.Id, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } - public void Delete(CancelSeriesTimer request) + public Task Delete(CancelSeriesTimer request) { AssertUserCanManageLiveTv(); - var task = _liveTvManager.CancelSeriesTimer(request.Id); - - Task.WaitAll(task); + return _liveTvManager.CancelSeriesTimer(request.Id); } - public void Post(UpdateSeriesTimer request) + public Task Post(UpdateSeriesTimer request) { AssertUserCanManageLiveTv(); - var task = _liveTvManager.UpdateSeriesTimer(request, CancellationToken.None); - - Task.WaitAll(task); + return _liveTvManager.UpdateSeriesTimer(request, CancellationToken.None); } public async Task<object> Get(GetDefaultTimer request) @@ -1209,61 +1210,47 @@ namespace MediaBrowser.Api.LiveTv { var result = await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } else { var result = await _liveTvManager.GetNewTimerDefaults(request.ProgramId, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } } public async Task<object> Get(GetProgram request) { - var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId); + var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); var result = await _liveTvManager.GetProgram(request.Id, CancellationToken.None, user).ConfigureAwait(false); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } - public void Post(CreateSeriesTimer request) + public Task Post(CreateSeriesTimer request) { AssertUserCanManageLiveTv(); - var task = _liveTvManager.CreateSeriesTimer(request, CancellationToken.None); - - Task.WaitAll(task); + return _liveTvManager.CreateSeriesTimer(request, CancellationToken.None); } - public void Post(CreateTimer request) + public Task Post(CreateTimer request) { AssertUserCanManageLiveTv(); - var task = _liveTvManager.CreateTimer(request, CancellationToken.None); - - Task.WaitAll(task); + return _liveTvManager.CreateTimer(request, CancellationToken.None); } - public async Task<object> Get(GetRecordingGroups request) + public object Get(GetRecordingGroups request) { - var result = await _liveTvManager.GetRecordingGroups(new RecordingGroupQuery - { - UserId = request.UserId - - }, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(new QueryResult<BaseItemDto>()); } - public async Task<object> Get(GetRecordingGroup request) + public object Get(GetRecordingGroup request) { - var result = await _liveTvManager.GetRecordingGroups(new RecordingGroupQuery(), CancellationToken.None).ConfigureAwait(false); - - var group = result.Items.FirstOrDefault(i => string.Equals(i.Id, request.Id, StringComparison.OrdinalIgnoreCase)); - - return ToOptimizedSerializedResultUsingCache(group); + throw new FileNotFoundException(); } public object Get(GetGuideInfo request) @@ -1271,13 +1258,11 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(_liveTvManager.GetGuideInfo()); } - public void Post(ResetTuner request) + public Task Post(ResetTuner request) { AssertUserCanManageLiveTv(); - var task = _liveTvManager.ResetTuner(request.Id, CancellationToken.None); - - Task.WaitAll(task); + return _liveTvManager.ResetTuner(request.Id, CancellationToken.None); } } }
\ No newline at end of file diff --git a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs index 74293ccd90..0a6ccd4c5c 100644 --- a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs +++ b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs @@ -8,6 +8,7 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; using MediaBrowser.Model.System; +using MediaBrowser.Controller.IO; namespace MediaBrowser.Api.LiveTv { @@ -20,28 +21,30 @@ namespace MediaBrowser.Api.LiveTv const int StreamCopyToBufferSize = 81920; - private long _bytesWritten = 0; public long StartPosition { get; set; } public bool AllowEndOfFile = true; private readonly IDirectStreamProvider _directStreamProvider; private readonly IEnvironmentInfo _environment; + private IStreamHelper _streamHelper; - public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment) + public ProgressiveFileCopier(IFileSystem fileSystem, IStreamHelper streamHelper, string path, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment) { _fileSystem = fileSystem; _path = path; _outputHeaders = outputHeaders; _logger = logger; _environment = environment; + _streamHelper = streamHelper; } - public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment) + public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, IStreamHelper streamHelper, Dictionary<string, string> outputHeaders, ILogger logger, IEnvironmentInfo environment) { _directStreamProvider = directStreamProvider; _outputHeaders = outputHeaders; _logger = logger; _environment = environment; + _streamHelper = streamHelper; } public IDictionary<string, string> Headers @@ -75,7 +78,7 @@ namespace MediaBrowser.Api.LiveTv var eofCount = 0; // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 - var allowAsyncFileRead = _environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows; + var allowAsyncFileRead = true; using (var inputStream = GetInputStream(allowAsyncFileRead)) { @@ -89,14 +92,7 @@ namespace MediaBrowser.Api.LiveTv while (eofCount < emptyReadLimit) { int bytesRead; - if (allowAsyncFileRead) - { - bytesRead = await CopyToInternalAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false); - } - else - { - bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false); - } + bytesRead = await _streamHelper.CopyToAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false); //var position = fs.Position; //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); @@ -113,49 +109,5 @@ namespace MediaBrowser.Api.LiveTv } } } - - private async Task<int> CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken) - { - var array = new byte[StreamCopyToBufferSize]; - int bytesRead; - int totalBytesRead = 0; - - while ((bytesRead = source.Read(array, 0, array.Length)) != 0) - { - var bytesToWrite = bytesRead; - - if (bytesToWrite > 0) - { - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); - - _bytesWritten += bytesRead; - totalBytesRead += bytesRead; - } - } - - return totalBytesRead; - } - - private async Task<int> CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken) - { - var array = new byte[StreamCopyToBufferSize]; - int bytesRead; - int totalBytesRead = 0; - - while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) - { - var bytesToWrite = bytesRead; - - if (bytesToWrite > 0) - { - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false); - - _bytesWritten += bytesRead; - totalBytesRead += bytesRead; - } - } - - return totalBytesRead; - } } } diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 650306ea60..e4c9a0c351 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -1,136 +1,17 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> - <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProjectGuid>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</ProjectGuid> - <OutputType>Library</OutputType> - <AppDesignerFolder>Properties</AppDesignerFolder> - <RootNamespace>MediaBrowser.Api</RootNamespace> - <AssemblyName>MediaBrowser.Api</AssemblyName> - <FileAlignment>512</FileAlignment> - <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> - <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> - <TargetFrameworkProfile>Profile7</TargetFrameworkProfile> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <PlatformTarget>AnyCPU</PlatformTarget> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>none</DebugType> - <Optimize>true</Optimize> - <OutputPath>bin\Release\</OutputPath> - <DefineConstants>TRACE</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - </PropertyGroup> - <PropertyGroup> - <RunPostBuildEvent>Always</RunPostBuildEvent> - </PropertyGroup> +<Project Sdk="Microsoft.NET.Sdk"> + <ItemGroup> - <Compile Include="..\SharedVersion.cs"> - <Link>Properties\SharedVersion.cs</Link> - </Compile> - <Compile Include="BrandingService.cs" /> - <Compile Include="ChannelService.cs" /> - <Compile Include="Devices\DeviceService.cs" /> - <Compile Include="Dlna\DlnaServerService.cs" /> - <Compile Include="Dlna\DlnaService.cs" /> - <Compile Include="FilterService.cs" /> - <Compile Include="IHasDtoOptions.cs" /> - <Compile Include="LiveTv\ProgressiveFileCopier.cs" /> - <Compile Include="PlaylistService.cs" /> - <Compile Include="Social\SharingService.cs" /> - <Compile Include="StartupWizardService.cs" /> - <Compile Include="Subtitles\SubtitleService.cs" /> - <Compile Include="Movies\CollectionService.cs" /> - <Compile Include="Music\AlbumsService.cs" /> - <Compile Include="BaseApiService.cs" /> - <Compile Include="ConfigurationService.cs" /> - <Compile Include="DisplayPreferencesService.cs" /> - <Compile Include="EnvironmentService.cs" /> - <Compile Include="GamesService.cs" /> - <Compile Include="IHasItemFields.cs" /> - <Compile Include="Images\ImageByNameService.cs" /> - <Compile Include="Images\ImageRequest.cs" /> - <Compile Include="Images\ImageService.cs" /> - <Compile Include="Music\InstantMixService.cs" /> - <Compile Include="ItemLookupService.cs" /> - <Compile Include="ItemRefreshService.cs" /> - <Compile Include="ItemUpdateService.cs" /> - <Compile Include="Library\LibraryService.cs" /> - <Compile Include="Library\LibraryStructureService.cs" /> - <Compile Include="LiveTv\LiveTvService.cs" /> - <Compile Include="LocalizationService.cs" /> - <Compile Include="Movies\MoviesService.cs" /> - <Compile Include="NewsService.cs" /> - <Compile Include="NotificationsService.cs" /> - <Compile Include="PackageService.cs" /> - <Compile Include="PluginService.cs" /> - <Compile Include="Images\RemoteImageService.cs" /> - <Compile Include="ScheduledTasks\ScheduledTaskService.cs" /> - <Compile Include="ScheduledTasks\ScheduledTasksWebSocketListener.cs" /> - <Compile Include="ApiEntryPoint.cs" /> - <Compile Include="SearchService.cs" /> - <Compile Include="Session\SessionsService.cs" /> - <Compile Include="SimilarItemsHelper.cs" /> - <Compile Include="SuggestionsService.cs" /> - <Compile Include="System\ActivityLogService.cs" /> - <Compile Include="System\ActivityLogWebSocketListener.cs" /> - <Compile Include="System\SystemService.cs" /> - <Compile Include="Movies\TrailersService.cs" /> - <Compile Include="TvShowsService.cs" /> - <Compile Include="UserLibrary\ArtistsService.cs" /> - <Compile Include="UserLibrary\BaseItemsByNameService.cs" /> - <Compile Include="UserLibrary\BaseItemsRequest.cs" /> - <Compile Include="UserLibrary\GameGenresService.cs" /> - <Compile Include="UserLibrary\GenresService.cs" /> - <Compile Include="UserLibrary\ItemsService.cs" /> - <Compile Include="UserLibrary\MusicGenresService.cs" /> - <Compile Include="UserLibrary\PersonsService.cs" /> - <Compile Include="UserLibrary\StudiosService.cs" /> - <Compile Include="UserLibrary\UserLibraryService.cs" /> - <Compile Include="UserLibrary\UserViewsService.cs" /> - <Compile Include="UserLibrary\YearsService.cs" /> - <Compile Include="UserService.cs" /> - <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="VideosService.cs" /> - <Compile Include="Session\SessionInfoWebSocketListener.cs" /> - <Compile Include="System\SystemInfoWebSocketListener.cs" /> + <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> + <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> </ItemGroup> + <ItemGroup> - <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj"> - <Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project> - <Name>MediaBrowser.Common</Name> - </ProjectReference> - <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj"> - <Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project> - <Name>MediaBrowser.Controller</Name> - </ProjectReference> - <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj"> - <Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project> - <Name>MediaBrowser.Model</Name> - </ProjectReference> + <Compile Include="..\SharedVersion.cs"/> </ItemGroup> - <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> + <PropertyGroup> - <PostBuildEvent> - </PostBuildEvent> + <TargetFramework>netcoreapp2.1</TargetFramework> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> </PropertyGroup> - <!-- To modify your build process, add your task inside one of the targets below and uncomment it. - Other similar extension points exist, see Microsoft.Common.targets. - <Target Name="BeforeBuild"> - </Target> - <Target Name="AfterBuild"> - </Target> - --> -</Project>
\ No newline at end of file + +</Project> diff --git a/MediaBrowser.Api/MediaBrowser.Api.nuget.targets b/MediaBrowser.Api/MediaBrowser.Api.nuget.targets deleted file mode 100644 index e69ce0e64f..0000000000 --- a/MediaBrowser.Api/MediaBrowser.Api.nuget.targets +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?> -<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Target Name="EmitMSBuildWarning" BeforeTargets="Build"> - <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." /> - </Target> -</Project>
\ No newline at end of file diff --git a/MediaBrowser.Api/Movies/CollectionService.cs b/MediaBrowser.Api/Movies/CollectionService.cs index c63712f4cc..9adefdf755 100644 --- a/MediaBrowser.Api/Movies/CollectionService.cs +++ b/MediaBrowser.Api/Movies/CollectionService.cs @@ -3,8 +3,6 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Collections; using System; -using System.Collections.Generic; -using System.Threading.Tasks; using MediaBrowser.Model.Services; namespace MediaBrowser.Api.Movies @@ -38,7 +36,7 @@ namespace MediaBrowser.Api.Movies [Route("/Collections/{Id}/Items", "DELETE", Summary = "Removes items from a collection")] public class RemoveFromCollection : IReturnVoid { - [ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] public string Ids { get; set; } [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] @@ -59,44 +57,40 @@ namespace MediaBrowser.Api.Movies _authContext = authContext; } - public async Task<object> Post(CreateCollection request) + public object Post(CreateCollection request) { var userId = _authContext.GetAuthorizationInfo(Request).UserId; var parentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId); - var item = await _collectionManager.CreateCollection(new CollectionCreationOptions + var item = _collectionManager.CreateCollection(new CollectionCreationOptions { IsLocked = request.IsLocked, Name = request.Name, ParentId = parentId, ItemIdList = SplitValue(request.Ids, ','), - UserIds = new string[] { userId } + UserIds = new [] { userId } - }).ConfigureAwait(false); + }); var dtoOptions = GetDtoOptions(_authContext, request); var dto = _dtoService.GetBaseItemDto(item, dtoOptions); - return ToOptimizedResult(new CollectionCreationResult + return new CollectionCreationResult { Id = dto.Id - }); + }; } public void Post(AddToCollection request) { - var task = _collectionManager.AddToCollection(new Guid(request.Id), SplitValue(request.Ids, ',')); - - Task.WaitAll(task); + _collectionManager.AddToCollection(new Guid(request.Id), SplitValue(request.Ids, ',')); } public void Delete(RemoveFromCollection request) { - var task = _collectionManager.RemoveFromCollection(new Guid(request.Id), SplitValue(request.Ids, ',')); - - Task.WaitAll(task); + _collectionManager.RemoveFromCollection(new Guid(request.Id), SplitValue(request.Ids, ',')); } } } diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index 254c93b33a..7112806011 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -19,22 +19,6 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Api.Movies { - /// <summary> - /// Class GetSimilarMovies - /// </summary> - [Route("/Movies/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given movie.")] - public class GetSimilarMovies : BaseGetSimilarItemsFromItem - { - } - - /// <summary> - /// Class GetSimilarTrailers - /// </summary> - [Route("/Trailers/{Id}/Similar", "GET", Summary = "Finds movies and trailers similar to a given trailer.")] - public class GetSimilarTrailers : BaseGetSimilarItemsFromItem - { - } - [Route("/Movies/Recommendations", "GET", Summary = "Gets movie recommendations")] public class GetMovieRecommendations : IReturn<RecommendationDto[]>, IHasDtoOptions { @@ -49,7 +33,7 @@ namespace MediaBrowser.Api.Movies /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Specify this to localize the search to a specific item or folder. Omit to use the root. @@ -108,25 +92,6 @@ namespace MediaBrowser.Api.Movies _authContext = authContext; } - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetSimilarMovies request) - { - var result = GetSimilarItemsResult(request); - - return ToOptimizedSerializedResultUsingCache(result); - } - - public object Get(GetSimilarTrailers request) - { - var result = GetSimilarItemsResult(request); - - return ToOptimizedSerializedResultUsingCache(result); - } - public object Get(GetMovieRecommendations request) { var user = _userManager.GetUserById(request.UserId); @@ -138,12 +103,12 @@ namespace MediaBrowser.Api.Movies return ToOptimizedResult(result); } - private QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request) + public QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request) { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; var item = string.IsNullOrEmpty(request.Id) ? - (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : + (!request.UserId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); var itemTypes = new List<string> { typeof(Movie).Name }; @@ -182,7 +147,7 @@ namespace MediaBrowser.Api.Movies { var categories = new List<RecommendationDto>(); - var parentIdGuid = string.IsNullOrWhiteSpace(parentId) ? (Guid?)null : new Guid(parentId); + var parentIdGuid = string.IsNullOrWhiteSpace(parentId) ? Guid.Empty : new Guid(parentId); var query = new InternalItemsQuery(user) { @@ -193,7 +158,7 @@ namespace MediaBrowser.Api.Movies //typeof(LiveTvProgram).Name }, // IsMovie = true - OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), + OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), Limit = 7, ParentId = parentIdGuid, Recursive = true, @@ -214,10 +179,10 @@ namespace MediaBrowser.Api.Movies { IncludeItemTypes = itemTypes.ToArray(itemTypes.Count), IsMovie = true, - OrderBy = new[] { ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), + OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), Limit = 10, IsFavoriteOrLiked = true, - ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id.ToString("N")).ToArray(recentlyPlayedMovies.Count), + ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id).ToArray(recentlyPlayedMovies.Count), EnableGroupByMetadataKey = true, ParentId = parentIdGuid, Recursive = true, @@ -316,7 +281,7 @@ namespace MediaBrowser.Api.Movies yield return new RecommendationDto { BaselineItemName = name, - CategoryId = name.GetMD5().ToString("N"), + CategoryId = name.GetMD5(), RecommendationType = type, Items = returnItems }; @@ -356,7 +321,7 @@ namespace MediaBrowser.Api.Movies yield return new RecommendationDto { BaselineItemName = name, - CategoryId = name.GetMD5().ToString("N"), + CategoryId = name.GetMD5(), RecommendationType = type, Items = returnItems }; @@ -393,7 +358,7 @@ namespace MediaBrowser.Api.Movies yield return new RecommendationDto { BaselineItemName = item.Name, - CategoryId = item.Id.ToString("N"), + CategoryId = item.Id, RecommendationType = type, Items = returnItems }; @@ -405,7 +370,7 @@ namespace MediaBrowser.Api.Movies { var people = _libraryManager.GetPeople(new InternalPeopleQuery { - ExcludePersonTypes = new List<string> + ExcludePersonTypes = new [] { PersonType.Director }, diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs index d7986fa501..538840f6c4 100644 --- a/MediaBrowser.Api/Music/AlbumsService.cs +++ b/MediaBrowser.Api/Music/AlbumsService.cs @@ -64,7 +64,7 @@ namespace MediaBrowser.Api.Music request, new[] { typeof(MusicArtist) }, SimilarItemsHelper.GetSimiliarityScore); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -85,7 +85,7 @@ namespace MediaBrowser.Api.Music request, new[] { typeof(MusicAlbum) }, GetAlbumSimilarityScore); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs index 8a18298f1e..8435b1ac1c 100644 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ b/MediaBrowser.Api/Music/InstantMixService.cs @@ -29,13 +29,6 @@ namespace MediaBrowser.Api.Music { } - [Route("/Artists/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")] - public class GetInstantMixFromArtist : BaseGetSimilarItems - { - [ApiMember(Name = "Name", Description = "The artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - [Route("/MusicGenres/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a music genre")] public class GetInstantMixFromMusicGenre : BaseGetSimilarItems { @@ -170,18 +163,6 @@ namespace MediaBrowser.Api.Music return GetResult(items, user, request, dtoOptions); } - public object Get(GetInstantMixFromArtist request) - { - var user = _userManager.GetUserById(request.UserId); - var artist = _libraryManager.GetArtist(request.Name, new DtoOptions(false)); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var items = _musicManager.GetInstantMixFromArtist(artist, user, dtoOptions); - - return GetResult(items, user, request, dtoOptions); - } - private object GetResult(List<BaseItem> items, User user, BaseGetSimilarItems request, DtoOptions dtoOptions) { var list = items; @@ -200,7 +181,7 @@ namespace MediaBrowser.Api.Music result.Items = returnList; - return ToOptimizedResult(result); + return result; } } diff --git a/MediaBrowser.Api/NewsService.cs b/MediaBrowser.Api/NewsService.cs index 542103799b..cbc77bfc9a 100644 --- a/MediaBrowser.Api/NewsService.cs +++ b/MediaBrowser.Api/NewsService.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Api }); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } } } diff --git a/MediaBrowser.Api/NotificationsService.cs b/MediaBrowser.Api/NotificationsService.cs deleted file mode 100644 index 4876351fc0..0000000000 --- a/MediaBrowser.Api/NotificationsService.cs +++ /dev/null @@ -1,183 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Notifications; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Api -{ - [Route("/Notifications/{UserId}", "GET", Summary = "Gets notifications")] - public class GetNotifications : IReturn<NotificationResult> - { - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } - - [ApiMember(Name = "IsRead", Description = "An optional filter by IsRead", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsRead { get; set; } - - [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? StartIndex { get; set; } - - [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; } - } - - [Route("/Notifications/{UserId}/Summary", "GET", Summary = "Gets a notification summary for a user")] - public class GetNotificationsSummary : IReturn<NotificationsSummary> - { - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } - } - - [Route("/Notifications/Types", "GET", Summary = "Gets notification types")] - public class GetNotificationTypes : IReturn<List<NotificationTypeInfo>> - { - } - - [Route("/Notifications/Services", "GET", Summary = "Gets notification types")] - public class GetNotificationServices : IReturn<List<NotificationServiceInfo>> - { - } - - [Route("/Notifications/Admin", "POST", Summary = "Sends a notification to all admin users")] - public class AddAdminNotification : IReturnVoid - { - [ApiMember(Name = "Name", Description = "The notification's name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Name { get; set; } - - [ApiMember(Name = "Description", Description = "The notification's description", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Description { get; set; } - - [ApiMember(Name = "ImageUrl", Description = "The notification's image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string ImageUrl { get; set; } - - [ApiMember(Name = "Url", Description = "The notification's info url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Url { get; set; } - - [ApiMember(Name = "Level", Description = "The notification level", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public NotificationLevel Level { get; set; } - } - - [Route("/Notifications/{UserId}/Read", "POST", Summary = "Marks notifications as read")] - public class MarkRead : IReturnVoid - { - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } - - [ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] - public string Ids { get; set; } - } - - [Route("/Notifications/{UserId}/Unread", "POST", Summary = "Marks notifications as unread")] - public class MarkUnread : IReturnVoid - { - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } - - [ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] - public string Ids { get; set; } - } - - [Authenticated] - public class NotificationsService : BaseApiService - { - private readonly INotificationsRepository _notificationsRepo; - private readonly INotificationManager _notificationManager; - private readonly IUserManager _userManager; - - public NotificationsService(INotificationsRepository notificationsRepo, INotificationManager notificationManager, IUserManager userManager) - { - _notificationsRepo = notificationsRepo; - _notificationManager = notificationManager; - _userManager = userManager; - } - - public object Get(GetNotificationTypes request) - { - var result = _notificationManager.GetNotificationTypes(); - - return ToOptimizedResult(result); - } - - public object Get(GetNotificationServices request) - { - var result = _notificationManager.GetNotificationServices().ToList(); - - return ToOptimizedResult(result); - } - - public object Get(GetNotificationsSummary request) - { - var result = _notificationsRepo.GetNotificationsSummary(request.UserId); - - return ToOptimizedResult(result); - } - - public void Post(AddAdminNotification request) - { - // This endpoint really just exists as post of a real with sickbeard - var task = AddNotification(request); - - Task.WaitAll(task); - } - - private async Task AddNotification(AddAdminNotification request) - { - var notification = new NotificationRequest - { - Date = DateTime.UtcNow, - Description = request.Description, - Level = request.Level, - Name = request.Name, - Url = request.Url, - UserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList() - }; - - await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false); - } - - public void Post(MarkRead request) - { - var task = MarkRead(request.Ids, request.UserId, true); - - Task.WaitAll(task); - } - - public void Post(MarkUnread request) - { - var task = MarkRead(request.Ids, request.UserId, false); - - Task.WaitAll(task); - } - - private Task MarkRead(string idList, string userId, bool read) - { - var ids = (idList ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - - if (ids.Length == 0) - { - return _notificationsRepo.MarkAllRead(userId, read, CancellationToken.None); - } - - return _notificationsRepo.MarkRead(ids, userId, read, CancellationToken.None); - } - - public object Get(GetNotifications request) - { - var result = _notificationsRepo.GetNotifications(new NotificationQuery - { - IsRead = request.IsRead, - Limit = request.Limit, - StartIndex = request.StartIndex, - UserId = request.UserId - }); - - return ToOptimizedResult(result); - } - } -} diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index 79dda87028..366d1318bf 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -49,7 +49,7 @@ namespace MediaBrowser.Api [ApiMember(Name = "PackageType", Description = "Optional package type filter (System/UserInstalled)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string PackageType { get; set; } - [ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "TargetSystems", Description = "Optional. Filter by target system type. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string TargetSystems { get; set; } [ApiMember(Name = "IsPremium", Description = "Optional. Filter by premium status", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] @@ -146,24 +146,24 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <returns>System.Object.</returns> - public object Get(GetPackageVersionUpdates request) + public async Task<object> Get(GetPackageVersionUpdates request) { PackageVersionInfo[] result = null; if (string.Equals(request.PackageType, "UserInstalled", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase)) { - result = _installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).Result.ToArray(); + result = (await _installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).ConfigureAwait(false)).ToArray(); } else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase)) { - var updateCheckResult = _appHost - .CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress<double>()).Result; + var updateCheckResult = await _appHost + .CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress<double>()).ConfigureAwait(false); if (updateCheckResult.IsUpdateAvailable) { - result = new PackageVersionInfo[] {updateCheckResult.Package}; + result = new PackageVersionInfo[] { updateCheckResult.Package }; } } @@ -224,11 +224,11 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <exception cref="ResourceNotFoundException"></exception> - public void Post(InstallPackage request) + public async Task Post(InstallPackage request) { var package = string.IsNullOrEmpty(request.Version) ? - _installationManager.GetLatestCompatibleVersion(request.Name, request.AssemblyGuid, _appHost.ApplicationVersion, request.UpdateClass).Result : - _installationManager.GetPackage(request.Name, request.AssemblyGuid, request.UpdateClass, Version.Parse(request.Version)).Result; + await _installationManager.GetLatestCompatibleVersion(request.Name, request.AssemblyGuid, _appHost.ApplicationVersion, request.UpdateClass).ConfigureAwait(false) : + await _installationManager.GetPackage(request.Name, request.AssemblyGuid, request.UpdateClass, Version.Parse(request.Version)).ConfigureAwait(false); if (package == null) { @@ -244,7 +244,7 @@ namespace MediaBrowser.Api /// <param name="request">The request.</param> public void Delete(CancelPackageInstallation request) { - var info = _installationManager.CurrentInstallations.FirstOrDefault(i => string.Equals(i.Item1.Id, request.Id)); + var info = _installationManager.CurrentInstallations.FirstOrDefault(i => i.Item1.Id.Equals(request.Id)); if (info != null) { diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs index 2266780217..471e3bb575 100644 --- a/MediaBrowser.Api/PlaylistService.cs +++ b/MediaBrowser.Api/PlaylistService.cs @@ -9,6 +9,7 @@ using MediaBrowser.Model.Querying; using System.Threading.Tasks; using MediaBrowser.Model.Services; using MediaBrowser.Model.Extensions; +using System; namespace MediaBrowser.Api { @@ -21,10 +22,10 @@ namespace MediaBrowser.Api [ApiMember(Name = "Ids", Description = "Item Ids to add to the playlist", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] public string Ids { get; set; } - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public Guid UserId { get; set; } - [ApiMember(Name = "MediaType", Description = "The playlist media type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + [ApiMember(Name = "MediaType", Description = "The playlist media type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public string MediaType { get; set; } } @@ -42,7 +43,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string UserId { get; set; } + public Guid UserId { get; set; } } [Route("/Playlists/{Id}/Items/{ItemId}/Move/{NewIndex}", "POST", Summary = "Moves a playlist item")] @@ -76,14 +77,14 @@ namespace MediaBrowser.Api public class GetPlaylistItems : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions { [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } + public Guid Id { get; set; } /// <summary> /// Gets or sets the user id. /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Skips over a given number of items within the results. Use for paging. @@ -139,9 +140,7 @@ namespace MediaBrowser.Api public void Post(MoveItem request) { - var task = _playlistManager.MoveItem(request.Id, request.ItemId, request.NewIndex); - - Task.WaitAll(task); + _playlistManager.MoveItem(request.Id, request.ItemId, request.NewIndex); } public async Task<object> Post(CreatePlaylist request) @@ -149,7 +148,7 @@ namespace MediaBrowser.Api var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest { Name = request.Name, - ItemIdList = SplitValue(request.Ids, ','), + ItemIdList = GetGuids(request.Ids), UserId = request.UserId, MediaType = request.MediaType @@ -160,22 +159,18 @@ namespace MediaBrowser.Api public void Post(AddToPlaylist request) { - var task = _playlistManager.AddToPlaylist(request.Id, request.Ids.Split(','), request.UserId); - - Task.WaitAll(task); + _playlistManager.AddToPlaylist(request.Id, GetGuids(request.Ids), request.UserId); } public void Delete(RemoveFromPlaylist request) { - var task = _playlistManager.RemoveFromPlaylist(request.Id, request.EntryIds.Split(',')); - - Task.WaitAll(task); + _playlistManager.RemoveFromPlaylist(request.Id, request.EntryIds.Split(',')); } public object Get(GetPlaylistItems request) { var playlist = (Playlist)_libraryManager.GetItemById(request.Id); - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; var items = playlist.GetManageableItems().ToArray(); diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs index 1eea894312..1ee096a2e7 100644 --- a/MediaBrowser.Api/PluginService.cs +++ b/MediaBrowser.Api/PluginService.cs @@ -6,7 +6,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Plugins; -using MediaBrowser.Model.Registration; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; @@ -15,6 +14,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; +using MediaBrowser.Common.Plugins; namespace MediaBrowser.Api { @@ -103,9 +103,6 @@ namespace MediaBrowser.Api { [ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Name { get; set; } - - [ApiMember(Name = "Mb2Equivalent", Description = "Optional. The equivalent feature name in MB2", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Mb2Equivalent { get; set; } } [Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)] @@ -116,6 +113,14 @@ namespace MediaBrowser.Api public string Name { get; set; } } + public class RegistrationInfo + { + public string Name { get; set; } + public DateTime ExpirationDate { get; set; } + public bool IsTrial { get; set; } + public bool IsRegistered { get; set; } + } + [Route("/Appstore/Register", "POST", Summary = "Registers an appstore sale", IsHidden = true)] [Authenticated] public class RegisterAppstoreSale @@ -168,7 +173,7 @@ namespace MediaBrowser.Api /// <returns>System.Object.</returns> public async Task<object> Get(GetRegistrationStatus request) { - var result = await _securityManager.GetRegistrationStatus(request.Name, request.Mb2Equivalent).ConfigureAwait(false); + var result = await _securityManager.GetRegistrationStatus(request.Name).ConfigureAwait(false); return ToOptimizedResult(result); } @@ -235,7 +240,7 @@ namespace MediaBrowser.Api } } - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -246,7 +251,7 @@ namespace MediaBrowser.Api public object Get(GetPluginConfiguration request) { var guid = new Guid(request.Id); - var plugin = _appHost.Plugins.First(p => p.Id == guid); + var plugin = _appHost.Plugins.First(p => p.Id == guid) as IHasPluginConfiguration; return ToOptimizedResult(plugin.Configuration); } @@ -256,15 +261,15 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <returns>System.Object.</returns> - public object Get(GetPluginSecurityInfo request) + public async Task<object> Get(GetPluginSecurityInfo request) { var result = new PluginSecurityInfo { - IsMBSupporter = _securityManager.IsMBSupporter, + IsMBSupporter = await _securityManager.IsSupporter().ConfigureAwait(false), SupporterKey = _securityManager.SupporterKey }; - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -272,37 +277,38 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request"></param> /// <returns></returns> - public void Post(RegisterAppstoreSale request) + public Task Post(RegisterAppstoreSale request) { - var task = _securityManager.RegisterAppStoreSale(request.Parameters); - - Task.WaitAll(task); + return _securityManager.RegisterAppStoreSale(request.Parameters); } /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(UpdatePluginSecurityInfo request) + public Task Post(UpdatePluginSecurityInfo request) { - var info = request; - - _securityManager.SupporterKey = info.SupporterKey; + return _securityManager.UpdateSupporterKey(request.SupporterKey); } /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(UpdatePluginConfiguration request) + public async Task Post(UpdatePluginConfiguration request) { // We need to parse this manually because we told service stack not to with IRequiresRequestStream // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs var id = new Guid(GetPathValue(1)); - var plugin = _appHost.Plugins.First(p => p.Id == id); + var plugin = _appHost.Plugins.First(p => p.Id == id) as IHasPluginConfiguration; + + if (plugin == null) + { + throw new FileNotFoundException(); + } - var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, plugin.ConfigurationType) as BasePluginConfiguration; + var configuration = (await _jsonSerializer.DeserializeFromStreamAsync(request.RequestStream, plugin.ConfigurationType).ConfigureAwait(false)) as BasePluginConfiguration; plugin.UpdateConfiguration(configuration); } diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs index abe3e5407c..959bdcd550 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs @@ -207,7 +207,7 @@ namespace MediaBrowser.Api.ScheduledTasks } } - TaskManager.Execute(task); + TaskManager.Execute(task, new TaskOptions()); } /// <summary> diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index 2d30625a98..ff57b4dd15 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -33,8 +33,8 @@ namespace MediaBrowser.Api.ScheduledTasks /// <summary> /// Initializes a new instance of the <see cref="ScheduledTasksWebSocketListener" /> class. /// </summary> - public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager, ITimerFactory timerFactory) - : base(logger, timerFactory) + public ScheduledTasksWebSocketListener(ILogger logger, ITaskManager taskManager) + : base(logger) { TaskManager = taskManager; @@ -72,14 +72,6 @@ namespace MediaBrowser.Api.ScheduledTasks .Where(i => !i.IsHidden)); } - protected override bool SendOnTimer - { - get - { - return false; - } - } - protected override void Dispose(bool dispose) { TaskManager.TaskExecuting -= TaskManager_TaskExecuting; diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index 50033eee82..2e363136b3 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -11,6 +11,7 @@ using MediaBrowser.Model.Search; using System.Threading.Tasks; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Services; +using System; namespace MediaBrowser.Api { @@ -39,7 +40,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Supply a user id to search within a user's library or omit to search all.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Search characters used to find items @@ -134,11 +135,11 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <returns>System.Object.</returns> - public async Task<object> Get(GetSearchHints request) + public object Get(GetSearchHints request) { - var result = await GetSearchHintsAsync(request).ConfigureAwait(false); + var result = GetSearchHintsAsync(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -146,9 +147,9 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <returns>Task{IEnumerable{SearchHintResult}}.</returns> - private async Task<SearchHintResult> GetSearchHintsAsync(GetSearchHints request) + private SearchHintResult GetSearchHintsAsync(GetSearchHints request) { - var result = await _searchEngine.GetSearchHints(new SearchQuery + var result = _searchEngine.GetSearchHints(new SearchQuery { Limit = request.Limit, SearchTerm = request.SearchTerm, @@ -170,7 +171,7 @@ namespace MediaBrowser.Api IsSeries = request.IsSeries, IsSports = request.IsSports - }).ConfigureAwait(false); + }); return new SearchHintResult { @@ -194,7 +195,7 @@ namespace MediaBrowser.Api Name = item.Name, IndexNumber = item.IndexNumber, ParentIndexNumber = item.ParentIndexNumber, - ItemId = _dtoService.GetDtoId(item), + Id = item.Id, Type = item.GetClientTypeName(), MediaType = item.MediaType, MatchedTerm = hintInfo.MatchedTerm, @@ -204,6 +205,14 @@ namespace MediaBrowser.Api EndDate = item.EndDate }; + // legacy + result.ItemId = result.Id; + + if (item.IsFolder) + { + result.IsFolder = true; + } + var primaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary); if (primaryImageTag != null) @@ -221,7 +230,7 @@ namespace MediaBrowser.Api result.StartDate = program.StartDate; } - var hasSeries = item as IHasSeries; + var hasSeries = item as IHasSeriesName; if (hasSeries != null) { result.Series = hasSeries.SeriesName; @@ -256,7 +265,7 @@ namespace MediaBrowser.Api if (album != null) { result.Album = album.Name; - result.AlbumId = album.Id.ToString("N"); + result.AlbumId = album.Id; } else { @@ -264,7 +273,7 @@ namespace MediaBrowser.Api } } - if (!string.IsNullOrWhiteSpace(item.ChannelId)) + if (!item.ChannelId.Equals(Guid.Empty)) { var channel = _libraryManager.GetItemById(item.ChannelId); result.ChannelName = channel == null ? null : channel.Name; diff --git a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs index 65c69fc7de..ca2c381cf6 100644 --- a/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Session/SessionInfoWebSocketListener.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Api.Session /// <summary> /// Class SessionInfoWebSocketListener /// </summary> - class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfoDto>, WebSocketListenerState> + class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState> { /// <summary> /// Gets the name. @@ -33,8 +33,8 @@ namespace MediaBrowser.Api.Session /// <summary> /// Initializes a new instance of the <see cref="SessionInfoWebSocketListener"/> class. /// </summary> - public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager, ITimerFactory timerFactory) - : base(logger, timerFactory) + public SessionInfoWebSocketListener(ILogger logger, ISessionManager sessionManager) + : base(logger) { _sessionManager = sessionManager; @@ -87,17 +87,9 @@ namespace MediaBrowser.Api.Session /// </summary> /// <param name="state">The state.</param> /// <returns>Task{SystemInfo}.</returns> - protected override Task<IEnumerable<SessionInfoDto>> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken) + protected override Task<IEnumerable<SessionInfo>> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken) { - return Task.FromResult(_sessionManager.Sessions.Where(i => i.IsActive).Select(_sessionManager.GetSessionInfoDto)); - } - - protected override bool SendOnTimer - { - get - { - return false; - } + return Task.FromResult(_sessionManager.Sessions); } protected override void Dispose(bool dispose) diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 35c29cd15b..1056b3c8bd 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Services; using MediaBrowser.Controller; +using MediaBrowser.Model.Dto; namespace MediaBrowser.Api.Session { @@ -19,13 +20,15 @@ namespace MediaBrowser.Api.Session /// </summary> [Route("/Sessions", "GET", Summary = "Gets a list of sessions")] [Authenticated] - public class GetSessions : IReturn<SessionInfoDto[]> + public class GetSessions : IReturn<SessionInfo[]> { [ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string ControllableByUserId { get; set; } + public Guid ControllableByUserId { get; set; } [ApiMember(Name = "DeviceId", Description = "Optional. Filter by device id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string DeviceId { get; set; } + + public int? ActiveWithinSeconds { get; set; } } /// <summary> @@ -189,7 +192,7 @@ namespace MediaBrowser.Api.Session /// Gets or sets the id. /// </summary> /// <value>The id.</value> - [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string Id { get; set; } [ApiMember(Name = "PlayableMediaTypes", Description = "A list of playable media types, comma delimited. Audio, Video, Book, Game, Photo.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] @@ -198,9 +201,6 @@ namespace MediaBrowser.Api.Session [ApiMember(Name = "SupportedCommands", Description = "A list of supported remote control commands, comma delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] public string SupportedCommands { get; set; } - [ApiMember(Name = "MessageCallbackUrl", Description = "A url to post messages to, including remote control commands.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string MessageCallbackUrl { get; set; } - [ApiMember(Name = "SupportsMediaControl", Description = "Determines whether media can be played remotely.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool SupportsMediaControl { get; set; } @@ -210,8 +210,6 @@ namespace MediaBrowser.Api.Session [ApiMember(Name = "SupportsPersistentIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool SupportsPersistentIdentifier { get; set; } - public bool SupportsContentUploading { get; set; } - public PostCapabilities() { SupportsPersistentIdentifier = true; @@ -226,7 +224,7 @@ namespace MediaBrowser.Api.Session /// Gets or sets the id. /// </summary> /// <value>The id.</value> - [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + [ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string Id { get; set; } } @@ -242,11 +240,17 @@ namespace MediaBrowser.Api.Session { } + [Route("/Auth/Providers", "GET")] + [Authenticated(Roles = "Admin")] + public class GetAuthProviders : IReturn<NameIdPair[]> + { + } + [Route("/Auth/Keys/{Key}", "DELETE")] [Authenticated(Roles = "Admin")] public class RevokeKey { - [ApiMember(Name = "Key", Description = "Auth Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + [ApiMember(Name = "Key", Description = "Auth Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] public string Key { get; set; } } @@ -286,6 +290,11 @@ namespace MediaBrowser.Api.Session _appHost = appHost; } + public object Get(GetAuthProviders request) + { + return _userManager.GetAuthenticationProviders(); + } + public void Delete(RevokeKey request) { _sessionManager.RevokeToken(request.Key); @@ -297,14 +306,13 @@ namespace MediaBrowser.Api.Session _authRepo.Create(new AuthenticationInfo { AppName = request.App, - IsActive = true, AccessToken = Guid.NewGuid().ToString("N"), DateCreated = DateTime.UtcNow, DeviceId = _appHost.SystemId, DeviceName = _appHost.FriendlyName, AppVersion = _appHost.ApplicationVersion.ToString() - }, CancellationToken.None); + }); } public void Post(ReportSessionEnded request) @@ -318,11 +326,10 @@ namespace MediaBrowser.Api.Session { var result = _authRepo.Get(new AuthenticationInfoQuery { - IsActive = true, HasUser = false }); - return ToOptimizedResult(result); + return result; } /// <summary> @@ -332,27 +339,33 @@ namespace MediaBrowser.Api.Session /// <returns>System.Object.</returns> public object Get(GetSessions request) { - var result = _sessionManager.Sessions.Where(i => i.IsActive); + var result = _sessionManager.Sessions; if (!string.IsNullOrEmpty(request.DeviceId)) { result = result.Where(i => string.Equals(i.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase)); } - if (!string.IsNullOrWhiteSpace(request.ControllableByUserId)) + if (!request.ControllableByUserId.Equals(Guid.Empty)) { - result = result.Where(i => i.SupportsMediaControl); + result = result.Where(i => i.SupportsRemoteControl); var user = _userManager.GetUserById(request.ControllableByUserId); if (!user.Policy.EnableRemoteControlOfOtherUsers) { - result = result.Where(i => !i.UserId.HasValue || i.ContainsUser(request.ControllableByUserId)); + result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(request.ControllableByUserId)); } if (!user.Policy.EnableSharedDeviceControl) { - result = result.Where(i => i.UserId.HasValue); + result = result.Where(i => !i.UserId.Equals(Guid.Empty)); + } + + if (request.ActiveWithinSeconds.HasValue && request.ActiveWithinSeconds.Value > 0) + { + var minActiveDate = DateTime.UtcNow.AddSeconds(0 - request.ActiveWithinSeconds.Value); + result = result.Where(i => i.LastActivityDate >= minActiveDate); } result = result.Where(i => @@ -361,7 +374,7 @@ namespace MediaBrowser.Api.Session if (!string.IsNullOrWhiteSpace(deviceId)) { - if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), deviceId)) + if (!_deviceManager.CanAccessDevice(user, deviceId)) { return false; } @@ -371,21 +384,19 @@ namespace MediaBrowser.Api.Session }); } - return ToOptimizedResult(result.Select(_sessionManager.GetSessionInfoDto).ToArray()); + return ToOptimizedResult(result.ToArray()); } - public void Post(SendPlaystateCommand request) + public Task Post(SendPlaystateCommand request) { - var task = _sessionManager.SendPlaystateCommand(GetSession(_sessionContext).Result.Id, request.Id, request, CancellationToken.None); - - Task.WaitAll(task); + return _sessionManager.SendPlaystateCommand(GetSession(_sessionContext).Id, request.Id, request, CancellationToken.None); } /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(DisplayContent request) + public Task Post(DisplayContent request) { var command = new BrowseRequest { @@ -394,16 +405,14 @@ namespace MediaBrowser.Api.Session ItemType = request.ItemType }; - var task = _sessionManager.SendBrowseCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None); - - Task.WaitAll(task); + return _sessionManager.SendBrowseCommand(GetSession(_sessionContext).Id, request.Id, command, CancellationToken.None); } /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SendSystemCommand request) + public Task Post(SendSystemCommand request) { GeneralCommandType commandType; var name = request.Command; @@ -413,24 +422,22 @@ namespace MediaBrowser.Api.Session name = commandType.ToString(); } - var currentSession = GetSession(_sessionContext).Result; + var currentSession = GetSession(_sessionContext); var command = new GeneralCommand { Name = name, - ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null + ControllingUserId = currentSession.UserId }; - var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None); - - Task.WaitAll(task); + return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None); } /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SendMessageCommand request) + public Task Post(SendMessageCommand request) { var command = new MessageCommand { @@ -439,63 +446,55 @@ namespace MediaBrowser.Api.Session Text = request.Text }; - var task = _sessionManager.SendMessageCommand(GetSession(_sessionContext).Result.Id, request.Id, command, CancellationToken.None); - - Task.WaitAll(task); + return _sessionManager.SendMessageCommand(GetSession(_sessionContext).Id, request.Id, command, CancellationToken.None); } /// <summary> /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(Play request) + public Task Post(Play request) { - var task = _sessionManager.SendPlayCommand(GetSession(_sessionContext).Result.Id, request.Id, request, CancellationToken.None); - - Task.WaitAll(task); + return _sessionManager.SendPlayCommand(GetSession(_sessionContext).Id, request.Id, request, CancellationToken.None); } - public void Post(SendGeneralCommand request) + public Task Post(SendGeneralCommand request) { - var currentSession = GetSession(_sessionContext).Result; + var currentSession = GetSession(_sessionContext); var command = new GeneralCommand { Name = request.Command, - ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null + ControllingUserId = currentSession.UserId }; - var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None); - - Task.WaitAll(task); + return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None); } - public void Post(SendFullGeneralCommand request) + public Task Post(SendFullGeneralCommand request) { - var currentSession = GetSession(_sessionContext).Result; + var currentSession = GetSession(_sessionContext); - request.ControllingUserId = currentSession.UserId.HasValue ? currentSession.UserId.Value.ToString("N") : null; + request.ControllingUserId = currentSession.UserId; - var task = _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, request, CancellationToken.None); - - Task.WaitAll(task); + return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, request, CancellationToken.None); } public void Post(AddUserToSession request) { - _sessionManager.AddAdditionalUser(request.Id, request.UserId); + _sessionManager.AddAdditionalUser(request.Id, new Guid(request.UserId)); } public void Delete(RemoveUserFromSession request) { - _sessionManager.RemoveAdditionalUser(request.Id, request.UserId); + _sessionManager.RemoveAdditionalUser(request.Id, new Guid(request.UserId)); } public void Post(PostCapabilities request) { if (string.IsNullOrWhiteSpace(request.Id)) { - request.Id = GetSession(_sessionContext).Result.Id; + request.Id = GetSession(_sessionContext).Id; } _sessionManager.ReportCapabilities(request.Id, new ClientCapabilities { @@ -505,12 +504,8 @@ namespace MediaBrowser.Api.Session SupportsMediaControl = request.SupportsMediaControl, - MessageCallbackUrl = request.MessageCallbackUrl, - SupportsSync = request.SupportsSync, - SupportsContentUploading = request.SupportsContentUploading, - SupportsPersistentIdentifier = request.SupportsPersistentIdentifier }); } @@ -519,7 +514,7 @@ namespace MediaBrowser.Api.Session { if (string.IsNullOrWhiteSpace(request.Id)) { - request.Id = GetSession(_sessionContext).Result.Id; + request.Id = GetSession(_sessionContext).Id; } _sessionManager.ReportCapabilities(request.Id, request); } diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs index 0b5eaa4760..be9c1a4c58 100644 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ b/MediaBrowser.Api/SimilarItemsHelper.cs @@ -49,7 +49,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// The maximum number of items to return @@ -73,10 +73,10 @@ namespace MediaBrowser.Api { 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) { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? userManager.GetUserById(request.UserId) : null; var item = string.IsNullOrEmpty(request.Id) ? - (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : + (!request.UserId.Equals(Guid.Empty) ? libraryManager.GetUserRootFolder() : libraryManager.RootFolder) : libraryManager.GetItemById(request.Id); var query = new InternalItemsQuery(user) @@ -89,7 +89,7 @@ namespace MediaBrowser.Api // ExcludeArtistIds if (!string.IsNullOrEmpty(request.ExcludeArtistIds)) { - query.ExcludeArtistIds = request.ExcludeArtistIds.Split('|'); + query.ExcludeArtistIds = BaseApiService.GetGuids(request.ExcludeArtistIds); } var inputItems = libraryManager.GetItemList(query); diff --git a/MediaBrowser.Api/Social/SharingService.cs b/MediaBrowser.Api/Social/SharingService.cs deleted file mode 100644 index 4f10667b72..0000000000 --- a/MediaBrowser.Api/Social/SharingService.cs +++ /dev/null @@ -1,178 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Social; -using System; -using System.IO; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Api.Social -{ - [Route("/Social/Shares/{Id}", "GET", Summary = "Gets a share")] - [Authenticated] - public class GetSocialShareInfo : IReturn<SocialShareInfo> - { - [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/Social/Shares/Public/{Id}", "GET", Summary = "Gets a share")] - public class GetPublicSocialShareInfo : IReturn<SocialShareInfo> - { - [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/Social/Shares/Public/{Id}/Image", "GET", Summary = "Gets a share")] - public class GetShareImage - { - [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/Social/Shares", "POST", Summary = "Creates a share")] - [Authenticated] - public class CreateShare : IReturn<SocialShareInfo> - { - [ApiMember(Name = "ItemId", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string ItemId { get; set; } - - [ApiMember(Name = "UserId", Description = "The user id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string UserId { get; set; } - } - - [Route("/Social/Shares/{Id}", "DELETE", Summary = "Deletes a share")] - [Authenticated] - public class DeleteShare : IReturnVoid - { - [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } - } - - [Route("/Social/Shares/Public/{Id}/Item", "GET", Summary = "Gets a share")] - public class GetSharedLibraryItem - { - [ApiMember(Name = "Id", Description = "The id of the item", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - public class SharingService : BaseApiService - { - private readonly ISharingManager _sharingManager; - private readonly ILibraryManager _libraryManager; - private readonly IDlnaManager _dlnaManager; - private readonly IDtoService _dtoService; - private readonly IHttpResultFactory _resultFactory; - - public SharingService(ISharingManager sharingManager, IDlnaManager dlnaManager, ILibraryManager libraryManager, IDtoService dtoService, IHttpResultFactory resultFactory) - { - _sharingManager = sharingManager; - _dlnaManager = dlnaManager; - _libraryManager = libraryManager; - _dtoService = dtoService; - _resultFactory = resultFactory; - } - - public object Get(GetSocialShareInfo request) - { - var info = _sharingManager.GetShareInfo(request.Id); - - return ToOptimizedResult(info); - } - - public object Get(GetSharedLibraryItem request) - { - var info = _sharingManager.GetShareInfo(request.Id); - - if (info.ExpirationDate <= DateTime.UtcNow) - { - throw new ResourceNotFoundException(); - } - - var item = _libraryManager.GetItemById(info.ItemId); - - var dto = _dtoService.GetBaseItemDto(item, new DtoOptions()); - - return ToOptimizedResult(dto); - } - - public object Get(GetPublicSocialShareInfo request) - { - var info = _sharingManager.GetShareInfo(request.Id); - - if (info.ExpirationDate <= DateTime.UtcNow) - { - throw new ResourceNotFoundException(); - } - - return ToOptimizedResult(info); - } - - public async Task<object> Post(CreateShare request) - { - var info = await _sharingManager.CreateShare(request.ItemId, request.UserId).ConfigureAwait(false); - - return ToOptimizedResult(info); - } - - public void Delete(DeleteShare request) - { - _sharingManager.DeleteShare(request.Id); - } - - public async Task<object> Get(GetShareImage request) - { - var share = _sharingManager.GetShareInfo(request.Id); - - if (share == null) - { - throw new ResourceNotFoundException(); - } - if (share.ExpirationDate <= DateTime.UtcNow) - { - throw new ResourceNotFoundException(); - } - - var item = _libraryManager.GetItemById(share.ItemId); - - var image = item.GetImageInfo(ImageType.Primary, 0); - - if (image != null) - { - if (image.IsLocalFile) - { - return await _resultFactory.GetStaticFileResult(Request, image.Path).ConfigureAwait(false); - } - - try - { - // Don't fail the request over this - var updatedImage = await _libraryManager.ConvertImageToLocal(item, image, 0).ConfigureAwait(false); - return await _resultFactory.GetStaticFileResult(Request, updatedImage.Path).ConfigureAwait(false); - } - catch - { - - } - } - - // Grab a dlna icon if nothing else is available - using (var response = _dlnaManager.GetIcon("logo240.jpg")) - { - using (var ms = new MemoryStream()) - { - response.Stream.CopyTo(ms); - - ms.Position = 0; - var bytes = ms.ToArray(); - return ResultFactory.GetResult(bytes, "image/" + response.Format.ToString().ToLower()); - } - } - - } - } -} diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index c6345c17f4..61d9a90d6c 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -3,7 +3,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Connect; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Configuration; using System; using System.Linq; using System.Threading.Tasks; @@ -14,32 +13,34 @@ using System.Threading; namespace MediaBrowser.Api { - [Route("/Startup/Complete", "POST", Summary = "Reports that the startup wizard has been completed")] + [Route("/Startup/Complete", "POST", Summary = "Reports that the startup wizard has been completed", IsHidden = true)] public class ReportStartupWizardComplete : IReturnVoid { } - [Route("/Startup/Info", "GET", Summary = "Gets initial server info")] - public class GetStartupInfo : IReturn<StartupInfo> + [Route("/Startup/Configuration", "GET", Summary = "Gets initial server configuration", IsHidden = true)] + public class GetStartupConfiguration : IReturn<StartupConfiguration> { } - [Route("/Startup/Configuration", "GET", Summary = "Gets initial server configuration")] - public class GetStartupConfiguration : IReturn<StartupConfiguration> + [Route("/Startup/Configuration", "POST", Summary = "Updates initial server configuration", IsHidden = true)] + public class UpdateStartupConfiguration : StartupConfiguration, IReturnVoid { } - [Route("/Startup/Configuration", "POST", Summary = "Updates initial server configuration")] - public class UpdateStartupConfiguration : StartupConfiguration, IReturnVoid + [Route("/Startup/RemoteAccess", "POST", Summary = "Updates initial server configuration", IsHidden = true)] + public class UpdateRemoteAccessConfiguration : IReturnVoid { + public bool EnableRemoteAccess { get; set; } + public bool EnableAutomaticPortMapping { get; set; } } - [Route("/Startup/User", "GET", Summary = "Gets initial user info")] + [Route("/Startup/User", "GET", Summary = "Gets initial user info", IsHidden = true)] public class GetStartupUser : IReturn<StartupUser> { } - [Route("/Startup/User", "POST", Summary = "Updates initial user info")] + [Route("/Startup/User", "POST", Summary = "Updates initial user info", IsHidden = true)] public class UpdateStartupUser : StartupUser, IReturn<UpdateStartupUserResult> { } @@ -67,7 +68,6 @@ namespace MediaBrowser.Api public void Post(ReportStartupWizardComplete request) { _config.Configuration.IsStartupWizardCompleted = true; - _config.Configuration.AutoRunWebApp = true; _config.SetOptimalValues(); _config.SaveConfiguration(); @@ -101,14 +101,6 @@ namespace MediaBrowser.Api } } - public object Get(GetStartupInfo request) - { - return new StartupInfo - { - HasMediaEncoder = !string.IsNullOrWhiteSpace(_mediaEncoder.EncoderPath) - }; - } - public object Get(GetStartupConfiguration request) { var result = new StartupConfiguration @@ -129,6 +121,13 @@ namespace MediaBrowser.Api _config.SaveConfiguration(); } + public void Post(UpdateRemoteAccessConfiguration request) + { + _config.Configuration.EnableRemoteAccess = request.EnableRemoteAccess; + _config.Configuration.EnableUPnP = request.EnableAutomaticPortMapping; + _config.SaveConfiguration(); + } + public object Get(GetStartupUser request) { var user = _userManager.Users.First(); @@ -152,11 +151,11 @@ namespace MediaBrowser.Api if (!string.IsNullOrWhiteSpace(user.ConnectUserName) && string.IsNullOrWhiteSpace(request.ConnectUserName)) { - await _connectManager.RemoveConnect(user.Id.ToString("N")).ConfigureAwait(false); + await _connectManager.RemoveConnect(user).ConfigureAwait(false); } else if (!string.Equals(user.ConnectUserName, request.ConnectUserName, StringComparison.OrdinalIgnoreCase)) { - result.UserLinkResult = await _connectManager.LinkUser(user.Id.ToString("N"), request.ConnectUserName).ConfigureAwait(false); + result.UserLinkResult = await _connectManager.LinkUser(user, request.ConnectUserName).ConfigureAwait(false); } return result; @@ -170,11 +169,6 @@ namespace MediaBrowser.Api public string PreferredMetadataLanguage { get; set; } } - public class StartupInfo - { - public bool HasMediaEncoder { get; set; } - } - public class StartupUser { public string Name { get; set; } diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs index 4d4b4cb273..c8b0a32e95 100644 --- a/MediaBrowser.Api/Subtitles/SubtitleService.cs +++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Api.Subtitles /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } + public Guid Id { get; set; } [ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "DELETE")] public int Index { get; set; } @@ -40,7 +40,7 @@ namespace MediaBrowser.Api.Subtitles public class SearchRemoteSubtitles : IReturn<RemoteSubtitleInfo[]> { [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } + public Guid Id { get; set; } [ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Language { get; set; } @@ -48,20 +48,12 @@ namespace MediaBrowser.Api.Subtitles public bool? IsPerfectMatch { get; set; } } - [Route("/Items/{Id}/RemoteSearch/Subtitles/Providers", "GET")] - [Authenticated] - public class GetSubtitleProviders : IReturn<SubtitleProviderInfo[]> - { - [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - [Route("/Items/{Id}/RemoteSearch/Subtitles/{SubtitleId}", "POST")] [Authenticated] public class DownloadRemoteSubtitles : IReturnVoid { [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } + public Guid Id { get; set; } [ApiMember(Name = "SubtitleId", Description = "SubtitleId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string SubtitleId { get; set; } @@ -84,7 +76,7 @@ namespace MediaBrowser.Api.Subtitles /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } + public Guid Id { get; set; } [ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string MediaSourceId { get; set; } @@ -123,7 +115,7 @@ namespace MediaBrowser.Api.Subtitles [ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] public int Index { get; set; } - [ApiMember(Name = "SegmentLength", Description = "The subtitle srgment length", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] + [ApiMember(Name = "SegmentLength", Description = "The subtitle srgment length", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")] public int SegmentLength { get; set; } } @@ -195,7 +187,7 @@ namespace MediaBrowser.Api.Subtitles builder.AppendLine("#EXT-X-ENDLIST"); - return ResultFactory.GetResult(builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); + return ResultFactory.GetResult(Request, builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } public async Task<object> Get(GetSubtitle request) @@ -206,10 +198,11 @@ namespace MediaBrowser.Api.Subtitles } if (string.IsNullOrEmpty(request.Format)) { - var item = (Video)_libraryManager.GetItemById(new Guid(request.Id)); + var item = (Video)_libraryManager.GetItemById(request.Id); + var idString = request.Id.ToString("N"); var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null) - .First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id)); + .First(i => string.Equals(i.Id, request.MediaSourceId ?? idString)); var subtitleStream = mediaSource.MediaStreams .First(i => i.Type == MediaStreamType.Subtitle && i.Index == request.Index); @@ -227,22 +220,24 @@ namespace MediaBrowser.Api.Subtitles text = text.Replace("WEBVTT", "WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000"); - return ResultFactory.GetResult(text, MimeTypes.GetMimeType("file." + request.Format)); + return ResultFactory.GetResult(Request, text, MimeTypes.GetMimeType("file." + request.Format)); } } } - return ResultFactory.GetResult(await GetSubtitles(request).ConfigureAwait(false), MimeTypes.GetMimeType("file." + request.Format)); + return ResultFactory.GetResult(Request, await GetSubtitles(request).ConfigureAwait(false), MimeTypes.GetMimeType("file." + request.Format)); } private Task<Stream> GetSubtitles(GetSubtitle request) { - return _subtitleEncoder.GetSubtitles(request.Id, + var item = _libraryManager.GetItemById(request.Id); + + return _subtitleEncoder.GetSubtitles(item, request.MediaSourceId, request.Index, request.Format, request.StartPositionTicks, - request.EndPositionTicks, + request.EndPositionTicks ?? 0, request.CopyTimestamps, CancellationToken.None); } @@ -251,30 +246,20 @@ namespace MediaBrowser.Api.Subtitles { var video = (Video)_libraryManager.GetItemById(request.Id); - var response = await _subtitleManager.SearchSubtitles(video, request.Language, request.IsPerfectMatch, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(response); - } - - public void Delete(DeleteSubtitle request) - { - var task = _subtitleManager.DeleteSubtitles(request.Id, request.Index); - - Task.WaitAll(task); + return await _subtitleManager.SearchSubtitles(video, request.Language, request.IsPerfectMatch, CancellationToken.None).ConfigureAwait(false); } - public object Get(GetSubtitleProviders request) + public Task Delete(DeleteSubtitle request) { - var result = _subtitleManager.GetProviders(request.Id); - - return ToOptimizedResult(result); + var item = _libraryManager.GetItemById(request.Id); + return _subtitleManager.DeleteSubtitles(item, request.Index); } public async Task<object> Get(GetRemoteSubtitles request) { var result = await _subtitleManager.GetRemoteSubtitles(request.Id, CancellationToken.None).ConfigureAwait(false); - return ResultFactory.GetResult(result.Stream, MimeTypes.GetMimeType("file." + result.Format)); + return ResultFactory.GetResult(Request, result.Stream, MimeTypes.GetMimeType("file." + result.Format)); } public void Post(DownloadRemoteSubtitles request) diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs index 3b918d8a2f..07053c5542 100644 --- a/MediaBrowser.Api/SuggestionsService.cs +++ b/MediaBrowser.Api/SuggestionsService.cs @@ -14,11 +14,11 @@ using MediaBrowser.Model.Extensions; namespace MediaBrowser.Api { [Route("/Users/{UserId}/Suggestions", "GET", Summary = "Gets items based on a query.")] - public class GetSuggestedItems : IReturn<QueryResult<BaseItem>> + public class GetSuggestedItems : IReturn<QueryResult<BaseItemDto>> { public string MediaType { get; set; } public string Type { get; set; } - public string UserId { get; set; } + public Guid UserId { get; set; } public bool EnableTotalRecordCount { get; set; } public int? StartIndex { get; set; } public int? Limit { get; set; } @@ -51,14 +51,12 @@ namespace MediaBrowser.Api public object Get(GetSuggestedItems request) { - var result = GetResultItems(request); - - return ToOptimizedResult(result); + return GetResultItems(request); } private QueryResult<BaseItemDto> GetResultItems(GetSuggestedItems request) { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; var dtoOptions = GetDtoOptions(_authContext, request); var result = GetItems(request, user, dtoOptions); @@ -81,7 +79,7 @@ namespace MediaBrowser.Api { return _libraryManager.GetItemsResult(new InternalItemsQuery(user) { - OrderBy = new[] { ItemSortBy.Random }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), + OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), MediaTypes = request.GetMediaTypes(), IncludeItemTypes = request.GetIncludeItemTypes(), IsVirtualItem = false, diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs index e3a18a9335..d55c57ffaa 100644 --- a/MediaBrowser.Api/System/ActivityLogService.cs +++ b/MediaBrowser.Api/System/ActivityLogService.cs @@ -24,8 +24,10 @@ namespace MediaBrowser.Api.System [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; } - [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string MinDate { get; set; } + + public bool? HasUserId { get; set; } } [Authenticated(Roles = "Admin")] @@ -44,7 +46,7 @@ namespace MediaBrowser.Api.System (DateTime?)null : DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - var result = _activityManager.GetActivityLogEntries(minDate, request.StartIndex, request.Limit); + var result = _activityManager.GetActivityLogEntries(minDate, request.HasUserId, request.StartIndex, request.Limit); return ToOptimizedResult(result); } diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index f9cac7389d..6991244c6c 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -4,7 +4,6 @@ using MediaBrowser.Model.Logging; using System.Collections.Generic; using System.Threading.Tasks; using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Threading; using System.Threading; namespace MediaBrowser.Api.System @@ -28,7 +27,7 @@ namespace MediaBrowser.Api.System /// </summary> private readonly IActivityManager _activityManager; - public ActivityLogWebSocketListener(ILogger logger, ITimerFactory timerFactory, IActivityManager activityManager) : base(logger, timerFactory) + public ActivityLogWebSocketListener(ILogger logger, IActivityManager activityManager) : base(logger) { _activityManager = activityManager; _activityManager.EntryCreated += _activityManager_EntryCreated; @@ -48,14 +47,7 @@ namespace MediaBrowser.Api.System { return Task.FromResult(new List<ActivityLogEntry>()); } - - protected override bool SendOnTimer - { - get - { - return false; - } - } + protected override void Dispose(bool dispose) { diff --git a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs b/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs deleted file mode 100644 index 63847f2b52..0000000000 --- a/MediaBrowser.Api/System/SystemInfoWebSocketListener.cs +++ /dev/null @@ -1,49 +0,0 @@ -using MediaBrowser.Controller; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.System; -using System.Threading.Tasks; -using MediaBrowser.Model.Threading; -using System.Threading; - -namespace MediaBrowser.Api.System -{ - /// <summary> - /// Class SystemInfoWebSocketListener - /// </summary> - public class SystemInfoWebSocketListener : BasePeriodicWebSocketListener<SystemInfo, WebSocketListenerState> - { - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - protected override string Name - { - get { return "SystemInfo"; } - } - - /// <summary> - /// The _kernel - /// </summary> - private readonly IServerApplicationHost _appHost; - - /// <summary> - /// Initializes a new instance of the <see cref="SystemInfoWebSocketListener" /> class. - /// </summary> - public SystemInfoWebSocketListener(ILogger logger, IServerApplicationHost appHost, ITimerFactory timerFactory) - : base(logger, timerFactory) - { - _appHost = appHost; - } - - /// <summary> - /// Gets the data to send. - /// </summary> - /// <param name="state">The state.</param> - /// <returns>Task{SystemInfo}.</returns> - protected override Task<SystemInfo> GetDataToSend(WebSocketListenerState state, CancellationToken cancellationToken) - { - return _appHost.GetSystemInfo(cancellationToken); - } - } -} diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index c0bbf70ead..d2880f735a 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/MediaBrowser.Api/System/SystemService.cs @@ -35,6 +35,7 @@ namespace MediaBrowser.Api.System } [Route("/System/Ping", "POST")] + [Route("/System/Ping", "GET")] public class PingSystem : IReturnVoid { @@ -79,6 +80,13 @@ namespace MediaBrowser.Api.System public string Name { get; set; } } + [Route("/System/WakeOnLanInfo", "GET", Summary = "Gets wake on lan information")] + [Authenticated] + public class GetWakeOnLanInfo : IReturn<WakeOnLanInfo[]> + { + + } + /// <summary> /// Class SystemInfoService /// </summary> @@ -116,6 +124,13 @@ namespace MediaBrowser.Api.System return _appHost.Name; } + public object Get(GetWakeOnLanInfo request) + { + var result = _appHost.GetWakeOnLanInfo(); + + return ToOptimizedResult(result); + } + public object Get(GetServerLogs request) { IEnumerable<FileSystemMetadata> files; diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index fd81a9a3e6..6cbb4062dc 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Skips over a given number of items within the results. Use for paging. @@ -88,7 +88,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Skips over a given number of items within the results. Use for paging. @@ -131,11 +131,6 @@ namespace MediaBrowser.Api public bool? EnableUserData { get; set; } } - [Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")] - public class GetSimilarShows : BaseGetSimilarItemsFromItem - { - } - [Route("/Shows/{Id}/Episodes", "GET", Summary = "Gets episodes for a tv season")] public class GetEpisodes : IReturn<QueryResult<BaseItemDto>>, IHasItemFields, IHasDtoOptions { @@ -144,7 +139,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Fields to return within the items, in addition to basic information @@ -212,7 +207,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Fields to return within the items, in addition to basic information @@ -288,66 +283,20 @@ namespace MediaBrowser.Api _authContext = authContext; } - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetSimilarShows request) - { - var result = GetSimilarItemsResult(request); - - return ToOptimizedSerializedResultUsingCache(result); - } - - private QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request) - { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; - - var item = string.IsNullOrEmpty(request.Id) ? - (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : - _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - Limit = request.Limit, - IncludeItemTypes = new[] - { - typeof(Series).Name - }, - SimilarTo = item, - DtoOptions = dtoOptions - - }); - - var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user); - - var result = new QueryResult<BaseItemDto> - { - Items = returnList, - - TotalRecordCount = itemsResult.Count - }; - - return result; - } - public object Get(GetUpcomingEpisodes request) { var user = _userManager.GetUserById(request.UserId); var minPremiereDate = DateTime.Now.Date.ToUniversalTime().AddDays(-1); - var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId); + var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId); var options = GetDtoOptions(_authContext, request); var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Episode).Name }, - OrderBy = new[] { ItemSortBy.PremiereDate, ItemSortBy.SortName }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(), + OrderBy = new[] { ItemSortBy.PremiereDate, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(), MinPremiereDate = minPremiereDate, StartIndex = request.StartIndex, Limit = request.Limit, @@ -365,7 +314,7 @@ namespace MediaBrowser.Api Items = returnItems }; - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -391,7 +340,7 @@ namespace MediaBrowser.Api var returnItems = _dtoService.GetBaseItemDtos(result.Items, options, user); - return ToOptimizedSerializedResultUsingCache(new QueryResult<BaseItemDto> + return ToOptimizedResult(new QueryResult<BaseItemDto> { TotalRecordCount = result.TotalRecordCount, Items = returnItems diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs index 4018759d99..cd3c80463b 100644 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } /// <summary> @@ -56,9 +56,7 @@ namespace MediaBrowser.Api.UserLibrary /// <returns>System.Object.</returns> public object Get(GetArtist request) { - var result = GetItem(request); - - return ToOptimizedResult(result); + return GetItem(request); } /// <summary> @@ -71,8 +69,8 @@ namespace MediaBrowser.Api.UserLibrary var dtoOptions = GetDtoOptions(AuthorizationContext, request); var item = GetArtist(request.Name, LibraryManager, dtoOptions); - - if (!string.IsNullOrWhiteSpace(request.UserId)) + + if (!request.UserId.Equals(Guid.Empty)) { var user = UserManager.GetUserById(request.UserId); @@ -94,9 +92,7 @@ namespace MediaBrowser.Api.UserLibrary //request.IncludeItemTypes = "Audio,MusicVideo"; } - var result = GetResultSlim(request); - - return ToOptimizedResult(result); + return GetResultSlim(request); } /// <summary> diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs index ed0c4069b3..fe8b446a1f 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs @@ -56,10 +56,10 @@ namespace MediaBrowser.Api.UserLibrary { BaseItem parentItem; - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { var user = UserManager.GetUserById(request.UserId); - parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId); + parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId); } else { @@ -73,18 +73,12 @@ namespace MediaBrowser.Api.UserLibrary { var parent = GetParentItem(request); - var collectionFolder = parent as ICollectionFolder; + var collectionFolder = parent as IHasCollectionType; if (collectionFolder != null) { return collectionFolder.CollectionType; } - var view = parent as UserView; - if (view != null) - { - return view.ViewType; - } - return null; } @@ -95,10 +89,10 @@ namespace MediaBrowser.Api.UserLibrary User user = null; BaseItem parentItem; - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { user = UserManager.GetUserById(request.UserId); - parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId); + parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId); } else { @@ -123,25 +117,27 @@ namespace MediaBrowser.Api.UserLibrary Tags = request.GetTags(), OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), - GenreIds = request.GetGenreIds(), - StudioIds = request.GetStudioIds(), + GenreIds = GetGuids(request.GenreIds), + StudioIds = GetGuids(request.StudioIds), Person = request.Person, - PersonIds = request.GetPersonIds(), + PersonIds = GetGuids(request.PersonIds), PersonTypes = request.GetPersonTypes(), Years = request.GetYears(), MinCommunityRating = request.MinCommunityRating, - DtoOptions = dtoOptions + DtoOptions = dtoOptions, + SearchTerm = request.SearchTerm, + EnableTotalRecordCount = request.EnableTotalRecordCount }; if (!string.IsNullOrWhiteSpace(request.ParentId)) { if (parentItem is Folder) { - query.AncestorIds = new[] { request.ParentId }; + query.AncestorIds = new[] { new Guid(request.ParentId) }; } else { - query.ItemIds = new[] { request.ParentId }; + query.ItemIds = new[] { new Guid(request.ParentId) }; } } @@ -158,7 +154,7 @@ namespace MediaBrowser.Api.UserLibrary { return null; } - }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray(); + }).Where(i => i != null).Select(i => i.Id).ToArray(); } foreach (var filter in request.GetFilters()) @@ -197,10 +193,9 @@ namespace MediaBrowser.Api.UserLibrary var result = GetItems(request, query); - var syncProgess = DtoService.GetSyncedItemProgress(dtoOptions); var dtos = result.Items.Select(i => { - var dto = DtoService.GetItemByNameDto(i.Item1, dtoOptions, null, syncProgess, user); + var dto = DtoService.GetItemByNameDto(i.Item1, dtoOptions, null, user); if (!string.IsNullOrWhiteSpace(request.IncludeItemTypes)) { @@ -247,10 +242,10 @@ namespace MediaBrowser.Api.UserLibrary User user = null; BaseItem parentItem; - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { user = UserManager.GetUserById(request.UserId); - parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId); + parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId); } else { @@ -277,7 +272,7 @@ namespace MediaBrowser.Api.UserLibrary { var folder = (Folder)parentItem; - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { items = request.Recursive ? folder.GetRecursiveChildren(user, query).ToList() : @@ -297,9 +292,7 @@ namespace MediaBrowser.Api.UserLibrary var extractedItems = GetAllItems(request, items); - var filteredItems = FilterItems(request, extractedItems, user); - - filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy()); + var filteredItems = LibraryManager.Sort(extractedItems, user, request.GetOrderBy()); var ibnItemsArray = filteredItems.ToList(); @@ -326,8 +319,7 @@ namespace MediaBrowser.Api.UserLibrary var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>())); - var syncProgess = DtoService.GetSyncedItemProgress(dtoOptions); - var dtos = tuples.Select(i => DtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, syncProgess, user)); + var dtos = tuples.Select(i => DtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, user)); result.Items = dtos.Where(i => i != null).ToArray(); @@ -338,125 +330,6 @@ namespace MediaBrowser.Api.UserLibrary /// Filters the items. /// </summary> /// <param name="request">The request.</param> - /// <param name="items">The items.</param> - /// <param name="user">The user.</param> - /// <returns>IEnumerable{`0}.</returns> - private IEnumerable<BaseItem> FilterItems(GetItemsByName request, IEnumerable<BaseItem> items, User user) - { - if (!string.IsNullOrEmpty(request.NameStartsWithOrGreater)) - { - items = items.Where(i => string.Compare(request.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1); - } - if (!string.IsNullOrEmpty(request.NameStartsWith)) - { - items = items.Where(i => string.Compare(request.NameStartsWith, i.SortName.Substring(0, 1), StringComparison.CurrentCultureIgnoreCase) == 0); - } - - if (!string.IsNullOrEmpty(request.NameLessThan)) - { - items = items.Where(i => string.Compare(request.NameLessThan, i.SortName, StringComparison.CurrentCultureIgnoreCase) == 1); - } - - var imageTypes = request.GetImageTypes(); - if (imageTypes.Length > 0) - { - items = items.Where(item => imageTypes.Any(item.HasImage)); - } - - var filters = request.GetFilters(); - - if (filters.Contains(ItemFilter.Dislikes)) - { - items = items.Where(i => - { - var userdata = UserDataRepository.GetUserData(user, i); - - return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; - }); - } - - if (filters.Contains(ItemFilter.Likes)) - { - items = items.Where(i => - { - var userdata = UserDataRepository.GetUserData(user, i); - - return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; - }); - } - - if (filters.Contains(ItemFilter.IsFavoriteOrLikes)) - { - items = items.Where(i => - { - var userdata = UserDataRepository.GetUserData(user, i); - - var likes = userdata.Likes ?? false; - var favorite = userdata.IsFavorite; - - return likes || favorite; - }); - } - - if (filters.Contains(ItemFilter.IsFavorite)) - { - items = items.Where(i => - { - var userdata = UserDataRepository.GetUserData(user, i); - - return userdata != null && userdata.IsFavorite; - }); - } - - // Avoid implicitly captured closure - var currentRequest = request; - return items.Where(i => ApplyAdditionalFilters(currentRequest, i, user, false)); - } - - private bool ApplyAdditionalFilters(BaseItemsRequest request, BaseItem i, User user, bool isPreFiltered) - { - if (!isPreFiltered) - { - // Apply tag filter - var tags = request.GetTags(); - if (tags.Length > 0) - { - if (!tags.Any(v => i.Tags.Contains(v, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - } - - // Apply official rating filter - var officialRatings = request.GetOfficialRatings(); - if (officialRatings.Length > 0 && !officialRatings.Contains(i.OfficialRating ?? string.Empty)) - { - return false; - } - - // Apply genre filter - var genres = request.GetGenres(); - if (genres.Length > 0 && !genres.Any(v => i.Genres.Contains(v, StringComparer.OrdinalIgnoreCase))) - { - return false; - } - - // Apply year filter - var years = request.GetYears(); - if (years.Length > 0 && !(i.ProductionYear.HasValue && years.Contains(i.ProductionYear.Value))) - { - return false; - } - } - - return true; - } - - - /// <summary> - /// Filters the items. - /// </summary> - /// <param name="request">The request.</param> /// <param name="f">The f.</param> /// <param name="excludeItemTypes">The exclude item types.</param> /// <param name="includeItemTypes">The include item types.</param> diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 88d080db58..11c12c718f 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -58,6 +58,8 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IsHD", Description = "Optional filter by items that are HD or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? IsHD { get; set; } + public bool? Is4K { get; set; } + [ApiMember(Name = "LocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string LocationTypes { get; set; } @@ -103,9 +105,7 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "HasTvdbId", Description = "Optional filter by items that have a tvdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool? HasTvdbId { get; set; } - [ApiMember(Name = "IsInBoxSet", Description = "Optional filter by items that are in boxsets, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsInBoxSet { get; set; } - + [ApiMember(Name = "ExcludeItemIds", Description = "Optional. If specified, results will be filtered by exxcluding item ids. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string ExcludeItemIds { get; set; } public bool EnableTotalRecordCount { get; set; } @@ -131,6 +131,8 @@ namespace MediaBrowser.Api.UserLibrary [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")] public bool Recursive { get; set; } + public string SearchTerm { get; set; } + /// <summary> /// Gets or sets the sort order. /// </summary> @@ -277,6 +279,10 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "ArtistIds", Description = "Optional. If specified, results will be filtered based on artist. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string ArtistIds { get; set; } + public string AlbumArtistIds { get; set; } + + public string ContributingArtistIds { get; set; } + [ApiMember(Name = "Albums", Description = "Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Albums { get; set; } @@ -300,8 +306,8 @@ namespace MediaBrowser.Api.UserLibrary /// Gets or sets the user id. /// </summary> /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public Guid UserId { get; set; } /// <summary> /// Gets or sets the min offical rating. @@ -321,6 +327,12 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "CollapseBoxSetItems", Description = "Whether or not to hide items behind their boxsets.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] 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> /// Gets or sets the video formats. /// </summary> @@ -369,11 +381,6 @@ namespace MediaBrowser.Api.UserLibrary return (IncludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); } - public string[] GetExcludeItemIds() - { - return (ExcludeItemIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - public string[] GetExcludeItemTypes() { return (ExcludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); @@ -389,36 +396,11 @@ namespace MediaBrowser.Api.UserLibrary return (Studios ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); } - public string[] GetArtistIds() - { - return (ArtistIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetStudioIds() - { - return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetGenreIds() - { - return (GenreIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - } - public string[] GetPersonTypes() { return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); } - public string[] GetPersonIds() - { - return (PersonIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetItemIds() - { - return (Ids ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - public VideoType[] GetVideoTypes() { var val = VideoTypes; @@ -467,18 +449,18 @@ namespace MediaBrowser.Api.UserLibrary /// Gets the order by. /// </summary> /// <returns>IEnumerable{ItemSortBy}.</returns> - public Tuple<string, SortOrder>[] GetOrderBy() + public ValueTuple<string, SortOrder>[] GetOrderBy() { return GetOrderBy(SortBy, SortOrder); } - public static Tuple<string, SortOrder>[] GetOrderBy(string sortBy, string requestedSortOrder) + public static ValueTuple<string, SortOrder>[] GetOrderBy(string sortBy, string requestedSortOrder) { var val = sortBy; if (string.IsNullOrEmpty(val)) { - return new Tuple<string, Model.Entities.SortOrder>[] { }; + return Array.Empty<ValueTuple<string, Model.Entities.SortOrder>>(); } var vals = val.Split(','); @@ -489,7 +471,7 @@ namespace MediaBrowser.Api.UserLibrary var sortOrders = requestedSortOrder.Split(','); - var result = new Tuple<string, Model.Entities.SortOrder>[vals.Length]; + var result = new ValueTuple<string, Model.Entities.SortOrder>[vals.Length]; for (var i = 0; i < vals.Length; i++) { @@ -498,7 +480,7 @@ namespace MediaBrowser.Api.UserLibrary var sortOrderValue = sortOrders.Length > sortOrderIndex ? sortOrders[sortOrderIndex] : null; var sortOrder = string.Equals(sortOrderValue, "Descending", StringComparison.OrdinalIgnoreCase) ? MediaBrowser.Model.Entities.SortOrder.Descending : MediaBrowser.Model.Entities.SortOrder.Ascending; - result[i] = new Tuple<string, Model.Entities.SortOrder>(vals[i], sortOrder); + result[i] = new ValueTuple<string, Model.Entities.SortOrder>(vals[i], sortOrder); } return result; diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs index 0b2ca4daf9..476e881b97 100644 --- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs @@ -31,7 +31,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } [Authenticated] @@ -46,7 +46,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetItem(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -60,7 +60,7 @@ namespace MediaBrowser.Api.UserLibrary var item = GetGameGenre(request.Name, LibraryManager, dtoOptions); - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { var user = UserManager.GetUserById(request.UserId); @@ -79,7 +79,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetResultSlim(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query) diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index d913f52d96..0bb7d7865a 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } /// <summary> @@ -56,7 +56,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetItem(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -70,7 +70,7 @@ namespace MediaBrowser.Api.UserLibrary var item = GetGenre(request.Name, LibraryManager, dtoOptions); - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { var user = UserManager.GetUserById(request.UserId); @@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetResultSlim(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query) diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 1e531ba664..aa17e85f34 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -88,25 +88,25 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId); + var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId); var options = GetDtoOptions(_authContext, request); - var ancestorIds = new List<string>(); + var ancestorIds = new List<Guid>(); var excludeFolderIds = user.Configuration.LatestItemsExcludes; - if (!parentIdGuid.HasValue && excludeFolderIds.Length > 0) + if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0) { - ancestorIds = user.RootFolder.GetChildren(user, true) + ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N"))) - .Select(i => i.Id.ToString("N")) + .Select(i => i.Id) .ToList(); } var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) { - OrderBy = new[] { ItemSortBy.DatePlayed }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), + OrderBy = new[] { ItemSortBy.DatePlayed }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), IsResumable = true, StartIndex = request.StartIndex, Limit = request.Limit, @@ -119,7 +119,8 @@ namespace MediaBrowser.Api.UserLibrary EnableTotalRecordCount = request.EnableTotalRecordCount, AncestorIds = ancestorIds.ToArray(), IncludeItemTypes = request.GetIncludeItemTypes(), - ExcludeItemTypes = request.GetExcludeItemTypes() + ExcludeItemTypes = request.GetExcludeItemTypes(), + SearchTerm = request.SearchTerm }); var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user); @@ -130,7 +131,7 @@ namespace MediaBrowser.Api.UserLibrary Items = returnItems }; - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -147,7 +148,7 @@ namespace MediaBrowser.Api.UserLibrary var result = GetItems(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -156,7 +157,7 @@ namespace MediaBrowser.Api.UserLibrary /// <param name="request">The request.</param> private QueryResult<BaseItemDto> GetItems(GetItems request) { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; var dtoOptions = GetDtoOptions(_authContext, request); @@ -191,26 +192,23 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> private QueryResult<BaseItem> GetQueryResult(GetItems request, DtoOptions dtoOptions, User user) { - var item = string.IsNullOrEmpty(request.ParentId) ? - null : - _libraryManager.GetItemById(request.ParentId); - if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase)) { - if (item == null || user != null) - { - item = _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, "PlaylistsFolder", StringComparison.OrdinalIgnoreCase)); - } + request.ParentId = null; } else if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase)) { - item = user == null ? _libraryManager.RootFolder : user.RootFolder; + request.ParentId = null; } + var item = string.IsNullOrEmpty(request.ParentId) ? + null : + _libraryManager.GetItemById(request.ParentId); + if (item == null) { item = string.IsNullOrEmpty(request.ParentId) ? - user == null ? _libraryManager.RootFolder : user.RootFolder : + user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.ParentId); } @@ -222,6 +220,15 @@ namespace MediaBrowser.Api.UserLibrary folder = user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder(); } + var hasCollectionType = folder as IHasCollectionType; + var isPlaylistQuery = (hasCollectionType != null && string.Equals(hasCollectionType.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)); + + if (isPlaylistQuery) + { + request.Recursive = true; + request.IncludeItemTypes = "Playlist"; + } + if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || user == null) { return folder.GetItems(GetItemsQuery(request, dtoOptions, user)); @@ -266,8 +273,10 @@ namespace MediaBrowser.Api.UserLibrary HasImdbId = request.HasImdbId, IsPlaceHolder = request.IsPlaceHolder, IsLocked = request.IsLocked, - IsInBoxSet = request.IsInBoxSet, - IsHD = request.IsHD, + MinWidth = request.MinWidth, + MinHeight = request.MinHeight, + MaxWidth = request.MaxWidth, + MaxHeight = request.MaxHeight, Is3D = request.Is3D, HasTvdbId = request.HasTvdbId, HasTmdbId = request.HasTmdbId, @@ -279,33 +288,37 @@ namespace MediaBrowser.Api.UserLibrary HasThemeSong = request.HasThemeSong, HasThemeVideo = request.HasThemeVideo, HasTrailer = request.HasTrailer, + IsHD = request.IsHD, + Is4K = request.Is4K, Tags = request.GetTags(), OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), - ArtistIds = request.GetArtistIds(), - GenreIds = request.GetGenreIds(), - StudioIds = request.GetStudioIds(), + ArtistIds = GetGuids(request.ArtistIds), + AlbumArtistIds = GetGuids(request.AlbumArtistIds), + ContributingArtistIds = GetGuids(request.ContributingArtistIds), + GenreIds = GetGuids(request.GenreIds), + StudioIds = GetGuids(request.StudioIds), Person = request.Person, - PersonIds = request.GetPersonIds(), + PersonIds = GetGuids(request.PersonIds), PersonTypes = request.GetPersonTypes(), Years = request.GetYears(), ImageTypes = request.GetImageTypes(), VideoTypes = request.GetVideoTypes(), AdjacentTo = request.AdjacentTo, - ItemIds = request.GetItemIds(), + ItemIds = GetGuids(request.Ids), MinPlayers = request.MinPlayers, MaxPlayers = request.MaxPlayers, MinCommunityRating = request.MinCommunityRating, MinCriticRating = request.MinCriticRating, - ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId), + ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId), ParentIndexNumber = request.ParentIndexNumber, - AiredDuringSeason = request.AiredDuringSeason, EnableTotalRecordCount = request.EnableTotalRecordCount, - ExcludeItemIds = request.GetExcludeItemIds(), - DtoOptions = dtoOptions + ExcludeItemIds = GetGuids(request.ExcludeItemIds), + DtoOptions = dtoOptions, + SearchTerm = request.SearchTerm }; - if (!string.IsNullOrWhiteSpace(request.Ids)) + if (!string.IsNullOrWhiteSpace(request.Ids) || !string.IsNullOrWhiteSpace(request.SearchTerm)) { query.CollapseBoxSetItems = false; } @@ -416,18 +429,18 @@ namespace MediaBrowser.Api.UserLibrary { return null; } - }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray(); + }).Where(i => i != null).Select(i => i.Id).ToArray(); } // ExcludeArtistIds if (!string.IsNullOrWhiteSpace(request.ExcludeArtistIds)) { - query.ExcludeArtistIds = request.ExcludeArtistIds.Split('|'); + query.ExcludeArtistIds = GetGuids(request.ExcludeArtistIds); } if (!string.IsNullOrWhiteSpace(request.AlbumIds)) { - query.AlbumIds = request.AlbumIds.Split('|'); + query.AlbumIds = GetGuids(request.AlbumIds); } // Albums @@ -441,7 +454,7 @@ namespace MediaBrowser.Api.UserLibrary Name = i, Limit = 1 - }).Select(albumId => albumId.ToString("N")); + }).Select(albumId => albumId); }).ToArray(); } @@ -459,7 +472,7 @@ namespace MediaBrowser.Api.UserLibrary { return null; } - }).Where(i => i != null).Select(i => i.Id.ToString("N")).ToArray(); + }).Where(i => i != null).Select(i => i.Id).ToArray(); } // Apply default sorting if none requested @@ -468,10 +481,10 @@ namespace MediaBrowser.Api.UserLibrary // Albums by artist if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], "MusicAlbum", StringComparison.OrdinalIgnoreCase)) { - query.OrderBy = new Tuple<string, SortOrder>[] + query.OrderBy = new [] { - new Tuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending), - new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) + new ValueTuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending), + new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) }; } } diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs index 36dc773d4d..d4f1b3fa8d 100644 --- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs +++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs @@ -32,7 +32,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } [Authenticated] @@ -47,7 +47,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetItem(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -61,7 +61,7 @@ namespace MediaBrowser.Api.UserLibrary var item = GetMusicGenre(request.Name, LibraryManager, dtoOptions); - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { var user = UserManager.GetUserById(request.UserId); @@ -80,7 +80,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetResultSlim(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query) diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index 9417447d84..d2c9ef33aa 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -7,6 +7,8 @@ using MediaBrowser.Model.Dto; using System.Collections.Generic; using System.Linq; using MediaBrowser.Model.Services; +using System; +using MediaBrowser.Model.Querying; namespace MediaBrowser.Api.UserLibrary { @@ -36,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } /// <summary> @@ -54,7 +56,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetItem(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -67,8 +69,8 @@ namespace MediaBrowser.Api.UserLibrary var dtoOptions = GetDtoOptions(AuthorizationContext, request); var item = GetPerson(request.Name, LibraryManager, dtoOptions); - - if (!string.IsNullOrWhiteSpace(request.UserId)) + + if (!request.UserId.Equals(Guid.Empty)) { var user = UserManager.GetUserById(request.UserId); @@ -85,9 +87,7 @@ namespace MediaBrowser.Api.UserLibrary /// <returns>System.Object.</returns> public object Get(GetPersons request) { - var result = GetResult(request); - - return ToOptimizedSerializedResultUsingCache(result); + return GetResultSlim(request); } /// <summary> @@ -98,48 +98,22 @@ namespace MediaBrowser.Api.UserLibrary /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns> protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items) { - var inputPersonTypes = ((GetPersons)request).PersonTypes; - var personTypes = string.IsNullOrEmpty(inputPersonTypes) ? new string[] { } : inputPersonTypes.Split(','); - - // Either get all people, or all people filtered by a specific person type - var allPeople = GetAllPeople(items, personTypes); - - return allPeople - .Select(i => i.Name) - .DistinctNames() - - .Select(name => - { - try - { - return LibraryManager.GetPerson(name); - } - catch - { - return null; - // Already logged at lower levels - } - } - - ).Where(i => i != null); + throw new NotImplementedException(); } - /// <summary> - /// Gets all people. - /// </summary> - /// <param name="itemsList">The items list.</param> - /// <param name="personTypes">The person types.</param> - /// <returns>IEnumerable{PersonInfo}.</returns> - private IEnumerable<PersonInfo> GetAllPeople(IList<BaseItem> itemsList, string[] personTypes) + protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query) { - var allIds = itemsList.Select(i => i.Id).ToArray(); - - var allPeople = LibraryManager.GetPeople(new InternalPeopleQuery + var items = LibraryManager.GetPeopleItems(new InternalPeopleQuery { - PersonTypes = personTypes + PersonTypes = query.PersonTypes, + NameContains = query.NameContains ?? query.SearchTerm }); - return allPeople.Where(i => allIds.Contains(i.ItemId)).OrderBy(p => p.SortOrder ?? int.MaxValue).ThenBy(p => p.Type); + return new QueryResult<Tuple<BaseItem, ItemCounts>> + { + TotalRecordCount = items.Count, + Items = items.Take(query.Limit ?? int.MaxValue).Select(i => new Tuple<BaseItem, ItemCounts>(i, new ItemCounts())).ToArray() + }; } public PersonsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs index f10cccbb13..4a3204f71f 100644 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } /// <summary> @@ -56,7 +56,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetItem(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -70,7 +70,7 @@ namespace MediaBrowser.Api.UserLibrary var item = GetStudio(request.Name, LibraryManager, dtoOptions); - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { var user = UserManager.GetUserById(request.UserId); @@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetResultSlim(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query) @@ -105,10 +105,7 @@ namespace MediaBrowser.Api.UserLibrary /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns> protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items) { - return items - .SelectMany(i => i.Studios) - .DistinctNames() - .Select(name => LibraryManager.GetStudio(name)); + throw new NotImplementedException(); } public StudiosService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepository, IDtoService dtoService, IAuthorizationContext authorizationContext) : base(userManager, libraryManager, userDataRepository, itemRepository, dtoService, authorizationContext) diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 1bbc740c01..30df0ad23c 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. @@ -50,7 +50,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } /// <summary> @@ -64,7 +64,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the item id. @@ -85,14 +85,14 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -106,14 +106,14 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -127,14 +127,14 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -148,14 +148,14 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. /// </summary> /// <value>The id.</value> [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } + public Guid Id { get; set; } /// <summary> /// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes. @@ -176,7 +176,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. @@ -197,7 +197,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. @@ -215,13 +215,13 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } [ApiMember(Name = "Limit", Description = "Limit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int Limit { get; set; } [ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string ParentId { get; set; } + public Guid ParentId { get; set; } [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Fields { get; set; } @@ -291,7 +291,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetAsync(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public object Get(GetLatestMedia request) @@ -344,43 +344,15 @@ namespace MediaBrowser.Api.UserLibrary var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? - user.RootFolder : + _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); - var series = item as Series; - - // Get them from the child tree - if (series != null) - { - var dtoOptions = GetDtoOptions(_authContext, request); - - // Avoid implicitly captured closure - var currentUser = user; - - var dtos = series - .GetEpisodes(user, dtoOptions) - .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) - .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, currentUser)); - - return dtos.ToArray(); - } - - var movie = item as IHasSpecialFeatures; - - // Get them from the db - if (movie != null) - { - var dtoOptions = GetDtoOptions(_authContext, request); - - var dtos = movie.SpecialFeatureIds - .Select(_libraryManager.GetItemById) - .OrderBy(i => i.SortName) - .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); + var dtoOptions = GetDtoOptions(_authContext, request); - return dtos.ToArray(); - } + var dtos = item.GetDisplayExtras() + .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); - return new BaseItemDto[] { }; + return dtos.ToArray(); } /// <summary> @@ -392,28 +364,15 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); - - List<Guid> trailerIds = null; - - var hasTrailers = item as IHasTrailers; - if (hasTrailers != null) - { - trailerIds = hasTrailers.GetTrailerIds(); - } - else - { - trailerIds = new List<Guid>(); - } + var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); var dtoOptions = GetDtoOptions(_authContext, request); - var dtos = trailerIds - .Select(_libraryManager.GetItemById) + var dtos = item.GetExtras(new[] { ExtraType.Trailer }) .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) .ToArray(); - return ToOptimizedSerializedResultUsingCache(dtos); + return ToOptimizedResult(dtos); } /// <summary> @@ -425,7 +384,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); + var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false); @@ -433,7 +392,7 @@ namespace MediaBrowser.Api.UserLibrary var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } private async Task RefreshItemOnDemandIfNeeded(BaseItem item) @@ -448,7 +407,7 @@ namespace MediaBrowser.Api.UserLibrary var options = new MetadataRefreshOptions(_fileSystem) { MetadataRefreshMode = MetadataRefreshMode.FullRefresh, - ImageRefreshMode = ImageRefreshMode.FullRefresh, + ImageRefreshMode = MetadataRefreshMode.FullRefresh, ForceSave = performFullRefresh }; @@ -466,13 +425,13 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var item = user.RootFolder; + var item = _libraryManager.GetUserRootFolder(); var dtoOptions = GetDtoOptions(_authContext, request); var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -484,7 +443,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); + var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); @@ -498,7 +457,7 @@ namespace MediaBrowser.Api.UserLibrary TotalRecordCount = dtos.Length }; - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -507,7 +466,7 @@ namespace MediaBrowser.Api.UserLibrary /// <param name="request">The request.</param> public object Post(MarkFavoriteItem request) { - var dto = MarkFavorite(request.UserId, request.Id, true); + var dto = MarkFavorite(request.UserId, request.Id, true); return ToOptimizedResult(dto); } @@ -529,11 +488,11 @@ namespace MediaBrowser.Api.UserLibrary /// <param name="userId">The user id.</param> /// <param name="itemId">The item id.</param> /// <param name="isFavorite">if set to <c>true</c> [is favorite].</param> - private UserItemDataDto MarkFavorite(string userId, string itemId, bool isFavorite) + private UserItemDataDto MarkFavorite(Guid userId, Guid itemId, bool isFavorite) { var user = _userManager.GetUserById(userId); - var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId); + var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); // Get the user data for this item var data = _userDataRepository.GetUserData(user, item); @@ -541,7 +500,7 @@ namespace MediaBrowser.Api.UserLibrary // Set favorite status data.IsFavorite = isFavorite; - _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None); + _userDataRepository.SaveUserData(user, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None); return _userDataRepository.GetUserDataDto(item, user); } @@ -563,7 +522,7 @@ namespace MediaBrowser.Api.UserLibrary /// <param name="request">The request.</param> public object Post(UpdateUserItemRating request) { - var dto = UpdateUserItemRating(request.UserId, request.Id, request.Likes); + var dto = UpdateUserItemRating(request.UserId, request.Id, request.Likes); return ToOptimizedResult(dto); } @@ -574,18 +533,18 @@ namespace MediaBrowser.Api.UserLibrary /// <param name="userId">The user id.</param> /// <param name="itemId">The item id.</param> /// <param name="likes">if set to <c>true</c> [likes].</param> - private UserItemDataDto UpdateUserItemRating(string userId, string itemId, bool? likes) + private UserItemDataDto UpdateUserItemRating(Guid userId, Guid itemId, bool? likes) { var user = _userManager.GetUserById(userId); - var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : _libraryManager.GetItemById(itemId); + var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); // Get the user data for this item var data = _userDataRepository.GetUserData(user, item); data.Likes = likes; - _userDataRepository.SaveUserData(user.Id, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None); + _userDataRepository.SaveUserData(user, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None); return _userDataRepository.GetUserDataDto(item, user); } diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index 096157e47f..5e9270e0bf 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -24,10 +24,11 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } - [ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")] + [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; } } @@ -40,7 +41,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } public class UserViewsService : BaseApiService @@ -49,16 +50,18 @@ namespace MediaBrowser.Api.UserLibrary private readonly IUserViewManager _userViewManager; private readonly IDtoService _dtoService; private readonly IAuthorizationContext _authContext; + private readonly ILibraryManager _libraryManager; - public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService, IAuthorizationContext authContext) + public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService, IAuthorizationContext authContext, ILibraryManager libraryManager) { _userManager = userManager; _userViewManager = userViewManager; _dtoService = dtoService; _authContext = authContext; + _libraryManager = libraryManager; } - public async Task<object> Get(GetUserViews request) + public object Get(GetUserViews request) { var query = new UserViewQuery { @@ -69,6 +72,7 @@ namespace MediaBrowser.Api.UserLibrary { query.IncludeExternalContent = request.IncludeExternalContent.Value; } + query.IncludeHidden = request.IncludeHidden; if (!string.IsNullOrWhiteSpace(request.PresetViews)) { @@ -78,18 +82,16 @@ namespace MediaBrowser.Api.UserLibrary var app = _authContext.GetAuthorizationInfo(Request).Client ?? string.Empty; if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1) { - query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows }; + query.PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows }; } - //query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows }; - var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false); + var folders = _userViewManager.GetUserViews(query); var dtoOptions = GetDtoOptions(_authContext, request); var fields = dtoOptions.Fields.ToList(); fields.Add(ItemFields.PrimaryImageAspectRatio); fields.Add(ItemFields.DisplayPreferencesId); - fields.Remove(ItemFields.SyncInfo); fields.Remove(ItemFields.BasicSyncInfo); dtoOptions.Fields = fields.ToArray(fields.Count); @@ -111,7 +113,7 @@ namespace MediaBrowser.Api.UserLibrary { var user = _userManager.GetUserById(request.UserId); - var list = user.RootFolder + var list = _libraryManager.GetUserRootFolder() .GetChildren(user, true) .OfType<Folder>() .Where(UserView.IsEligibleForGrouping) diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs index db622a9b34..30ac88e00f 100644 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ b/MediaBrowser.Api/UserLibrary/YearsService.cs @@ -7,6 +7,7 @@ using MediaBrowser.Model.Dto; using System.Collections.Generic; using System.Linq; using MediaBrowser.Model.Services; +using System; namespace MediaBrowser.Api.UserLibrary { @@ -36,7 +37,7 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <value>The user id.</value> [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } } /// <summary> @@ -54,7 +55,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetItem(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> @@ -68,7 +69,7 @@ namespace MediaBrowser.Api.UserLibrary var dtoOptions = GetDtoOptions(AuthorizationContext, request); - if (!string.IsNullOrWhiteSpace(request.UserId)) + if (!request.UserId.Equals(Guid.Empty)) { var user = UserManager.GetUserById(request.UserId); @@ -87,7 +88,7 @@ namespace MediaBrowser.Api.UserLibrary { var result = GetResult(request); - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } /// <summary> diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 66d6a024eb..29f3070a59 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using MediaBrowser.Model.Services; +using MediaBrowser.Controller.Authentication; namespace MediaBrowser.Api { @@ -51,22 +52,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The id.</value> [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - /// <summary> - /// Class GetUser - /// </summary> - [Route("/Users/{Id}/Offline", "GET", Summary = "Gets an offline user record by Id")] - [Authenticated] - public class GetOfflineUser : IReturn<UserDto> - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -81,7 +67,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The id.</value> [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -95,7 +81,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The id.</value> [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } + public Guid Id { get; set; } [ApiMember(Name = "Pw", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")] public string Pw { get; set; } @@ -130,9 +116,6 @@ namespace MediaBrowser.Api [ApiMember(Name = "Pw", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")] public string Pw { get; set; } - - [ApiMember(Name = "PasswordMd5", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")] - public string PasswordMd5 { get; set; } } /// <summary> @@ -146,7 +129,7 @@ namespace MediaBrowser.Api /// Gets or sets the id. /// </summary> /// <value>The id.</value> - public string Id { get; set; } + public Guid Id { get; set; } /// <summary> /// Gets or sets the password. @@ -156,12 +139,6 @@ namespace MediaBrowser.Api public string CurrentPw { get; set; } - /// <summary> - /// Gets or sets the new password. - /// </summary> - /// <value>The new password.</value> - public string NewPassword { get; set; } - public string NewPw { get; set; } /// <summary> @@ -182,7 +159,7 @@ namespace MediaBrowser.Api /// Gets or sets the id. /// </summary> /// <value>The id.</value> - public string Id { get; set; } + public Guid Id { get; set; } /// <summary> /// Gets or sets the new password. @@ -216,7 +193,7 @@ namespace MediaBrowser.Api public class UpdateUserPolicy : UserPolicy, IReturnVoid { [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -227,7 +204,7 @@ namespace MediaBrowser.Api public class UpdateUserConfiguration : UserConfiguration, IReturnVoid { [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } + public Guid Id { get; set; } } /// <summary> @@ -296,7 +273,7 @@ namespace MediaBrowser.Api IsHidden = false, IsDisabled = false - }, true); + }, true, true); } /// <summary> @@ -306,10 +283,10 @@ namespace MediaBrowser.Api /// <returns>System.Object.</returns> public object Get(GetUsers request) { - return Get(request, false); + return Get(request, false, false); } - private object Get(GetUsers request, bool filterByDevice) + private object Get(GetUsers request, bool filterByDevice, bool filterByNetwork) { var users = _userManager.Users; @@ -334,7 +311,15 @@ namespace MediaBrowser.Api if (!string.IsNullOrWhiteSpace(deviceId)) { - users = users.Where(i => _deviceManager.CanAccessDevice(i.Id.ToString("N"), deviceId)); + users = users.Where(i => _deviceManager.CanAccessDevice(i, deviceId)); + } + } + + if (filterByNetwork) + { + if (!_networkManager.IsInLocalNetwork(Request.RemoteIp)) + { + users = users.Where(i => i.Policy.EnableRemoteAccess); } } @@ -365,32 +350,16 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } - public object Get(GetOfflineUser request) - { - var user = _userManager.GetUserById(request.Id); - - if (user == null) - { - throw new ResourceNotFoundException("User not found"); - } - - var result = _userManager.GetOfflineUserDto(user); - - return ToOptimizedResult(result); - } - /// <summary> /// Deletes the specified request. /// </summary> /// <param name="request">The request.</param> - public void Delete(DeleteUser request) + public Task Delete(DeleteUser request) { - var task = DeleteAsync(request); - - Task.WaitAll(task); + return DeleteAsync(request); } - public async Task DeleteAsync(DeleteUser request) + public Task DeleteAsync(DeleteUser request) { var user = _userManager.GetUserById(request.Id); @@ -399,9 +368,9 @@ namespace MediaBrowser.Api throw new ResourceNotFoundException("User not found"); } - _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), null); + _sessionMananger.RevokeUserTokens(user.Id, null); - await _userManager.DeleteUser(user).ConfigureAwait(false); + return _userManager.DeleteUser(user); } /// <summary> @@ -437,7 +406,6 @@ namespace MediaBrowser.Api DeviceName = auth.Device, Password = request.Pw, PasswordSha1 = request.Password, - PasswordMd5 = request.PasswordMd5, RemoteEndPoint = Request.RemoteIp, Username = request.Username @@ -450,10 +418,9 @@ namespace MediaBrowser.Api /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(UpdateUserPassword request) + public Task Post(UpdateUserPassword request) { - var task = PostAsync(request); - Task.WaitAll(task); + return PostAsync(request); } public async Task PostAsync(UpdateUserPassword request) @@ -469,22 +436,22 @@ namespace MediaBrowser.Api if (request.ResetPassword) { - _userManager.ResetPassword(user); + await _userManager.ResetPassword(user).ConfigureAwait(false); } else { - var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, null, Request.RemoteIp, false).ConfigureAwait(false); + var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, Request.RemoteIp, false).ConfigureAwait(false); if (success == null) { throw new ArgumentException("Invalid user or password entered."); } - _userManager.ChangePassword(user, request.NewPw, request.NewPassword); + await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false); var currentToken = _authContext.GetAuthorizationInfo(Request).Token; - _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken); + _sessionMananger.RevokeUserTokens(user.Id, currentToken); } } @@ -513,11 +480,11 @@ namespace MediaBrowser.Api /// Posts the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(UpdateUser request) + public async Task Post(UpdateUser request) { var id = GetPathValue(1); - AssertCanUpdateUser(_authContext, _userManager, id, false); + AssertCanUpdateUser(_authContext, _userManager, new Guid(id), false); var dtoUser = request; @@ -526,15 +493,14 @@ namespace MediaBrowser.Api if (string.Equals(user.Name, dtoUser.Name, StringComparison.Ordinal)) { _userManager.UpdateUser(user); + _userManager.UpdateConfiguration(user, dtoUser.Configuration); } else { - var task = _userManager.RenameUser(user, dtoUser.Name); + await _userManager.RenameUser(user, dtoUser.Name).ConfigureAwait(false); - Task.WaitAll(task); + _userManager.UpdateConfiguration(dtoUser.Id, dtoUser.Configuration); } - - _userManager.UpdateConfiguration(dtoUser.Id, dtoUser.Configuration); } /// <summary> @@ -542,11 +508,11 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <returns>System.Object.</returns> - public object Post(CreateUserByName request) + public async Task<object> Post(CreateUserByName request) { var dtoUser = request; - var newUser = _userManager.CreateUser(dtoUser.Name).Result; + var newUser = await _userManager.CreateUser(dtoUser.Name).ConfigureAwait(false); var result = _userManager.GetUserDto(newUser, Request.RemoteIp); @@ -558,16 +524,20 @@ namespace MediaBrowser.Api /// </summary> /// <param name="request">The request.</param> /// <returns>System.Object.</returns> - public object Post(ForgotPassword request) + public async Task<object> Post(ForgotPassword request) { var isLocal = Request.IsLocal || _networkManager.IsInLocalNetwork(Request.RemoteIp); - return _userManager.StartForgotPasswordProcess(request.EnteredUsername, isLocal); + var result = await _userManager.StartForgotPasswordProcess(request.EnteredUsername, isLocal).ConfigureAwait(false); + + return result; } - public object Post(ForgotPasswordPin request) + public async Task<object> Post(ForgotPasswordPin request) { - return _userManager.RedeemPasswordResetPin(request.Pin); + var result = await _userManager.RedeemPasswordResetPin(request.Pin).ConfigureAwait(false); + + return result; } public void Post(UpdateUserConfiguration request) @@ -606,7 +576,7 @@ namespace MediaBrowser.Api } var currentToken = _authContext.GetAuthorizationInfo(Request).Token; - _sessionMananger.RevokeUserTokens(user.Id.ToString("N"), currentToken); + _sessionMananger.RevokeUserTokens(user.Id, currentToken); } _userManager.UpdateUserPolicy(request.Id, request); diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs index 3f4bb46f45..40d2e066c5 100644 --- a/MediaBrowser.Api/VideosService.cs +++ b/MediaBrowser.Api/VideosService.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Api public class GetAdditionalParts : IReturn<QueryResult<BaseItemDto>> { [ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserId { get; set; } + public Guid UserId { get; set; } /// <summary> /// Gets or sets the id. @@ -76,11 +76,11 @@ namespace MediaBrowser.Api /// <returns>System.Object.</returns> public object Get(GetAdditionalParts request) { - var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null; var item = string.IsNullOrEmpty(request.Id) - ? (!string.IsNullOrWhiteSpace(request.UserId) - ? user.RootFolder + ? (!request.UserId.Equals(Guid.Empty) + ? _libraryManager.GetUserRootFolder() : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); @@ -105,7 +105,7 @@ namespace MediaBrowser.Api TotalRecordCount = items.Length }; - return ToOptimizedSerializedResultUsingCache(result); + return ToOptimizedResult(result); } public void Delete(DeleteAlternateSources request) @@ -115,12 +115,12 @@ namespace MediaBrowser.Api foreach (var link in video.GetLinkedAlternateVersions()) { link.SetPrimaryVersionId(null); - link.LinkedAlternateVersions = Video.EmptyLinkedChildArray; + link.LinkedAlternateVersions = Array.Empty<LinkedChild>(); link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); } - video.LinkedAlternateVersions = Video.EmptyLinkedChildArray; + video.LinkedAlternateVersions = Array.Empty<LinkedChild>(); video.SetPrimaryVersionId(null); video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); } @@ -140,11 +140,6 @@ namespace MediaBrowser.Api var videosWithVersions = items.Where(i => i.MediaSourceCount > 1) .ToList(); - if (videosWithVersions.Count > 1) - { - throw new ArgumentException("Videos with sub-versions cannot be merged."); - } - var primaryVersion = videosWithVersions.FirstOrDefault(); if (primaryVersion == null) @@ -185,10 +180,23 @@ namespace MediaBrowser.Api Path = item.Path, ItemId = item.Id }); + + foreach (var linkedItem in item.LinkedAlternateVersions) + { + if (!list.Any(i => string.Equals(i.Path, linkedItem.Path, StringComparison.OrdinalIgnoreCase))) + { + list.Add(linkedItem); + } + } + + if (item.LinkedAlternateVersions.Length > 0) + { + item.LinkedAlternateVersions = Array.Empty<LinkedChild>(); + item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); + } } primaryVersion.LinkedAlternateVersions = list.ToArray(); - primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); } } |
