diff options
Diffstat (limited to 'MediaBrowser.Api')
47 files changed, 137 insertions, 6761 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 9e651fb197..b041effb2e 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.Api private readonly IMediaSourceManager _mediaSourceManager; /// <summary> - /// The active transcoding jobs + /// The active transcoding jobs. /// </summary> private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>(); @@ -293,7 +293,7 @@ namespace MediaBrowser.Api /// <summary> /// <summary> - /// The progressive + /// The progressive. /// </summary> /// Called when [transcode failed to start]. /// </summary> diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 2ece16ee15..63a31a7452 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -17,7 +17,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { /// <summary> - /// Class BaseApiService + /// Class BaseApiService. /// </summary> public abstract class BaseApiService : IService, IRequiresRequest { @@ -268,7 +268,6 @@ namespace MediaBrowser.Api Name = name.Replace(BaseItem.SlugChar, '&'), IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<T>().FirstOrDefault(); result ??= libraryManager.GetItemList(new InternalItemsQuery @@ -276,7 +275,6 @@ namespace MediaBrowser.Api Name = name.Replace(BaseItem.SlugChar, '/'), IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<T>().FirstOrDefault(); result ??= libraryManager.GetItemList(new InternalItemsQuery @@ -284,7 +282,6 @@ namespace MediaBrowser.Api Name = name.Replace(BaseItem.SlugChar, '?'), IncludeItemTypes = new[] { typeof(T).Name }, DtoOptions = dtoOptions - }).OfType<T>().FirstOrDefault(); return result; diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs deleted file mode 100644 index fd9b8c3968..0000000000 --- a/MediaBrowser.Api/ChannelService.cs +++ /dev/null @@ -1,341 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Api.UserLibrary; -using MediaBrowser.Controller.Channels; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Channels; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api -{ - [Route("/Channels", "GET", Summary = "Gets available channels")] - public class GetChannels : IReturn<QueryResult<BaseItemDto>> - { - /// <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 Guid UserId { get; set; } - - /// <summary> - /// Skips over a given number of items within the results. Use for paging. - /// </summary> - /// <value>The start index.</value> - [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; } - - /// <summary> - /// The maximum number of items to return - /// </summary> - /// <value>The limit.</value> - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - - [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> - /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value> - public bool? IsFavorite { get; set; } - } - - [Route("/Channels/{Id}/Features", "GET", Summary = "Gets features for a channel")] - public class GetChannelFeatures : IReturn<ChannelFeatures> - { - [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/Channels/Features", "GET", Summary = "Gets features for a channel")] - public class GetAllChannelFeatures : IReturn<ChannelFeatures[]> - { - } - - [Route("/Channels/{Id}/Items", "GET", Summary = "Gets channel items")] - public class GetChannelItems : IReturn<QueryResult<BaseItemDto>>, IHasItemFields - { - [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - - [ApiMember(Name = "FolderId", Description = "Folder Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string FolderId { 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 Guid UserId { get; set; } - - /// <summary> - /// Skips over a given number of items within the results. Use for paging. - /// </summary> - /// <value>The start index.</value> - [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; } - - /// <summary> - /// The maximum number of items to return - /// </summary> - /// <value>The limit.</value> - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - - [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string SortOrder { get; set; } - - [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Filters { get; set; } - - [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string SortBy { get; set; } - - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - - /// <summary> - /// Gets the filters. - /// </summary> - /// <returns>IEnumerable{ItemFilter}.</returns> - public IEnumerable<ItemFilter> GetFilters() - { - var val = Filters; - - return string.IsNullOrEmpty(val) - ? Array.Empty<ItemFilter>() - : val.Split(',').Select(v => Enum.Parse<ItemFilter>(v, true)); - } - - /// <summary> - /// Gets the order by. - /// </summary> - /// <returns>IEnumerable{ItemSortBy}.</returns> - public ValueTuple<string, SortOrder>[] GetOrderBy() - { - return BaseItemsRequest.GetOrderBy(SortBy, SortOrder); - } - } - - [Route("/Channels/Items/Latest", "GET", Summary = "Gets channel items")] - public class GetLatestChannelItems : IReturn<QueryResult<BaseItemDto>>, IHasItemFields - { - /// <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 Guid UserId { get; set; } - - /// <summary> - /// Skips over a given number of items within the results. Use for paging. - /// </summary> - /// <value>The start index.</value> - [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; } - - /// <summary> - /// The maximum number of items to return - /// </summary> - /// <value>The limit.</value> - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - - [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Filters { get; set; } - - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - - [ApiMember(Name = "ChannelIds", Description = "Optional. Specify one or more channel id's, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ChannelIds { get; set; } - - /// <summary> - /// Gets the filters. - /// </summary> - /// <returns>IEnumerable{ItemFilter}.</returns> - public IEnumerable<ItemFilter> GetFilters() - { - return string.IsNullOrEmpty(Filters) - ? Array.Empty<ItemFilter>() - : Filters.Split(',').Select(v => Enum.Parse<ItemFilter>(v, true)); - } - } - - [Authenticated] - public class ChannelService : BaseApiService - { - private readonly IChannelManager _channelManager; - private IUserManager _userManager; - - public ChannelService( - ILogger<ChannelService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IChannelManager channelManager, - IUserManager userManager) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _channelManager = channelManager; - _userManager = userManager; - } - - public object Get(GetAllChannelFeatures request) - { - var result = _channelManager.GetAllChannelFeatures(); - - return ToOptimizedResult(result); - } - - public object Get(GetChannelFeatures request) - { - var result = _channelManager.GetChannelFeatures(request.Id); - - return ToOptimizedResult(result); - } - - public object Get(GetChannels request) - { - var result = _channelManager.GetChannels(new ChannelQuery - { - Limit = request.Limit, - StartIndex = request.StartIndex, - UserId = request.UserId, - SupportsLatestItems = request.SupportsLatestItems, - SupportsMediaDeletion = request.SupportsMediaDeletion, - IsFavorite = request.IsFavorite - }); - - return ToOptimizedResult(result); - } - - public async Task<object> Get(GetChannelItems request) - { - var user = request.UserId.Equals(Guid.Empty) - ? null - : _userManager.GetUserById(request.UserId); - - var query = new InternalItemsQuery(user) - { - Limit = request.Limit, - StartIndex = request.StartIndex, - ChannelIds = new[] { new Guid(request.Id) }, - ParentId = string.IsNullOrWhiteSpace(request.FolderId) ? Guid.Empty : new Guid(request.FolderId), - OrderBy = request.GetOrderBy(), - 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; - } - } - - var result = await _channelManager.GetChannelItems(query, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - - public async Task<object> Get(GetLatestChannelItems request) - { - 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)).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; - } - } - - var result = await _channelManager.GetLatestChannelItems(query, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - } -} diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs deleted file mode 100644 index dd3f3e738a..0000000000 --- a/MediaBrowser.Api/Devices/DeviceService.cs +++ /dev/null @@ -1,104 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Security; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Devices; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.Devices -{ - [Route("/Devices", "GET", Summary = "Gets all devices")] - [Authenticated(Roles = "Admin")] - public class GetDevices : DeviceQuery, IReturn<QueryResult<DeviceInfo>> - { - } - - [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 - { - [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 - { - [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string Id { get; set; } - } - - public class DeviceService : BaseApiService - { - private readonly IDeviceManager _deviceManager; - private readonly IAuthenticationRepository _authRepo; - private readonly ISessionManager _sessionManager; - - public DeviceService( - ILogger<DeviceService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IDeviceManager deviceManager, - IAuthenticationRepository authRepo, - ISessionManager sessionManager) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _deviceManager = deviceManager; - _authRepo = authRepo; - _sessionManager = sessionManager; - } - - public void Post(PostDeviceOptions request) - { - _deviceManager.UpdateDeviceOptions(request.Id, request); - } - - public object Get(GetDevices request) - { - return ToOptimizedResult(_deviceManager.GetDevices(request)); - } - - public object Get(GetDeviceInfo request) - { - return _deviceManager.GetDevice(request.Id); - } - - public object Get(GetDeviceOptions request) - { - return _deviceManager.GetDeviceOptions(request.Id); - } - - public void Delete(DeleteDevice request) - { - var sessions = _authRepo.Get(new AuthenticationInfoQuery - { - DeviceId = request.Id - - }).Items; - - foreach (var session in sessions) - { - _sessionManager.Logout(session); - } - } - } -} diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs deleted file mode 100644 index 82d471412d..0000000000 --- a/MediaBrowser.Api/EnvironmentService.cs +++ /dev/null @@ -1,285 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api -{ - /// <summary> - /// Class GetDirectoryContents - /// </summary> - [Route("/Environment/DirectoryContents", "GET", Summary = "Gets the contents of a given directory in the file system")] - public class GetDirectoryContents : IReturn<List<FileSystemEntryInfo>> - { - /// <summary> - /// Gets or sets the path. - /// </summary> - /// <value>The path.</value> - [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Path { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether [include files]. - /// </summary> - /// <value><c>true</c> if [include files]; otherwise, <c>false</c>.</value> - [ApiMember(Name = "IncludeFiles", Description = "An optional filter to include or exclude files from the results. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool IncludeFiles { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether [include directories]. - /// </summary> - /// <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; } - } - - [Route("/Environment/ValidatePath", "POST", Summary = "Gets the contents of a given directory in the file system")] - public class ValidatePath - { - /// <summary> - /// Gets or sets the path. - /// </summary> - /// <value>The path.</value> - [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string Path { get; set; } - - public bool ValidateWriteable { get; set; } - public bool? IsFile { get; set; } - } - - [Obsolete] - [Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")] - public class GetNetworkShares : IReturn<List<FileSystemEntryInfo>> - { - /// <summary> - /// Gets or sets the path. - /// </summary> - /// <value>The path.</value> - [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Path { get; set; } - } - - /// <summary> - /// Class GetDrives - /// </summary> - [Route("/Environment/Drives", "GET", Summary = "Gets available drives from the server's file system")] - public class GetDrives : IReturn<List<FileSystemEntryInfo>> - { - } - - /// <summary> - /// Class GetNetworkComputers - /// </summary> - [Route("/Environment/NetworkDevices", "GET", Summary = "Gets a list of devices on the network")] - public class GetNetworkDevices : IReturn<List<FileSystemEntryInfo>> - { - } - - [Route("/Environment/ParentPath", "GET", Summary = "Gets the parent path of a given path")] - public class GetParentPath : IReturn<string> - { - /// <summary> - /// Gets or sets the path. - /// </summary> - /// <value>The path.</value> - [ApiMember(Name = "Path", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Path { get; set; } - } - - public class DefaultDirectoryBrowserInfo - { - public string Path { get; set; } - } - - [Route("/Environment/DefaultDirectoryBrowser", "GET", Summary = "Gets the parent path of a given path")] - public class GetDefaultDirectoryBrowser : IReturn<DefaultDirectoryBrowserInfo> - { - - } - - /// <summary> - /// Class EnvironmentService - /// </summary> - [Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)] - public class EnvironmentService : BaseApiService - { - private const char UncSeparator = '\\'; - private const string UncSeparatorString = "\\"; - - /// <summary> - /// The _network manager - /// </summary> - private readonly INetworkManager _networkManager; - private readonly IFileSystem _fileSystem; - - /// <summary> - /// Initializes a new instance of the <see cref="EnvironmentService" /> class. - /// </summary> - /// <param name="networkManager">The network manager.</param> - public EnvironmentService( - ILogger<EnvironmentService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - INetworkManager networkManager, - IFileSystem fileSystem) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _networkManager = networkManager; - _fileSystem = fileSystem; - } - - public void Post(ValidatePath request) - { - if (request.IsFile.HasValue) - { - if (request.IsFile.Value) - { - if (!File.Exists(request.Path)) - { - throw new FileNotFoundException("File not found", request.Path); - } - } - else - { - if (!Directory.Exists(request.Path)) - { - throw new FileNotFoundException("File not found", request.Path); - } - } - } - - else - { - if (!File.Exists(request.Path) && !Directory.Exists(request.Path)) - { - throw new FileNotFoundException("Path not found", request.Path); - } - - if (request.ValidateWriteable) - { - EnsureWriteAccess(request.Path); - } - } - } - - protected void EnsureWriteAccess(string path) - { - var file = Path.Combine(path, Guid.NewGuid().ToString()); - - File.WriteAllText(file, string.Empty); - _fileSystem.DeleteFile(file); - } - - public object Get(GetDefaultDirectoryBrowser request) => - ToOptimizedResult(new DefaultDirectoryBrowserInfo { Path = null }); - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetDirectoryContents request) - { - var path = request.Path; - - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(nameof(Path)); - } - - var networkPrefix = UncSeparatorString + UncSeparatorString; - - if (path.StartsWith(networkPrefix, StringComparison.OrdinalIgnoreCase) - && path.LastIndexOf(UncSeparator) == 1) - { - return ToOptimizedResult(Array.Empty<FileSystemEntryInfo>()); - } - - return ToOptimizedResult(GetFileSystemEntries(request).ToList()); - } - - [Obsolete] - public object Get(GetNetworkShares request) - => ToOptimizedResult(Array.Empty<FileSystemEntryInfo>()); - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetDrives request) - { - var result = GetDrives().ToList(); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the list that is returned when an empty path is supplied - /// </summary> - /// <returns>IEnumerable{FileSystemEntryInfo}.</returns> - private IEnumerable<FileSystemEntryInfo> GetDrives() - { - return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo(d.Name, d.FullName, FileSystemEntryType.Directory)); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetNetworkDevices request) - => ToOptimizedResult(Array.Empty<FileSystemEntryInfo>()); - - /// <summary> - /// Gets the file system entries. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>IEnumerable{FileSystemEntryInfo}.</returns> - private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request) - { - var entries = _fileSystem.GetFileSystemEntries(request.Path).OrderBy(i => i.FullName).Where(i => - { - var isDirectory = i.IsDirectory; - - if (!request.IncludeFiles && !isDirectory) - { - return false; - } - - return request.IncludeDirectories || !isDirectory; - }); - - return entries.Select(f => new FileSystemEntryInfo(f.Name, f.FullName, f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File)); - } - - public object Get(GetParentPath request) - { - var parent = Path.GetDirectoryName(request.Path); - - if (string.IsNullOrEmpty(parent)) - { - // Check if unc share - var index = request.Path.LastIndexOf(UncSeparator); - - if (index != -1 && request.Path.IndexOf(UncSeparator) == 0) - { - parent = request.Path.Substring(0, index); - - if (string.IsNullOrWhiteSpace(parent.TrimStart(UncSeparator))) - { - parent = null; - } - } - } - - return parent; - } - } -} diff --git a/MediaBrowser.Api/IHasDtoOptions.cs b/MediaBrowser.Api/IHasDtoOptions.cs index 03d3b3692f..33d498e8bd 100644 --- a/MediaBrowser.Api/IHasDtoOptions.cs +++ b/MediaBrowser.Api/IHasDtoOptions.cs @@ -3,6 +3,7 @@ namespace MediaBrowser.Api public interface IHasDtoOptions : IHasItemFields { bool? EnableImages { get; set; } + bool? EnableUserData { get; set; } int? ImageTypeLimit { get; set; } diff --git a/MediaBrowser.Api/IHasItemFields.cs b/MediaBrowser.Api/IHasItemFields.cs index 85b4a7e2d2..ad4f1b4891 100644 --- a/MediaBrowser.Api/IHasItemFields.cs +++ b/MediaBrowser.Api/IHasItemFields.cs @@ -5,7 +5,7 @@ using MediaBrowser.Model.Querying; namespace MediaBrowser.Api { /// <summary> - /// Interface IHasItemFields + /// Interface IHasItemFields. /// </summary> public interface IHasItemFields { @@ -43,7 +43,6 @@ namespace MediaBrowser.Api } return null; - }).Where(i => i.HasValue).Select(i => i.Value).ToArray(); } } diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs index 71ff09b63f..0f3455548d 100644 --- a/MediaBrowser.Api/Images/ImageRequest.cs +++ b/MediaBrowser.Api/Images/ImageRequest.cs @@ -4,30 +4,30 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Api.Images { /// <summary> - /// Class ImageRequest + /// Class ImageRequest. /// </summary> public class ImageRequest : DeleteImageRequest { /// <summary> - /// The max width + /// The max width. /// </summary> [ApiMember(Name = "MaxWidth", Description = "The maximum image width to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? MaxWidth { get; set; } /// <summary> - /// The max height + /// The max height. /// </summary> [ApiMember(Name = "MaxHeight", Description = "The maximum image height to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? MaxHeight { get; set; } /// <summary> - /// The width + /// The width. /// </summary> [ApiMember(Name = "Width", Description = "The fixed image width to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Width { get; set; } /// <summary> - /// The height + /// The height. /// </summary> [ApiMember(Name = "Height", Description = "The fixed image height to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Height { get; set; } @@ -79,7 +79,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class DeleteImageRequest + /// Class DeleteImageRequest. /// </summary> public class DeleteImageRequest { diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index a98266e0d2..55859d9f17 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class GetPersonImage + /// Class GetPersonImage. /// </summary> [Route("/Artists/{Name}/Images/{Type}", "GET")] [Route("/Artists/{Name}/Images/{Type}/{Index}", "GET")] @@ -78,7 +78,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class GetUserImage + /// Class GetUserImage. /// </summary> [Route("/Users/{Id}/Images/{Type}", "GET")] [Route("/Users/{Id}/Images/{Type}/{Index}", "GET")] @@ -95,7 +95,7 @@ namespace MediaBrowser.Api.Images } /// <summary> - /// Class ImageService + /// Class ImageService. /// </summary> public class ImageService : BaseApiService { @@ -405,7 +405,6 @@ namespace MediaBrowser.Api.Images Path = imageResult.Item1, FileShare = FileShare.Read - }).ConfigureAwait(false); } @@ -558,6 +557,11 @@ namespace MediaBrowser.Api.Images // Handle image/png; charset=utf-8 mimeType = mimeType.Split(';').FirstOrDefault(); var userDataPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username); + if (user.ProfileImage != null) + { + _userManager.ClearProfileImage(user); + } + user.ProfileImage = new Jellyfin.Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType))); await _providerManager diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs deleted file mode 100644 index 279fd6ee9b..0000000000 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ /dev/null @@ -1,1279 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Data.Enums; -using MediaBrowser.Api.UserLibrary; -using MediaBrowser.Common; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.LiveTv; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; - -namespace MediaBrowser.Api.LiveTv -{ - /// <summary> - /// This is insecure right now to avoid windows phone refactoring - /// </summary> - [Route("/LiveTv/Info", "GET", Summary = "Gets available live tv services.")] - [Authenticated] - public class GetLiveTvInfo : IReturn<LiveTvInfo> - { - } - - [Route("/LiveTv/Channels", "GET", Summary = "Gets available live tv channels.")] - [Authenticated] - 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 Guid UserId { get; set; } - - /// <summary> - /// Skips over a given number of items within the results. Use for paging. - /// </summary> - /// <value>The start index.</value> - [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 = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsMovie { get; set; } - - [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSeries { get; set; } - - [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsNews { get; set; } - - [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsKids { get; set; } - - [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSports { get; set; } - - /// <summary> - /// The maximum number of items to return - /// </summary> - /// <value>The limit.</value> - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - - [ApiMember(Name = "IsFavorite", Description = "Filter by channels that are favorites, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsFavorite { get; set; } - - [ApiMember(Name = "IsLiked", Description = "Filter by channels that are liked, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsLiked { get; set; } - - [ApiMember(Name = "IsDisliked", Description = "Filter by channels that are disliked, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsDisliked { get; set; } - - [ApiMember(Name = "EnableFavoriteSorting", Description = "Incorporate favorite and like status into channel sorting.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool EnableFavoriteSorting { get; set; } - - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - /// <summary> - /// Fields to return within the items, in addition to basic information - /// </summary> - /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - - [ApiMember(Name = "AddCurrentProgram", Description = "Optional. Adds current program info to each channel", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool AddCurrentProgram { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - - public string SortBy { get; set; } - - public SortOrder? SortOrder { get; set; } - - /// <summary> - /// Gets the order by. - /// </summary> - /// <returns>IEnumerable{ItemSortBy}.</returns> - public string[] GetOrderBy() - { - var val = SortBy; - - if (string.IsNullOrEmpty(val)) - { - return Array.Empty<string>(); - } - - return val.Split(','); - } - - public GetChannels() - { - AddCurrentProgram = true; - } - } - - [Route("/LiveTv/Channels/{Id}", "GET", Summary = "Gets a live tv channel")] - [Authenticated] - public class GetChannel : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - - [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public Guid UserId { get; set; } - } - - [Route("/LiveTv/Recordings", "GET", Summary = "Gets live tv recordings")] - [Authenticated] - public class GetRecordings : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions - { - [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - 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 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; } - - [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 = "Status", Description = "Optional filter by recording status.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public RecordingStatus? Status { get; set; } - - [ApiMember(Name = "Status", Description = "Optional filter by recordings that are in progress, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsInProgress { get; set; } - - [ApiMember(Name = "SeriesTimerId", Description = "Optional filter by recordings belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string SeriesTimerId { get; set; } - - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - /// <summary> - /// Fields to return within the items, in addition to basic information - /// </summary> - /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - - public bool EnableTotalRecordCount { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - - public bool? IsMovie { get; set; } - public bool? IsSeries { get; set; } - public bool? IsKids { get; set; } - public bool? IsSports { get; set; } - public bool? IsNews { get; set; } - public bool? IsLibraryItem { get; set; } - - public GetRecordings() - { - EnableTotalRecordCount = true; - } - } - - [Route("/LiveTv/Recordings/Series", "GET", Summary = "Gets live tv recordings")] - [Authenticated] - public class GetRecordingSeries : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions - { - [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - 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; } - - [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; } - - [ApiMember(Name = "Status", Description = "Optional filter by recording status.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public RecordingStatus? Status { get; set; } - - [ApiMember(Name = "Status", Description = "Optional filter by recordings that are in progress, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsInProgress { get; set; } - - [ApiMember(Name = "SeriesTimerId", Description = "Optional filter by recordings belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string SeriesTimerId { get; set; } - - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - /// <summary> - /// Fields to return within the items, in addition to basic information - /// </summary> - /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - - public bool EnableTotalRecordCount { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - - public GetRecordingSeries() - { - EnableTotalRecordCount = true; - } - } - - [Route("/LiveTv/Recordings/Groups", "GET", Summary = "Gets live tv recording groups")] - [Authenticated] - public class GetRecordingGroups : IReturn<QueryResult<BaseItemDto>> - { - [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; } - } - - [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> - { - [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - - [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public Guid UserId { get; set; } - } - - [Route("/LiveTv/Tuners/{Id}/Reset", "POST", Summary = "Resets a tv tuner")] - [Authenticated] - public class ResetTuner : IReturnVoid - { - [ApiMember(Name = "Id", Description = "Tuner Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/LiveTv/Timers/{Id}", "GET", Summary = "Gets a live tv timer")] - [Authenticated] - public class GetTimer : IReturn<TimerInfoDto> - { - [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/LiveTv/Timers/Defaults", "GET", Summary = "Gets default values for a new timer")] - [Authenticated] - public class GetDefaultTimer : IReturn<SeriesTimerInfoDto> - { - [ApiMember(Name = "ProgramId", Description = "Optional, to attach default values based on a program.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string ProgramId { get; set; } - } - - [Route("/LiveTv/Timers", "GET", Summary = "Gets live tv timers")] - [Authenticated] - public class GetTimers : IReturn<QueryResult<TimerInfoDto>> - { - [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string ChannelId { get; set; } - - [ApiMember(Name = "SeriesTimerId", Description = "Optional filter by timers belonging to a series timer", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string SeriesTimerId { get; set; } - - public bool? IsActive { get; set; } - - public bool? IsScheduled { get; set; } - } - - [Route("/LiveTv/Programs", "GET,POST", Summary = "Gets available live tv epgs..")] - [Authenticated] - public class GetPrograms : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions - { - [ApiMember(Name = "ChannelIds", Description = "The channels to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string ChannelIds { get; set; } - - [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - 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; } - - [ApiMember(Name = "MinEndDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string MinEndDate { get; set; } - - [ApiMember(Name = "MaxEndDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string MaxEndDate { get; set; } - - [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsMovie { get; set; } - - [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSeries { get; set; } - - [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsNews { get; set; } - - [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsKids { get; set; } - - [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSports { 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; } - - [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Name, StartDate", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string SortBy { get; set; } - - [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string SortOrder { get; set; } - - [ApiMember(Name = "Genres", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string Genres { get; set; } - - [ApiMember(Name = "GenreIds", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string GenreIds { get; set; } - - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - public bool EnableTotalRecordCount { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - - public string SeriesTimerId { get; set; } - public Guid LibrarySeriesId { get; set; } - - /// <summary> - /// Fields to return within the items, in addition to basic information - /// </summary> - /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - - public GetPrograms() - { - EnableTotalRecordCount = true; - } - } - - [Route("/LiveTv/Programs/Recommended", "GET", Summary = "Gets available live tv epgs..")] - [Authenticated] - public class GetRecommendedPrograms : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions - { - public bool EnableTotalRecordCount { get; set; } - - public GetRecommendedPrograms() - { - EnableTotalRecordCount = true; - } - - [ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - 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; } - - [ApiMember(Name = "IsAiring", Description = "Optional. Filter by programs that are currently airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsAiring { 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; } - - [ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSeries { get; set; } - - [ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsMovie { get; set; } - - [ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsNews { get; set; } - - [ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsKids { get; set; } - - [ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")] - public bool? IsSports { get; set; } - - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - [ApiMember(Name = "GenreIds", Description = "The genres to return guide information for.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string GenreIds { get; set; } - - /// <summary> - /// Fields to return within the items, in addition to basic information - /// </summary> - /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - } - - [Route("/LiveTv/Programs/{Id}", "GET", Summary = "Gets a live tv program")] - [Authenticated] - public class GetProgram : IReturn<BaseItemDto> - { - [ApiMember(Name = "Id", Description = "Program Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - - [ApiMember(Name = "UserId", Description = "Optional attach user data.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public Guid UserId { get; set; } - } - - - [Route("/LiveTv/Recordings/{Id}", "DELETE", Summary = "Deletes a live tv recording")] - [Authenticated] - public class DeleteRecording : IReturnVoid - { - [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 = "DELETE")] - public string Id { get; set; } - } - - [Route("/LiveTv/Timers/{Id}", "POST", Summary = "Updates a live tv timer")] - [Authenticated] - public class UpdateTimer : TimerInfoDto, IReturnVoid - { - } - - [Route("/LiveTv/Timers", "POST", Summary = "Creates a live tv timer")] - [Authenticated] - public class CreateTimer : TimerInfoDto, IReturnVoid - { - } - - [Route("/LiveTv/SeriesTimers/{Id}", "GET", Summary = "Gets a live tv series timer")] - [Authenticated] - public class GetSeriesTimer : IReturn<TimerInfoDto> - { - [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/LiveTv/SeriesTimers", "GET", Summary = "Gets live tv series timers")] - [Authenticated] - public class GetSeriesTimers : IReturn<QueryResult<SeriesTimerInfoDto>> - { - [ApiMember(Name = "SortBy", Description = "Optional. Sort by SortName or Priority", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public string SortBy { get; set; } - - [ApiMember(Name = "SortOrder", Description = "Optional. Sort in Ascending or Descending order", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")] - public SortOrder SortOrder { get; set; } - } - - [Route("/LiveTv/SeriesTimers/{Id}", "DELETE", Summary = "Cancels a live tv series timer")] - [Authenticated] - public class CancelSeriesTimer : IReturnVoid - { - [ApiMember(Name = "Id", Description = "Timer Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } - } - - [Route("/LiveTv/SeriesTimers/{Id}", "POST", Summary = "Updates a live tv series timer")] - [Authenticated] - public class UpdateSeriesTimer : SeriesTimerInfoDto, IReturnVoid - { - } - - [Route("/LiveTv/SeriesTimers", "POST", Summary = "Creates a live tv series timer")] - [Authenticated] - public class CreateSeriesTimer : SeriesTimerInfoDto, IReturnVoid - { - } - - [Route("/LiveTv/Recordings/Groups/{Id}", "GET", Summary = "Gets a recording group")] - [Authenticated] - public class GetRecordingGroup : IReturn<BaseItemDto> - { - [ApiMember(Name = "Id", Description = "Recording group Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/LiveTv/GuideInfo", "GET", Summary = "Gets guide info")] - [Authenticated] - public class GetGuideInfo : IReturn<GuideInfo> - { - } - - [Route("/LiveTv/TunerHosts", "POST", Summary = "Adds a tuner host")] - [Authenticated] - public class AddTunerHost : TunerHostInfo, IReturn<TunerHostInfo> - { - } - - [Route("/LiveTv/TunerHosts", "DELETE", Summary = "Deletes a tuner host")] - [Authenticated] - public class DeleteTunerHost : IReturnVoid - { - [ApiMember(Name = "Id", Description = "Tuner host id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string Id { get; set; } - } - - [Route("/LiveTv/ListingProviders/Default", "GET")] - [Authenticated] - public class GetDefaultListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo> - { - } - - [Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")] - [Authenticated] - public class AddListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo> - { - public bool ValidateLogin { get; set; } - public bool ValidateListings { get; set; } - public string Pw { get; set; } - } - - [Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")] - [Authenticated] - public class DeleteListingProvider : IReturnVoid - { - [ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string Id { get; set; } - } - - [Route("/LiveTv/ListingProviders/Lineups", "GET", Summary = "Gets available lineups")] - [Authenticated] - public class GetLineups : IReturn<List<NameIdPair>> - { - [ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Id { get; set; } - - [ApiMember(Name = "Type", Description = "Provider Type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Type { get; set; } - - [ApiMember(Name = "Location", Description = "Location", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Location { get; set; } - - [ApiMember(Name = "Country", Description = "Country", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Country { get; set; } - } - - [Route("/LiveTv/ListingProviders/SchedulesDirect/Countries", "GET", Summary = "Gets available lineups")] - [Authenticated] - public class GetSchedulesDirectCountries - { - } - - [Route("/LiveTv/ChannelMappingOptions")] - [Authenticated] - public class GetChannelMappingOptions - { - [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")] - public string ProviderId { get; set; } - } - - [Route("/LiveTv/ChannelMappings")] - [Authenticated] - public class SetChannelMapping - { - [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")] - public string ProviderId { get; set; } - public string TunerChannelId { get; set; } - public string ProviderChannelId { get; set; } - } - - public class ChannelMappingOptions - { - public List<TunerChannelMapping> TunerChannels { get; set; } - public List<NameIdPair> ProviderChannels { get; set; } - public NameValuePair[] Mappings { get; set; } - public string ProviderName { get; set; } - } - - [Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")] - public class GetLiveStreamFile - { - public string Id { get; set; } - public string Container { get; set; } - } - - [Route("/LiveTv/LiveRecordings/{Id}/stream", "GET", Summary = "Gets a live tv channel")] - public class GetLiveRecordingFile - { - public string Id { get; set; } - } - - [Route("/LiveTv/TunerHosts/Types", "GET")] - [Authenticated] - public class GetTunerHostTypes : IReturn<List<NameIdPair>> - { - - } - - [Route("/LiveTv/Tuners/Discvover", "GET")] - [Authenticated] - public class DiscoverTuners : IReturn<List<TunerHostInfo>> - { - public bool NewDevicesOnly { get; set; } - } - - public class LiveTvService : BaseApiService - { - private readonly ILiveTvManager _liveTvManager; - private readonly IUserManager _userManager; - private readonly IHttpClient _httpClient; - private readonly ILibraryManager _libraryManager; - private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; - private readonly ISessionContext _sessionContext; - private readonly IStreamHelper _streamHelper; - private readonly IMediaSourceManager _mediaSourceManager; - - public LiveTvService( - ILogger<LiveTvService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IMediaSourceManager mediaSourceManager, - IStreamHelper streamHelper, - ILiveTvManager liveTvManager, - IUserManager userManager, - IHttpClient httpClient, - ILibraryManager libraryManager, - IDtoService dtoService, - IAuthorizationContext authContext, - ISessionContext sessionContext) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _mediaSourceManager = mediaSourceManager; - _streamHelper = streamHelper; - _liveTvManager = liveTvManager; - _userManager = userManager; - _httpClient = httpClient; - _libraryManager = libraryManager; - _dtoService = dtoService; - _authContext = authContext; - _sessionContext = sessionContext; - } - - public object Get(GetTunerHostTypes request) - { - var list = _liveTvManager.GetTunerHostTypes(); - 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, new DtoOptions(), user); - - var result = new QueryResult<BaseItemDto> - { - Items = returnArray, - TotalRecordCount = returnArray.Count - }; - - return ToOptimizedResult(result); - } - - public object Get(GetLiveRecordingFile request) - { - var path = _liveTvManager.GetEmbyTvActiveRecordingPath(request.Id); - - if (string.IsNullOrWhiteSpace(path)) - { - throw new FileNotFoundException(); - } - - var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) - { - [HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType(path) - }; - - return new ProgressiveFileCopier(_streamHelper, path, outputHeaders, Logger) - { - AllowEndOfFile = false - }; - } - - public async Task<object> Get(DiscoverTuners request) - { - var result = await _liveTvManager.DiscoverTuners(request.NewDevicesOnly, CancellationToken.None).ConfigureAwait(false); - return ToOptimizedResult(result); - } - - public async Task<object> Get(GetLiveStreamFile request) - { - var liveStreamInfo = await _mediaSourceManager.GetDirectStreamProviderByUniqueId(request.Id, CancellationToken.None).ConfigureAwait(false); - - var directStreamProvider = liveStreamInfo; - - var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) - { - [HeaderNames.ContentType] = Model.Net.MimeTypes.GetMimeType("file." + request.Container) - }; - - return new ProgressiveFileCopier(directStreamProvider, _streamHelper, outputHeaders, Logger) - { - AllowEndOfFile = false - }; - } - - public object Get(GetDefaultListingProvider request) - { - return ToOptimizedResult(new ListingsProviderInfo()); - } - - public async Task<object> Post(SetChannelMapping request) - { - return await _liveTvManager.SetChannelMapping(request.ProviderId, request.TunerChannelId, request.ProviderChannelId).ConfigureAwait(false); - } - - public async Task<object> Get(GetChannelMappingOptions request) - { - var config = GetConfiguration(); - - var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(request.ProviderId, i.Id, StringComparison.OrdinalIgnoreCase)); - - var listingsProviderName = _liveTvManager.ListingProviders.First(i => string.Equals(i.Type, listingsProviderInfo.Type, StringComparison.OrdinalIgnoreCase)).Name; - - var tunerChannels = await _liveTvManager.GetChannelsForListingsProvider(request.ProviderId, CancellationToken.None) - .ConfigureAwait(false); - - var providerChannels = await _liveTvManager.GetChannelsFromListingsProviderData(request.ProviderId, CancellationToken.None) - .ConfigureAwait(false); - - var mappings = listingsProviderInfo.ChannelMappings; - - var result = new ChannelMappingOptions - { - TunerChannels = tunerChannels.Select(i => _liveTvManager.GetTunerChannelMapping(i, mappings, providerChannels)).ToList(), - - ProviderChannels = providerChannels.Select(i => new NameIdPair - { - Name = i.Name, - Id = i.Id - - }).ToList(), - - Mappings = mappings, - - ProviderName = listingsProviderName - }; - - return ToOptimizedResult(result); - } - - public async Task<object> Get(GetSchedulesDirectCountries request) - { - // https://json.schedulesdirect.org/20141201/available/countries - - var response = await _httpClient.Get(new HttpRequestOptions - { - Url = "https://json.schedulesdirect.org/20141201/available/countries", - BufferContent = false - - }).ConfigureAwait(false); - - return ResultFactory.GetResult(Request, response, "application/json"); - } - - private void AssertUserCanManageLiveTv() - { - var user = _sessionContext.GetUser(Request); - - if (user == null) - { - throw new SecurityException("Anonymous live tv management is not allowed."); - } - - if (!user.HasPermission(PermissionKind.EnableLiveTvManagement)) - { - throw new SecurityException("The current user does not have permission to manage live tv."); - } - } - - 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) - { - // SchedulesDirect requires a SHA1 hash of the user's password - // https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#obtain-a-token - using SHA1 sha = SHA1.Create(); - - return Hex.Encode( - sha.ComputeHash(Encoding.UTF8.GetBytes(str))); - } - - public void Delete(DeleteListingProvider request) - { - _liveTvManager.DeleteListingsProvider(request.Id); - } - - public async Task<object> Post(AddTunerHost request) - { - var result = await _liveTvManager.SaveTunerHost(request).ConfigureAwait(false); - return ToOptimizedResult(result); - } - - public void Delete(DeleteTunerHost request) - { - var config = GetConfiguration(); - - config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(request.Id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray(); - - ServerConfigurationManager.SaveConfiguration("livetv", config); - } - - private LiveTvOptions GetConfiguration() - { - return ServerConfigurationManager.GetConfiguration<LiveTvOptions>("livetv"); - } - - private void UpdateConfiguration(LiveTvOptions options) - { - ServerConfigurationManager.SaveConfiguration("livetv", options); - } - - public async Task<object> Get(GetLineups request) - { - var info = await _liveTvManager.GetLineups(request.Type, request.Id, request.Country, request.Location).ConfigureAwait(false); - - return ToOptimizedResult(info); - } - - public object Get(GetLiveTvInfo request) - { - var info = _liveTvManager.GetLiveTvInfo(CancellationToken.None); - - return ToOptimizedResult(info); - } - - public object Get(GetChannels request) - { - var options = GetDtoOptions(_authContext, request); - - var channelResult = _liveTvManager.GetInternalChannels(new LiveTvChannelQuery - { - ChannelType = request.Type, - UserId = request.UserId, - StartIndex = request.StartIndex, - Limit = request.Limit, - IsFavorite = request.IsFavorite, - IsLiked = request.IsLiked, - IsDisliked = request.IsDisliked, - EnableFavoriteSorting = request.EnableFavoriteSorting, - IsMovie = request.IsMovie, - IsSeries = request.IsSeries, - IsNews = request.IsNews, - IsKids = request.IsKids, - IsSports = request.IsSports, - SortBy = request.GetOrderBy(), - SortOrder = request.SortOrder ?? SortOrder.Ascending, - AddCurrentProgram = request.AddCurrentProgram - - }, options, CancellationToken.None); - - var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); - - RemoveFields(options); - - options.AddCurrentProgram = request.AddCurrentProgram; - - var returnArray = _dtoService.GetBaseItemDtos(channelResult.Items, options, user); - - var result = new QueryResult<BaseItemDto> - { - Items = returnArray, - TotalRecordCount = channelResult.TotalRecordCount - }; - - return ToOptimizedResult(result); - } - - private void RemoveFields(DtoOptions options) - { - var fields = options.Fields.ToList(); - - fields.Remove(ItemFields.CanDelete); - fields.Remove(ItemFields.CanDownload); - fields.Remove(ItemFields.DisplayPreferencesId); - fields.Remove(ItemFields.Etag); - options.Fields = fields.ToArray(); - } - - public object Get(GetChannel request) - { - var user = _userManager.GetUserById(request.UserId); - - 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 ToOptimizedResult(result); - } - - public async Task<object> Get(GetPrograms request) - { - var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId); - - var query = new InternalItemsQuery(user) - { - ChannelIds = ApiEntryPoint.Split(request.ChannelIds, ',', true).Select(i => new Guid(i)).ToArray(), - HasAired = request.HasAired, - IsAiring = request.IsAiring, - EnableTotalRecordCount = request.EnableTotalRecordCount - }; - - if (!string.IsNullOrEmpty(request.MinStartDate)) - { - query.MinStartDate = DateTime.Parse(request.MinStartDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - } - - if (!string.IsNullOrEmpty(request.MinEndDate)) - { - query.MinEndDate = DateTime.Parse(request.MinEndDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - } - - if (!string.IsNullOrEmpty(request.MaxStartDate)) - { - query.MaxStartDate = DateTime.Parse(request.MaxStartDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - } - - if (!string.IsNullOrEmpty(request.MaxEndDate)) - { - query.MaxEndDate = DateTime.Parse(request.MaxEndDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - } - - query.StartIndex = request.StartIndex; - query.Limit = request.Limit; - query.OrderBy = BaseItemsRequest.GetOrderBy(request.SortBy, request.SortOrder); - query.IsNews = request.IsNews; - query.IsMovie = request.IsMovie; - query.IsSeries = request.IsSeries; - query.IsKids = request.IsKids; - query.IsSports = request.IsSports; - query.SeriesTimerId = request.SeriesTimerId; - query.Genres = (request.Genres ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - query.GenreIds = GetGuids(request.GenreIds); - - if (!request.LibrarySeriesId.Equals(Guid.Empty)) - { - query.IsSeries = true; - - if (_libraryManager.GetItemById(request.LibrarySeriesId) is Series series) - { - query.Name = series.Name; - } - } - - var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - - public object Get(GetRecommendedPrograms request) - { - var user = _userManager.GetUserById(request.UserId); - - var query = new InternalItemsQuery(user) - { - IsAiring = request.IsAiring, - Limit = request.Limit, - HasAired = request.HasAired, - IsSeries = request.IsSeries, - IsMovie = request.IsMovie, - IsKids = request.IsKids, - IsNews = request.IsNews, - IsSports = request.IsSports, - EnableTotalRecordCount = request.EnableTotalRecordCount - }; - - query.GenreIds = GetGuids(request.GenreIds); - - var result = _liveTvManager.GetRecommendedPrograms(query, GetDtoOptions(_authContext, request), CancellationToken.None); - - return ToOptimizedResult(result); - } - - public object Post(GetPrograms request) - { - return Get(request); - } - - public object Get(GetRecordings request) - { - var options = GetDtoOptions(_authContext, request); - - var result = _liveTvManager.GetRecordings(new RecordingQuery - { - ChannelId = request.ChannelId, - UserId = request.UserId, - StartIndex = request.StartIndex, - Limit = request.Limit, - Status = request.Status, - SeriesTimerId = request.SeriesTimerId, - IsInProgress = request.IsInProgress, - EnableTotalRecordCount = request.EnableTotalRecordCount, - IsMovie = request.IsMovie, - IsNews = request.IsNews, - IsSeries = request.IsSeries, - IsKids = request.IsKids, - IsSports = request.IsSports, - IsLibraryItem = request.IsLibraryItem, - Fields = request.GetItemFields(), - ImageTypeLimit = request.ImageTypeLimit, - EnableImages = request.EnableImages - - }, options); - - return ToOptimizedResult(result); - } - - public object Get(GetRecordingSeries request) - { - return ToOptimizedResult(new QueryResult<BaseItemDto>()); - } - - public object Get(GetRecording request) - { - var user = _userManager.GetUserById(request.UserId); - - 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 ToOptimizedResult(result); - } - - public async Task<object> Get(GetTimer request) - { - var result = await _liveTvManager.GetTimer(request.Id, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - - public async Task<object> Get(GetTimers request) - { - var result = await _liveTvManager.GetTimers(new TimerQuery - { - ChannelId = request.ChannelId, - SeriesTimerId = request.SeriesTimerId, - IsActive = request.IsActive, - IsScheduled = request.IsScheduled - - }, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - - public void Delete(DeleteRecording request) - { - AssertUserCanManageLiveTv(); - - _libraryManager.DeleteItem(_libraryManager.GetItemById(request.Id), new DeleteOptions - { - DeleteFileLocation = false - }); - } - - public Task Delete(CancelTimer request) - { - AssertUserCanManageLiveTv(); - - return _liveTvManager.CancelTimer(request.Id); - } - - public Task Post(UpdateTimer request) - { - AssertUserCanManageLiveTv(); - - return _liveTvManager.UpdateTimer(request, CancellationToken.None); - } - - public async Task<object> Get(GetSeriesTimers request) - { - var result = await _liveTvManager.GetSeriesTimers(new SeriesTimerQuery - { - SortOrder = request.SortOrder, - SortBy = request.SortBy - - }, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - - public async Task<object> Get(GetSeriesTimer request) - { - var result = await _liveTvManager.GetSeriesTimer(request.Id, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - - public Task Delete(CancelSeriesTimer request) - { - AssertUserCanManageLiveTv(); - - return _liveTvManager.CancelSeriesTimer(request.Id); - } - - public Task Post(UpdateSeriesTimer request) - { - AssertUserCanManageLiveTv(); - - return _liveTvManager.UpdateSeriesTimer(request, CancellationToken.None); - } - - public async Task<object> Get(GetDefaultTimer request) - { - if (string.IsNullOrEmpty(request.ProgramId)) - { - var result = await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - else - { - var result = await _liveTvManager.GetNewTimerDefaults(request.ProgramId, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - } - - public async Task<object> Get(GetProgram request) - { - 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 ToOptimizedResult(result); - } - - public Task Post(CreateSeriesTimer request) - { - AssertUserCanManageLiveTv(); - - return _liveTvManager.CreateSeriesTimer(request, CancellationToken.None); - } - - public Task Post(CreateTimer request) - { - AssertUserCanManageLiveTv(); - - return _liveTvManager.CreateTimer(request, CancellationToken.None); - } - - public object Get(GetRecordingGroups request) - { - return ToOptimizedResult(new QueryResult<BaseItemDto>()); - } - - public object Get(GetRecordingGroup request) - { - throw new FileNotFoundException(); - } - - public object Get(GetGuideInfo request) - { - return ToOptimizedResult(_liveTvManager.GetGuideInfo()); - } - - public Task Post(ResetTuner request) - { - AssertUserCanManageLiveTv(); - - return _liveTvManager.ResetTuner(request.Id, CancellationToken.None); - } - } -} diff --git a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs b/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs deleted file mode 100644 index 4c608d9a33..0000000000 --- a/MediaBrowser.Api/LiveTv/ProgressiveFileCopier.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.LiveTv -{ - public class ProgressiveFileCopier : IAsyncStreamWriter, IHasHeaders - { - private readonly ILogger _logger; - private readonly string _path; - private readonly Dictionary<string, string> _outputHeaders; - - public bool AllowEndOfFile = true; - - private readonly IDirectStreamProvider _directStreamProvider; - private IStreamHelper _streamHelper; - - public ProgressiveFileCopier(IStreamHelper streamHelper, string path, Dictionary<string, string> outputHeaders, ILogger logger) - { - _path = path; - _outputHeaders = outputHeaders; - _logger = logger; - _streamHelper = streamHelper; - } - - public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, IStreamHelper streamHelper, Dictionary<string, string> outputHeaders, ILogger logger) - { - _directStreamProvider = directStreamProvider; - _outputHeaders = outputHeaders; - _logger = logger; - _streamHelper = streamHelper; - } - - public IDictionary<string, string> Headers => _outputHeaders; - - public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken) - { - if (_directStreamProvider != null) - { - await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false); - return; - } - - var fileOptions = FileOptions.SequentialScan; - - // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 - if (Environment.OSVersion.Platform != PlatformID.Win32NT) - { - fileOptions |= FileOptions.Asynchronous; - } - - using (var inputStream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, fileOptions)) - { - var emptyReadLimit = AllowEndOfFile ? 20 : 100; - var eofCount = 0; - while (eofCount < emptyReadLimit) - { - int bytesRead; - bytesRead = await _streamHelper.CopyToAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false); - - if (bytesRead == 0) - { - eofCount++; - await Task.Delay(100, cancellationToken).ConfigureAwait(false); - } - else - { - eofCount = 0; - } - } - } - } - } -} diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index cd329c94f9..d703bdb058 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -14,10 +14,6 @@ <Compile Include="..\SharedVersion.cs" /> </ItemGroup> - <ItemGroup> - <Folder Include="Library" /> - </ItemGroup> - <PropertyGroup> <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs deleted file mode 100644 index 2d61299c76..0000000000 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ /dev/null @@ -1,418 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Jellyfin.Data.Entities; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; -using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; -using Movie = MediaBrowser.Controller.Entities.Movies.Movie; - -namespace MediaBrowser.Api.Movies -{ - [Route("/Movies/Recommendations", "GET", Summary = "Gets movie recommendations")] - public class GetMovieRecommendations : IReturn<RecommendationDto[]>, IHasDtoOptions - { - [ApiMember(Name = "CategoryLimit", Description = "The max number of categories to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int CategoryLimit { get; set; } - - [ApiMember(Name = "ItemLimit", Description = "The max number of items to return per category", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int ItemLimit { get; set; } - - /// <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 Guid UserId { get; set; } - - /// <summary> - /// Specify this to localize the search to a specific item or folder. Omit to use the root. - /// </summary> - /// <value>The parent id.</value> - [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; } - - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - public GetMovieRecommendations() - { - CategoryLimit = 5; - ItemLimit = 8; - } - - public string Fields { get; set; } - } - - /// <summary> - /// Class MoviesService - /// </summary> - [Authenticated] - public class MoviesService : BaseApiService - { - /// <summary> - /// The _user manager - /// </summary> - private readonly IUserManager _userManager; - - private readonly ILibraryManager _libraryManager; - - private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; - - /// <summary> - /// Initializes a new instance of the <see cref="MoviesService" /> class. - /// </summary> - public MoviesService( - ILogger<MoviesService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IDtoService dtoService, - IAuthorizationContext authContext) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _userManager = userManager; - _libraryManager = libraryManager; - _dtoService = dtoService; - _authContext = authContext; - } - - public object Get(GetMovieRecommendations request) - { - var user = _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var result = GetRecommendationCategories(user, request.ParentId, request.CategoryLimit, request.ItemLimit, dtoOptions); - - return ToOptimizedResult(result); - } - - public QueryResult<BaseItemDto> GetSimilarItemsResult(BaseGetSimilarItemsFromItem 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 itemTypes = new List<string> { typeof(Movie).Name }; - if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions) - { - itemTypes.Add(typeof(Trailer).Name); - itemTypes.Add(typeof(LiveTvProgram).Name); - } - - var dtoOptions = GetDtoOptions(_authContext, request); - - var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - Limit = request.Limit, - IncludeItemTypes = itemTypes.ToArray(), - IsMovie = true, - SimilarTo = item, - EnableGroupByMetadataKey = true, - DtoOptions = dtoOptions - - }); - - var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user); - - var result = new QueryResult<BaseItemDto> - { - Items = returnList, - - TotalRecordCount = itemsResult.Count - }; - - return result; - } - - private IEnumerable<RecommendationDto> GetRecommendationCategories(User user, string parentId, int categoryLimit, int itemLimit, DtoOptions dtoOptions) - { - var categories = new List<RecommendationDto>(); - - var parentIdGuid = string.IsNullOrWhiteSpace(parentId) ? Guid.Empty : new Guid(parentId); - - var query = new InternalItemsQuery(user) - { - IncludeItemTypes = new[] - { - typeof(Movie).Name, - //typeof(Trailer).Name, - //typeof(LiveTvProgram).Name - }, - // IsMovie = true - OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), - Limit = 7, - ParentId = parentIdGuid, - Recursive = true, - IsPlayed = true, - DtoOptions = dtoOptions - }; - - var recentlyPlayedMovies = _libraryManager.GetItemList(query); - - var itemTypes = new List<string> { typeof(Movie).Name }; - if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions) - { - itemTypes.Add(typeof(Trailer).Name); - itemTypes.Add(typeof(LiveTvProgram).Name); - } - - var likedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - IncludeItemTypes = itemTypes.ToArray(), - IsMovie = true, - OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(), - Limit = 10, - IsFavoriteOrLiked = true, - ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id).ToArray(), - EnableGroupByMetadataKey = true, - ParentId = parentIdGuid, - Recursive = true, - DtoOptions = dtoOptions - - }); - - var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList(); - // Get recently played directors - var recentDirectors = GetDirectors(mostRecentMovies) - .ToList(); - - // Get recently played actors - var recentActors = GetActors(mostRecentMovies) - .ToList(); - - var similarToRecentlyPlayed = GetSimilarTo(user, recentlyPlayedMovies, itemLimit, dtoOptions, RecommendationType.SimilarToRecentlyPlayed).GetEnumerator(); - var similarToLiked = GetSimilarTo(user, likedMovies, itemLimit, dtoOptions, RecommendationType.SimilarToLikedItem).GetEnumerator(); - - var hasDirectorFromRecentlyPlayed = GetWithDirector(user, recentDirectors, itemLimit, dtoOptions, RecommendationType.HasDirectorFromRecentlyPlayed).GetEnumerator(); - var hasActorFromRecentlyPlayed = GetWithActor(user, recentActors, itemLimit, dtoOptions, RecommendationType.HasActorFromRecentlyPlayed).GetEnumerator(); - - var categoryTypes = new List<IEnumerator<RecommendationDto>> - { - // Give this extra weight - similarToRecentlyPlayed, - similarToRecentlyPlayed, - - // Give this extra weight - similarToLiked, - similarToLiked, - - hasDirectorFromRecentlyPlayed, - hasActorFromRecentlyPlayed - }; - - while (categories.Count < categoryLimit) - { - var allEmpty = true; - - foreach (var category in categoryTypes) - { - if (category.MoveNext()) - { - categories.Add(category.Current); - allEmpty = false; - - if (categories.Count >= categoryLimit) - { - break; - } - } - } - - if (allEmpty) - { - break; - } - } - - return categories.OrderBy(i => i.RecommendationType); - } - - private IEnumerable<RecommendationDto> GetWithDirector( - User user, - IEnumerable<string> names, - int itemLimit, - DtoOptions dtoOptions, - RecommendationType type) - { - var itemTypes = new List<string> { typeof(Movie).Name }; - if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions) - { - itemTypes.Add(typeof(Trailer).Name); - itemTypes.Add(typeof(LiveTvProgram).Name); - } - - foreach (var name in names) - { - var items = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - Person = name, - // Account for duplicates by imdb id, since the database doesn't support this yet - Limit = itemLimit + 2, - PersonTypes = new[] { PersonType.Director }, - IncludeItemTypes = itemTypes.ToArray(), - IsMovie = true, - EnableGroupByMetadataKey = true, - DtoOptions = dtoOptions - - }).GroupBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) - .Select(x => x.First()) - .Take(itemLimit) - .ToList(); - - if (items.Count > 0) - { - var returnItems = _dtoService.GetBaseItemDtos(items, dtoOptions, user); - - yield return new RecommendationDto - { - BaselineItemName = name, - CategoryId = name.GetMD5(), - RecommendationType = type, - Items = returnItems - }; - } - } - } - - private IEnumerable<RecommendationDto> GetWithActor(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type) - { - var itemTypes = new List<string> { typeof(Movie).Name }; - if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions) - { - itemTypes.Add(typeof(Trailer).Name); - itemTypes.Add(typeof(LiveTvProgram).Name); - } - - foreach (var name in names) - { - var items = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - Person = name, - // Account for duplicates by imdb id, since the database doesn't support this yet - Limit = itemLimit + 2, - IncludeItemTypes = itemTypes.ToArray(), - IsMovie = true, - EnableGroupByMetadataKey = true, - DtoOptions = dtoOptions - - }).GroupBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) - .Select(x => x.First()) - .Take(itemLimit) - .ToList(); - - if (items.Count > 0) - { - var returnItems = _dtoService.GetBaseItemDtos(items, dtoOptions, user); - - yield return new RecommendationDto - { - BaselineItemName = name, - CategoryId = name.GetMD5(), - RecommendationType = type, - Items = returnItems - }; - } - } - } - - private IEnumerable<RecommendationDto> GetSimilarTo(User user, List<BaseItem> baselineItems, int itemLimit, DtoOptions dtoOptions, RecommendationType type) - { - var itemTypes = new List<string> { typeof(Movie).Name }; - if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions) - { - itemTypes.Add(typeof(Trailer).Name); - itemTypes.Add(typeof(LiveTvProgram).Name); - } - - foreach (var item in baselineItems) - { - var similar = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - Limit = itemLimit, - IncludeItemTypes = itemTypes.ToArray(), - IsMovie = true, - SimilarTo = item, - EnableGroupByMetadataKey = true, - DtoOptions = dtoOptions - - }); - - if (similar.Count > 0) - { - var returnItems = _dtoService.GetBaseItemDtos(similar, dtoOptions, user); - - yield return new RecommendationDto - { - BaselineItemName = item.Name, - CategoryId = item.Id, - RecommendationType = type, - Items = returnItems - }; - } - } - } - - private IEnumerable<string> GetActors(List<BaseItem> items) - { - var people = _libraryManager.GetPeople(new InternalPeopleQuery - { - ExcludePersonTypes = new[] - { - PersonType.Director - }, - MaxListOrder = 3 - }); - - var itemIds = items.Select(i => i.Id).ToList(); - - return people - .Where(i => itemIds.Contains(i.ItemId)) - .Select(i => i.Name) - .DistinctNames(); - } - - private IEnumerable<string> GetDirectors(List<BaseItem> items) - { - var people = _libraryManager.GetPeople(new InternalPeopleQuery - { - PersonTypes = new[] - { - PersonType.Director - } - }); - - var itemIds = items.Select(i => i.Id).ToList(); - - return people - .Where(i => itemIds.Contains(i.ItemId)) - .Select(i => i.Name) - .DistinctNames(); - } - } -} diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs deleted file mode 100644 index 0b53342359..0000000000 --- a/MediaBrowser.Api/Movies/TrailersService.cs +++ /dev/null @@ -1,89 +0,0 @@ -using MediaBrowser.Api.UserLibrary; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.Movies -{ - [Route("/Trailers", "GET", Summary = "Finds movies and trailers similar to a given trailer.")] - public class Getrailers : BaseItemsRequest, IReturn<QueryResult<BaseItemDto>> - { - } - - /// <summary> - /// Class TrailersService - /// </summary> - [Authenticated] - public class TrailersService : BaseApiService - { - /// <summary> - /// The _user manager - /// </summary> - private readonly IUserManager _userManager; - - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - - /// <summary> - /// The logger for the created <see cref="ItemsService"/> instances. - /// </summary> - private readonly ILogger<ItemsService> _logger; - - private readonly IDtoService _dtoService; - private readonly ILocalizationManager _localizationManager; - private readonly IJsonSerializer _json; - private readonly IAuthorizationContext _authContext; - - public TrailersService( - ILoggerFactory loggerFactory, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IDtoService dtoService, - ILocalizationManager localizationManager, - IJsonSerializer json, - IAuthorizationContext authContext) - : base(loggerFactory.CreateLogger<TrailersService>(), serverConfigurationManager, httpResultFactory) - { - _userManager = userManager; - _libraryManager = libraryManager; - _dtoService = dtoService; - _localizationManager = localizationManager; - _json = json; - _authContext = authContext; - _logger = loggerFactory.CreateLogger<ItemsService>(); - } - - public object Get(Getrailers request) - { - var json = _json.SerializeToString(request); - var getItems = _json.DeserializeFromString<GetItems>(json); - - getItems.IncludeItemTypes = "Trailer"; - - return new ItemsService( - _logger, - ServerConfigurationManager, - ResultFactory, - _userManager, - _libraryManager, - _localizationManager, - _dtoService, - _authContext) - { - Request = Request, - - }.Get(getItems); - } - } -} diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs deleted file mode 100644 index 7d10c94271..0000000000 --- a/MediaBrowser.Api/Music/InstantMixService.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Jellyfin.Data.Entities; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Playlists; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.Music -{ - [Route("/Songs/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given song")] - public class GetInstantMixFromSong : BaseGetSimilarItemsFromItem - { - } - - [Route("/Albums/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given album")] - public class GetInstantMixFromAlbum : BaseGetSimilarItemsFromItem - { - } - - [Route("/Playlists/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given playlist")] - public class GetInstantMixFromPlaylist : BaseGetSimilarItemsFromItem - { - } - - [Route("/MusicGenres/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a music genre")] - public class GetInstantMixFromMusicGenre : BaseGetSimilarItems - { - [ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - } - - [Route("/Artists/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")] - public class GetInstantMixFromArtistId : BaseGetSimilarItems - { - [ApiMember(Name = "Id", Description = "The artist Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/MusicGenres/InstantMix", "GET", Summary = "Creates an instant playlist based on a music genre")] - public class GetInstantMixFromMusicGenreId : BaseGetSimilarItems - { - [ApiMember(Name = "Id", Description = "The genre Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/Items/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given item")] - public class GetInstantMixFromItem : BaseGetSimilarItemsFromItem - { - } - - [Authenticated] - public class InstantMixService : BaseApiService - { - private readonly IUserManager _userManager; - - private readonly IDtoService _dtoService; - private readonly ILibraryManager _libraryManager; - private readonly IMusicManager _musicManager; - private readonly IAuthorizationContext _authContext; - - public InstantMixService( - ILogger<InstantMixService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - IDtoService dtoService, - IMusicManager musicManager, - ILibraryManager libraryManager, - IAuthorizationContext authContext) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _userManager = userManager; - _dtoService = dtoService; - _musicManager = musicManager; - _libraryManager = libraryManager; - _authContext = authContext; - } - - public object Get(GetInstantMixFromItem request) - { - var item = _libraryManager.GetItemById(request.Id); - - var user = _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); - - return GetResult(items, user, request, dtoOptions); - } - - public object Get(GetInstantMixFromArtistId request) - { - var item = _libraryManager.GetItemById(request.Id); - - var user = _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); - - return GetResult(items, user, request, dtoOptions); - } - - public object Get(GetInstantMixFromMusicGenreId request) - { - var item = _libraryManager.GetItemById(request.Id); - - var user = _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); - - return GetResult(items, user, request, dtoOptions); - } - - public object Get(GetInstantMixFromSong request) - { - var item = _libraryManager.GetItemById(request.Id); - - var user = _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); - - return GetResult(items, user, request, dtoOptions); - } - - public object Get(GetInstantMixFromAlbum request) - { - var album = _libraryManager.GetItemById(request.Id); - - var user = _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var items = _musicManager.GetInstantMixFromItem(album, user, dtoOptions); - - return GetResult(items, user, request, dtoOptions); - } - - public object Get(GetInstantMixFromPlaylist request) - { - var playlist = (Playlist)_libraryManager.GetItemById(request.Id); - - var user = _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var items = _musicManager.GetInstantMixFromItem(playlist, user, dtoOptions); - - return GetResult(items, user, request, dtoOptions); - } - - public object Get(GetInstantMixFromMusicGenre request) - { - var user = _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var items = _musicManager.GetInstantMixFromGenres(new[] { request.Name }, user, dtoOptions); - - return GetResult(items, user, request, dtoOptions); - } - - private object GetResult(List<BaseItem> items, User user, BaseGetSimilarItems request, DtoOptions dtoOptions) - { - var list = items; - - var result = new QueryResult<BaseItemDto> - { - TotalRecordCount = list.Count - }; - - if (request.Limit.HasValue) - { - list = list.Take(request.Limit.Value).ToList(); - } - - var returnList = _dtoService.GetBaseItemDtos(list, dtoOptions, user); - - result.Items = returnList; - - return result; - } - - } -} diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 957e4ae910..84ed5dcac4 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -28,7 +28,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback { /// <summary> - /// Class BaseStreamingService + /// Class BaseStreamingService. /// </summary> public abstract class BaseStreamingService : BaseApiService { @@ -216,7 +216,7 @@ namespace MediaBrowser.Api.Playback UseShellExecute = false, // Must consume both stdout and stderr or deadlocks may occur - //RedirectStandardOutput = true, + // RedirectStandardOutput = true, RedirectStandardError = true, RedirectStandardInput = true, @@ -303,6 +303,7 @@ namespace MediaBrowser.Api.Playback { StartThrottler(state, transcodingJob); } + Logger.LogDebug("StartFfMpeg() finished successfully"); return transcodingJob; @@ -608,6 +609,7 @@ namespace MediaBrowser.Api.Playback { throw new ArgumentException("Invalid timeseek header"); } + int index = value.IndexOf('-'); value = index == -1 ? value.Substring(Npt.Length) @@ -639,8 +641,10 @@ namespace MediaBrowser.Api.Playback { throw new ArgumentException("Invalid timeseek header"); } + timeFactor /= 60; } + return TimeSpan.FromSeconds(secondsSum).Ticks; } @@ -685,7 +689,7 @@ namespace MediaBrowser.Api.Playback state.User = UserManager.GetUserById(auth.UserId); } - //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || + // if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 || // (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1) //{ @@ -716,9 +720,9 @@ namespace MediaBrowser.Api.Playback state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - //var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ?? + // var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ?? // item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null); - //if (primaryImage != null) + // if (primaryImage != null) //{ // state.AlbumCoverPath = primaryImage.Path; //} @@ -885,7 +889,7 @@ namespace MediaBrowser.Api.Playback if (transcodingProfile != null) { state.EstimateContentLength = transcodingProfile.EstimateContentLength; - //state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; + // state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; if (state.VideoRequest != null) diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 627421aac6..418cd92b3d 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback.Hls { /// <summary> - /// Class BaseHlsService + /// Class BaseHlsService. /// </summary> public abstract class BaseHlsService : BaseStreamingService { @@ -146,6 +146,7 @@ namespace MediaBrowser.Api.Playback.Hls { ApiEntryPoint.Instance.OnTranscodeEndRequest(job); } + return ResultFactory.GetResult(GetLivePlaylistText(playlist, state.SegmentLength), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>()); } @@ -178,7 +179,7 @@ namespace MediaBrowser.Api.Playback.Hls var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(CultureInfo.InvariantCulture); text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength - 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase); - //text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase); + // text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase); return text; } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 0a6ed27866..fe5f980b18 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -234,6 +234,7 @@ namespace MediaBrowser.Api.Playback.Hls Logger.LogDebug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", requestedIndex - currentTranscodingIndex.Value, segmentGapRequiringTranscodingChange, requestedIndex); startTranscoding = true; } + if (startTranscoding) { // If the playlist doesn't already exist, startup ffmpeg @@ -257,7 +258,7 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - //await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false); + // await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false); } else { @@ -277,8 +278,8 @@ namespace MediaBrowser.Api.Playback.Hls } } - //Logger.LogInformation("waiting for {0}", segmentPath); - //while (!File.Exists(segmentPath)) + // Logger.LogInformation("waiting for {0}", segmentPath); + // while (!File.Exists(segmentPath)) //{ // await Task.Delay(50, cancellationToken).ConfigureAwait(false); //} @@ -518,6 +519,7 @@ namespace MediaBrowser.Api.Playback.Hls { Logger.LogDebug("serving {0} as it's on disk and transcoding stopped", segmentPath); } + cancellationToken.ThrowIfCancellationRequested(); } else @@ -717,7 +719,7 @@ namespace MediaBrowser.Api.Playback.Hls // Having problems in android return false; - //return state.VideoRequest.VideoBitRate.HasValue; + // return state.VideoRequest.VideoBitRate.HasValue; } /// <summary> @@ -972,7 +974,7 @@ namespace MediaBrowser.Api.Playback.Hls var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); - //if ((Request.UserAgent ?? string.Empty).IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1) + // if ((Request.UserAgent ?? string.Empty).IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1) //{ // queryString = string.Empty; //} @@ -1100,7 +1102,7 @@ namespace MediaBrowser.Api.Playback.Hls } } - //args += " -flags -global_header"; + // args += " -flags -global_header"; } else { @@ -1142,7 +1144,7 @@ namespace MediaBrowser.Api.Playback.Hls args += " " + keyFrameArg + gopArg; } - //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; + // args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; @@ -1164,7 +1166,7 @@ namespace MediaBrowser.Api.Playback.Hls args += " -start_at_zero"; } - //args += " -flags -global_header"; + // args += " -flags -global_header"; } if (!string.IsNullOrEmpty(state.OutputVideoSync)) diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 87ccde2e04..8a3d00283f 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback.Hls { /// <summary> - /// Class GetHlsAudioSegment + /// Class GetHlsAudioSegment. /// </summary> // Can't require authentication just yet due to seeing some requests come from Chrome without full query string //[Authenticated] @@ -37,7 +37,7 @@ namespace MediaBrowser.Api.Playback.Hls } /// <summary> - /// Class GetHlsVideoSegment + /// Class GetHlsVideoSegment. /// </summary> [Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")] [Authenticated] @@ -66,7 +66,7 @@ namespace MediaBrowser.Api.Playback.Hls } /// <summary> - /// Class GetHlsVideoSegment + /// Class GetHlsVideoSegment. /// </summary> // Can't require authentication just yet due to seeing some requests come from Chrome without full query string //[Authenticated] diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index aefb3f019b..9562f9953a 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Api.Playback.Hls } /// <summary> - /// Class VideoHlsService + /// Class VideoHlsService. /// </summary> [Authenticated] public class VideoHlsService : BaseHlsService diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 2c6534cc08..427d255081 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -28,7 +28,6 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback { - [Route("/Items/{Id}/PlaybackInfo", "GET", Summary = "Gets live playback media info for an item")] public class GetPlaybackInfo : IReturn<PlaybackInfoResponse> { [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] @@ -38,24 +37,20 @@ namespace MediaBrowser.Api.Playback public Guid UserId { get; set; } } - [Route("/Items/{Id}/PlaybackInfo", "POST", Summary = "Gets live playback media info for an item")] public class GetPostedPlaybackInfo : PlaybackInfoRequest, IReturn<PlaybackInfoResponse> { } - [Route("/LiveStreams/Open", "POST", Summary = "Opens a media source")] public class OpenMediaSource : LiveStreamRequest, IReturn<LiveStreamResponse> { } - [Route("/LiveStreams/Close", "POST", Summary = "Closes a media source")] public class CloseMediaSource : IReturnVoid { [ApiMember(Name = "LiveStreamId", Description = "LiveStreamId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] public string LiveStreamId { get; set; } } - [Route("/Playback/BitrateTest", "GET")] public class GetBitrateTestBytes { [ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")] @@ -551,10 +546,12 @@ namespace MediaBrowser.Api.Playback { mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; } + if (!allowAudioStreamCopy) { mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; } + mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; } @@ -646,7 +643,6 @@ namespace MediaBrowser.Api.Playback } return 1; - }).ThenBy(i => { // Let's assume direct streaming a file is just as desirable as direct playing a remote url @@ -656,7 +652,6 @@ namespace MediaBrowser.Api.Playback } return 1; - }).ThenBy(i => { return i.Protocol switch @@ -672,7 +667,6 @@ namespace MediaBrowser.Api.Playback } return 1; - }).ThenBy(originalList.IndexOf) .ToArray(); } diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs index 34c7986ca5..d51787df27 100644 --- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs +++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs @@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback.Progressive { /// <summary> - /// Class GetAudioStream + /// Class GetAudioStream. /// </summary> [Route("/Audio/{Id}/stream.{Container}", "GET", Summary = "Gets an audio stream")] [Route("/Audio/{Id}/stream", "GET", Summary = "Gets an audio stream")] @@ -26,7 +26,7 @@ namespace MediaBrowser.Api.Playback.Progressive } /// <summary> - /// Class AudioService + /// Class AudioService. /// </summary> // TODO: In order to autheneticate this in the future, Dlna playback will require updating //[Authenticated] diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index c7bf055fba..2ebf0e420d 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -21,7 +21,7 @@ using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Playback.Progressive { /// <summary> - /// Class BaseProgressiveStreamingService + /// Class BaseProgressiveStreamingService. /// </summary> public abstract class BaseProgressiveStreamingService : BaseStreamingService { @@ -88,14 +88,17 @@ namespace MediaBrowser.Api.Playback.Progressive { return ".ts"; } + if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase)) { return ".ogv"; } + if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase)) { return ".webm"; } + if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase)) { return ".asf"; @@ -111,14 +114,17 @@ namespace MediaBrowser.Api.Playback.Progressive { return ".aac"; } + if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase)) { return ".mp3"; } + if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase)) { return ".ogg"; } + if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase)) { return ".wma"; @@ -231,7 +237,7 @@ namespace MediaBrowser.Api.Playback.Progressive } //// Not static but transcode cache file exists - //if (isTranscodeCached && state.VideoRequest == null) + // if (isTranscodeCached && state.VideoRequest == null) //{ // var contentType = state.GetMimeType(outputPath); diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index a53b848f9e..b70fff128b 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -23,6 +23,7 @@ namespace MediaBrowser.Api.Playback.Progressive private long _bytesWritten = 0; public long StartPosition { get; set; } + public bool AllowEndOfFile = true; private readonly IDirectStreamProvider _directStreamProvider; @@ -96,8 +97,8 @@ namespace MediaBrowser.Api.Playback.Progressive bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false); } - //var position = fs.Position; - //_logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); + // var position = fs.Position; + // _logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path); if (bytesRead == 0) { @@ -105,6 +106,7 @@ namespace MediaBrowser.Api.Playback.Progressive { eofCount++; } + await Task.Delay(100, cancellationToken).ConfigureAwait(false); } else diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index 4de81655ce..c3f6b905cb 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Playback.Progressive { /// <summary> - /// Class GetVideoStream + /// Class GetVideoStream. /// </summary> [Route("/Videos/{Id}/stream.mpegts", "GET")] [Route("/Videos/{Id}/stream.ts", "GET")] @@ -59,11 +59,10 @@ namespace MediaBrowser.Api.Playback.Progressive [Route("/Videos/{Id}/stream", "HEAD")] public class GetVideoStream : VideoStreamRequest { - } /// <summary> - /// Class VideoService + /// Class VideoService. /// </summary> // TODO: In order to autheneticate this in the future, Dlna playback will require updating //[Authenticated] diff --git a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs index 3b8b299957..7e2e337ad1 100644 --- a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs +++ b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs @@ -8,17 +8,17 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Api.Playback { /// <summary> - /// Class StaticRemoteStreamWriter + /// Class StaticRemoteStreamWriter. /// </summary> public class StaticRemoteStreamWriter : IAsyncStreamWriter, IHasHeaders { /// <summary> - /// The _input stream + /// The _input stream. /// </summary> private readonly HttpResponseInfo _response; /// <summary> - /// The _options + /// The _options. /// </summary> private readonly IDictionary<string, string> _options = new Dictionary<string, string>(); diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 9ba8eda91f..67c334e489 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -4,7 +4,7 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Api.Playback { /// <summary> - /// Class StreamRequest + /// Class StreamRequest. /// </summary> public class StreamRequest : BaseEncodingJobOptions { @@ -12,11 +12,15 @@ namespace MediaBrowser.Api.Playback public string DeviceProfileId { get; set; } public string Params { get; set; } + public string PlaySessionId { get; set; } + public string Tag { get; set; } + public string SegmentContainer { get; set; } public int? SegmentLength { get; set; } + public int? MinSegments { get; set; } } diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index a3b319d44b..d5d78cf37a 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -37,10 +37,13 @@ namespace MediaBrowser.Api.Playback public string DeviceId { get; set; } public Guid UserId { get; set; } + public string AudioCodec { get; set; } + public string Container { get; set; } public int? MaxAudioChannels { get; set; } + public int? TranscodingAudioChannels { get; set; } public long? MaxStreamingBitrate { get; set; } @@ -49,12 +52,17 @@ namespace MediaBrowser.Api.Playback public long? StartTimeTicks { get; set; } public string TranscodingContainer { get; set; } + public string TranscodingProtocol { get; set; } + public int? MaxAudioSampleRate { get; set; } + public int? MaxAudioBitDepth { get; set; } public bool EnableRedirection { get; set; } + public bool EnableRemoteMedia { get; set; } + public bool BreakOnNonKeyFrames { get; set; } public BaseUniversalRequest() @@ -114,16 +122,27 @@ namespace MediaBrowser.Api.Playback } protected IHttpClient HttpClient { get; private set; } + protected IUserManager UserManager { get; private set; } + protected ILibraryManager LibraryManager { get; private set; } + protected IIsoManager IsoManager { get; private set; } + protected IMediaEncoder MediaEncoder { get; private set; } + protected IFileSystem FileSystem { get; private set; } + protected IDlnaManager DlnaManager { get; private set; } + protected IDeviceManager DeviceManager { get; private set; } + protected IMediaSourceManager MediaSourceManager { get; private set; } + protected IJsonSerializer JsonSerializer { get; private set; } + protected IAuthorizationContext AuthorizationContext { get; private set; } + protected INetworkManager NetworkManager { get; private set; } public Task<object> Get(GetUniversalAudioStream request) @@ -259,7 +278,6 @@ namespace MediaBrowser.Api.Playback UserId = request.UserId, DeviceProfile = deviceProfile, MediaSourceId = request.MediaSourceId - }).ConfigureAwait(false); var mediaSource = playbackInfoResult.MediaSources[0]; @@ -329,6 +347,7 @@ namespace MediaBrowser.Api.Playback { return await service.Head(newRequest).ConfigureAwait(false); } + return await service.Get(newRequest).ConfigureAwait(false); } else diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs deleted file mode 100644 index e08a8482e0..0000000000 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs +++ /dev/null @@ -1,234 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Services; -using MediaBrowser.Model.Tasks; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.ScheduledTasks -{ - /// <summary> - /// Class GetScheduledTask - /// </summary> - [Route("/ScheduledTasks/{Id}", "GET", Summary = "Gets a scheduled task, by Id")] - public class GetScheduledTask : IReturn<TaskInfo> - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - /// <summary> - /// Class GetScheduledTasks - /// </summary> - [Route("/ScheduledTasks", "GET", Summary = "Gets scheduled tasks")] - public class GetScheduledTasks : IReturn<TaskInfo[]> - { - [ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsHidden { get; set; } - - [ApiMember(Name = "IsEnabled", Description = "Optional filter tasks that are enabled, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsEnabled { get; set; } - } - - /// <summary> - /// Class StartScheduledTask - /// </summary> - [Route("/ScheduledTasks/Running/{Id}", "POST", Summary = "Starts a scheduled task")] - public class StartScheduledTask : IReturnVoid - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } - } - - /// <summary> - /// Class StopScheduledTask - /// </summary> - [Route("/ScheduledTasks/Running/{Id}", "DELETE", Summary = "Stops a scheduled task")] - public class StopScheduledTask : IReturnVoid - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - public string Id { get; set; } - } - - /// <summary> - /// Class UpdateScheduledTaskTriggers - /// </summary> - [Route("/ScheduledTasks/{Id}/Triggers", "POST", Summary = "Updates the triggers for a scheduled task")] - public class UpdateScheduledTaskTriggers : List<TaskTriggerInfo>, IReturnVoid - { - /// <summary> - /// Gets or sets the task id. - /// </summary> - /// <value>The task id.</value> - [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string Id { get; set; } - } - - /// <summary> - /// Class ScheduledTasksService - /// </summary> - [Authenticated(Roles = "Admin")] - public class ScheduledTaskService : BaseApiService - { - /// <summary> - /// The task manager. - /// </summary> - private readonly ITaskManager _taskManager; - - /// <summary> - /// Initializes a new instance of the <see cref="ScheduledTaskService" /> class. - /// </summary> - /// <param name="taskManager">The task manager.</param> - /// <exception cref="ArgumentNullException">taskManager</exception> - public ScheduledTaskService( - ILogger<ScheduledTaskService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - ITaskManager taskManager) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _taskManager = taskManager; - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>IEnumerable{TaskInfo}.</returns> - public object Get(GetScheduledTasks request) - { - IEnumerable<IScheduledTaskWorker> result = _taskManager.ScheduledTasks - .OrderBy(i => i.Name); - - if (request.IsHidden.HasValue) - { - var val = request.IsHidden.Value; - - result = result.Where(i => - { - var isHidden = false; - - if (i.ScheduledTask is IConfigurableScheduledTask configurableTask) - { - isHidden = configurableTask.IsHidden; - } - - return isHidden == val; - }); - } - - if (request.IsEnabled.HasValue) - { - var val = request.IsEnabled.Value; - - result = result.Where(i => - { - var isEnabled = true; - - if (i.ScheduledTask is IConfigurableScheduledTask configurableTask) - { - isEnabled = configurableTask.IsEnabled; - } - - return isEnabled == val; - }); - } - - var infos = result - .Select(ScheduledTaskHelpers.GetTaskInfo) - .ToArray(); - - return ToOptimizedResult(infos); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>IEnumerable{TaskInfo}.</returns> - /// <exception cref="ResourceNotFoundException">Task not found</exception> - public object Get(GetScheduledTask request) - { - var task = _taskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, request.Id)); - - if (task == null) - { - throw new ResourceNotFoundException("Task not found"); - } - - var result = ScheduledTaskHelpers.GetTaskInfo(task); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <exception cref="ResourceNotFoundException">Task not found</exception> - public void Post(StartScheduledTask request) - { - var task = _taskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, request.Id)); - - if (task == null) - { - throw new ResourceNotFoundException("Task not found"); - } - - _taskManager.Execute(task, new TaskOptions()); - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <exception cref="ResourceNotFoundException">Task not found</exception> - public void Delete(StopScheduledTask request) - { - var task = _taskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, request.Id)); - - if (task == null) - { - throw new ResourceNotFoundException("Task not found"); - } - - _taskManager.Cancel(task); - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <exception cref="ResourceNotFoundException">Task not found</exception> - public void Post(UpdateScheduledTaskTriggers 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 = GetPathValue(1).ToString(); - - var task = _taskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.Ordinal)); - - if (task == null) - { - throw new ResourceNotFoundException("Task not found"); - } - - task.Triggers = request.ToArray(); - } - } -} diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index 14b9b3618b..25dd39f2de 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.ScheduledTasks { /// <summary> - /// Class ScheduledTasksWebSocketListener + /// Class ScheduledTasksWebSocketListener. /// </summary> public class ScheduledTasksWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<TaskInfo>, WebSocketListenerState> { diff --git a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs index 175984575d..2400d6defe 100644 --- a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Sessions { /// <summary> - /// Class SessionInfoWebSocketListener + /// Class SessionInfoWebSocketListener. /// </summary> public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState> { @@ -19,7 +19,7 @@ namespace MediaBrowser.Api.Sessions protected override string Name => "Sessions"; /// <summary> - /// The _kernel + /// The _kernel. /// </summary> private readonly ISessionManager _sessionManager; diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs deleted file mode 100644 index dcd22280a6..0000000000 --- a/MediaBrowser.Api/SimilarItemsHelper.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api -{ - /// <summary> - /// Class BaseGetSimilarItemsFromItem - /// </summary> - public class BaseGetSimilarItemsFromItem : BaseGetSimilarItems - { - /// <summary> - /// 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; } - - public string ExcludeArtistIds { get; set; } - } - - public class BaseGetSimilarItems : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions - { - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - /// <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 Guid UserId { get; set; } - - /// <summary> - /// The maximum number of items to return - /// </summary> - /// <value>The limit.</value> - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - - /// <summary> - /// Fields to return within the items, in addition to basic information - /// </summary> - /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - } - - /// <summary> - /// Class SimilarItemsHelper - /// </summary> - public static class SimilarItemsHelper - { - internal static QueryResult<BaseItemDto> GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore) - { - var user = !request.UserId.Equals(Guid.Empty) ? userManager.GetUserById(request.UserId) : null; - - var item = string.IsNullOrEmpty(request.Id) ? - (!request.UserId.Equals(Guid.Empty) ? libraryManager.GetUserRootFolder() : - libraryManager.RootFolder) : libraryManager.GetItemById(request.Id); - - var query = new InternalItemsQuery(user) - { - IncludeItemTypes = includeTypes.Select(i => i.Name).ToArray(), - Recursive = true, - DtoOptions = dtoOptions - }; - - // ExcludeArtistIds - if (!string.IsNullOrEmpty(request.ExcludeArtistIds)) - { - query.ExcludeArtistIds = BaseApiService.GetGuids(request.ExcludeArtistIds); - } - - var inputItems = libraryManager.GetItemList(query); - - var items = GetSimilaritems(item, libraryManager, inputItems, getSimilarityScore) - .ToList(); - - var returnItems = items; - - if (request.Limit.HasValue) - { - returnItems = returnItems.Take(request.Limit.Value).ToList(); - } - - var dtos = dtoService.GetBaseItemDtos(returnItems, dtoOptions, user); - - return new QueryResult<BaseItemDto> - { - Items = dtos, - - TotalRecordCount = items.Count - }; - } - - /// <summary> - /// Gets the similaritems. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="inputItems">The input items.</param> - /// <param name="getSimilarityScore">The get similarity score.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - internal static IEnumerable<BaseItem> GetSimilaritems(BaseItem item, ILibraryManager libraryManager, IEnumerable<BaseItem> inputItems, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore) - { - var itemId = item.Id; - inputItems = inputItems.Where(i => i.Id != itemId); - var itemPeople = libraryManager.GetPeople(item); - var allPeople = libraryManager.GetPeople(new InternalPeopleQuery - { - AppearsInItemId = item.Id - }); - - return inputItems.Select(i => new Tuple<BaseItem, int>(i, getSimilarityScore(item, itemPeople, allPeople, i))) - .Where(i => i.Item2 > 2) - .OrderByDescending(i => i.Item2) - .Select(i => i.Item1); - } - - private static IEnumerable<string> GetTags(BaseItem item) - { - return item.Tags; - } - - /// <summary> - /// Gets the similiarity score. - /// </summary> - /// <param name="item1">The item1.</param> - /// <param name="item1People">The item1 people.</param> - /// <param name="allPeople">All people.</param> - /// <param name="item2">The item2.</param> - /// <returns>System.Int32.</returns> - internal static int GetSimiliarityScore(BaseItem item1, List<PersonInfo> item1People, List<PersonInfo> allPeople, BaseItem item2) - { - var points = 0; - - if (!string.IsNullOrEmpty(item1.OfficialRating) && string.Equals(item1.OfficialRating, item2.OfficialRating, StringComparison.OrdinalIgnoreCase)) - { - points += 10; - } - - // Find common genres - points += item1.Genres.Where(i => item2.Genres.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10); - - // Find common tags - points += GetTags(item1).Where(i => GetTags(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10); - - // Find common studios - points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 3); - - var item2PeopleNames = allPeople.Where(i => i.ItemId == item2.Id) - .Select(i => i.Name) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .DistinctNames() - .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - - points += item1People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i => - { - if (string.Equals(i.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) - { - return 5; - } - if (string.Equals(i.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - if (string.Equals(i.Type, PersonType.Composer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Composer, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - if (string.Equals(i.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - if (string.Equals(i.Type, PersonType.Writer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - - return 1; - }); - - if (item1.ProductionYear.HasValue && item2.ProductionYear.HasValue) - { - var diff = Math.Abs(item1.ProductionYear.Value - item2.ProductionYear.Value); - - // Add if they came out within the same decade - if (diff < 10) - { - points += 2; - } - - // And more if within five years - if (diff < 5) - { - points += 2; - } - } - - return points; - } - - } -} diff --git a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs index 1e14ea552c..daa1b521f4 100644 --- a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs +++ b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs @@ -11,93 +11,66 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.SyncPlay { - [Route("/SyncPlay/{SessionId}/NewGroup", "POST", Summary = "Create a new SyncPlay group")] + [Route("/SyncPlay/New", "POST", Summary = "Create a new SyncPlay group")] [Authenticated] - public class SyncPlayNewGroup : IReturnVoid + public class SyncPlayNew : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")] + [Route("/SyncPlay/Join", "POST", Summary = "Join an existing SyncPlay group")] [Authenticated] - public class SyncPlayJoinGroup : IReturnVoid + public class SyncPlayJoin : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the Group id. /// </summary> /// <value>The Group id to join.</value> [ApiMember(Name = "GroupId", Description = "Group Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string GroupId { get; set; } - - /// <summary> - /// Gets or sets the playing item id. - /// </summary> - /// <value>The client's currently playing item id.</value> - [ApiMember(Name = "PlayingItemId", Description = "Client's playing item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string PlayingItemId { get; set; } } - [Route("/SyncPlay/{SessionId}/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")] + [Route("/SyncPlay/Leave", "POST", Summary = "Leave joined SyncPlay group")] [Authenticated] - public class SyncPlayLeaveGroup : IReturnVoid + public class SyncPlayLeave : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/ListGroups", "POST", Summary = "List SyncPlay groups")] + [Route("/SyncPlay/List", "GET", Summary = "List SyncPlay groups")] [Authenticated] - public class SyncPlayListGroups : IReturnVoid + public class SyncPlayList : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the filter item id. /// </summary> /// <value>The filter item id.</value> - [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string FilterItemId { get; set; } } - [Route("/SyncPlay/{SessionId}/PlayRequest", "POST", Summary = "Request play in SyncPlay group")] + [Route("/SyncPlay/Play", "POST", Summary = "Request play in SyncPlay group")] [Authenticated] - public class SyncPlayPlayRequest : IReturnVoid + public class SyncPlayPlay : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")] + [Route("/SyncPlay/Pause", "POST", Summary = "Request pause in SyncPlay group")] [Authenticated] - public class SyncPlayPauseRequest : IReturnVoid + public class SyncPlayPause : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")] + [Route("/SyncPlay/Seek", "POST", Summary = "Request seek in SyncPlay group")] [Authenticated] - public class SyncPlaySeekRequest : IReturnVoid + public class SyncPlaySeek : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")] public long PositionTicks { get; set; } } - [Route("/SyncPlay/{SessionId}/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")] + [Route("/SyncPlay/Buffering", "POST", Summary = "Request group wait in SyncPlay group while buffering")] [Authenticated] - public class SyncPlayBufferingRequest : IReturnVoid + public class SyncPlayBuffering : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the date used to pin PositionTicks in time. /// </summary> @@ -109,20 +82,17 @@ namespace MediaBrowser.Api.SyncPlay public long PositionTicks { get; set; } /// <summary> - /// Gets or sets whether this is a buffering or a buffering-done request. + /// Gets or sets whether this is a buffering or a ready request. /// </summary> /// <value><c>true</c> if buffering is complete; <c>false</c> otherwise.</value> [ApiMember(Name = "BufferingDone", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool BufferingDone { get; set; } } - [Route("/SyncPlay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")] + [Route("/SyncPlay/Ping", "POST", Summary = "Update session ping")] [Authenticated] - public class SyncPlayUpdatePing : IReturnVoid + public class SyncPlayPing : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - [ApiMember(Name = "Ping", IsRequired = true, DataType = "double", ParameterType = "query", Verb = "POST")] public double Ping { get; set; } } @@ -158,7 +128,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayNewGroup request) + public void Post(SyncPlayNew request) { var currentSession = GetSession(_sessionContext); _syncPlayManager.NewGroup(currentSession, CancellationToken.None); @@ -168,30 +138,20 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayJoinGroup request) + public void Post(SyncPlayJoin request) { var currentSession = GetSession(_sessionContext); Guid groupId; - Guid playingItemId = Guid.Empty; - if (!Guid.TryParse(request.GroupId, out groupId)) { Logger.LogError("JoinGroup: {0} is not a valid format for GroupId. Ignoring request.", request.GroupId); return; } - // Both null and empty strings mean that client isn't playing anything - if (!string.IsNullOrEmpty(request.PlayingItemId) && !Guid.TryParse(request.PlayingItemId, out playingItemId)) - { - Logger.LogError("JoinGroup: {0} is not a valid format for PlayingItemId. Ignoring request.", request.PlayingItemId); - return; - } - var joinRequest = new JoinGroupRequest() { - GroupId = groupId, - PlayingItemId = playingItemId + GroupId = groupId }; _syncPlayManager.JoinGroup(currentSession, groupId, joinRequest, CancellationToken.None); @@ -201,7 +161,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayLeaveGroup request) + public void Post(SyncPlayLeave request) { var currentSession = GetSession(_sessionContext); _syncPlayManager.LeaveGroup(currentSession, CancellationToken.None); @@ -212,7 +172,7 @@ namespace MediaBrowser.Api.SyncPlay /// </summary> /// <param name="request">The request.</param> /// <value>The requested list of groups.</value> - public List<GroupInfoView> Post(SyncPlayListGroups request) + public List<GroupInfoView> Get(SyncPlayList request) { var currentSession = GetSession(_sessionContext); var filterItemId = Guid.Empty; @@ -229,7 +189,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayPlayRequest request) + public void Post(SyncPlayPlay request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() @@ -243,7 +203,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayPauseRequest request) + public void Post(SyncPlayPause request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() @@ -257,7 +217,7 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlaySeekRequest request) + public void Post(SyncPlaySeek request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() @@ -272,12 +232,12 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayBufferingRequest request) + public void Post(SyncPlayBuffering request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() { - Type = request.BufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering, + Type = request.BufferingDone ? PlaybackRequestType.Ready : PlaybackRequestType.Buffer, When = DateTime.Parse(request.When), PositionTicks = request.PositionTicks }; @@ -288,12 +248,12 @@ namespace MediaBrowser.Api.SyncPlay /// Handles the specified request. /// </summary> /// <param name="request">The request.</param> - public void Post(SyncPlayUpdatePing request) + public void Post(SyncPlayPing request) { var currentSession = GetSession(_sessionContext); var syncPlayRequest = new PlaybackRequest() { - Type = PlaybackRequestType.UpdatePing, + Type = PlaybackRequestType.Ping, Ping = Convert.ToInt64(request.Ping) }; _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index 8e4860be4b..39976371a9 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.System { /// <summary> - /// Class SessionInfoWebSocketListener + /// Class SessionInfoWebSocketListener. /// </summary> public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<ActivityLogEntry[], WebSocketListenerState> { @@ -19,7 +19,7 @@ namespace MediaBrowser.Api.System protected override string Name => "ActivityLogEntry"; /// <summary> - /// The _kernel + /// The _kernel. /// </summary> private readonly IActivityManager _activityManager; diff --git a/MediaBrowser.Api/TranscodingJob.cs b/MediaBrowser.Api/TranscodingJob.cs index 8c24e3ce18..bfc311a272 100644 --- a/MediaBrowser.Api/TranscodingJob.cs +++ b/MediaBrowser.Api/TranscodingJob.cs @@ -32,6 +32,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The path.</value> public MediaSourceInfo MediaSource { get; set; } + public string Path { get; set; } /// <summary> /// Gets or sets the type. @@ -43,6 +44,7 @@ namespace MediaBrowser.Api /// </summary> /// <value>The process.</value> public Process Process { get; set; } + public ILogger Logger { get; private set; } /// <summary> /// Gets or sets the active request count. @@ -62,18 +64,23 @@ namespace MediaBrowser.Api public object ProcessLock = new object(); public bool HasExited { get; set; } + public bool IsUserPaused { get; set; } public string Id { get; set; } public float? Framerate { get; set; } + public double? CompletionPercentage { get; set; } public long? BytesDownloaded { get; set; } + public long? BytesTranscoded { get; set; } + public int? BitRate { get; set; } public long? TranscodingPositionTicks { get; set; } + public long? DownloadPositionTicks { get; set; } public TranscodingThrottler TranscodingThrottler { get; set; } @@ -81,6 +88,7 @@ namespace MediaBrowser.Api private readonly object _timerLock = new object(); public DateTime LastPingDate { get; set; } + public int PingTimeout { get; set; } public TranscodingJob(ILogger logger) diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs deleted file mode 100644 index bef91d54df..0000000000 --- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class GetArtists - /// </summary> - [Route("/Artists", "GET", Summary = "Gets all artists from a given item, folder, or the entire library")] - public class GetArtists : GetItemsByName - { - } - - [Route("/Artists/AlbumArtists", "GET", Summary = "Gets all album artists from a given item, folder, or the entire library")] - public class GetAlbumArtists : GetItemsByName - { - } - - [Route("/Artists/{Name}", "GET", Summary = "Gets an artist, by name")] - public class GetArtist : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the name. - /// </summary> - /// <value>The name.</value> - [ApiMember(Name = "Name", Description = "The artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - - /// <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 Guid UserId { get; set; } - } - - /// <summary> - /// Class ArtistsService - /// </summary> - [Authenticated] - public class ArtistsService : BaseItemsByNameService<MusicArtist> - { - public ArtistsService( - ILogger<ArtistsService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IUserDataManager userDataRepository, - IDtoService dtoService, - IAuthorizationContext authorizationContext) - : base( - logger, - serverConfigurationManager, - httpResultFactory, - userManager, - libraryManager, - userDataRepository, - dtoService, - authorizationContext) - { - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetArtist request) - { - return GetItem(request); - } - - /// <summary> - /// Gets the item. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{BaseItemDto}.</returns> - private BaseItemDto GetItem(GetArtist request) - { - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - - var item = GetArtist(request.Name, LibraryManager, dtoOptions); - - if (!request.UserId.Equals(Guid.Empty)) - { - var user = UserManager.GetUserById(request.UserId); - - return DtoService.GetBaseItemDto(item, dtoOptions, user); - } - - return DtoService.GetBaseItemDto(item, dtoOptions); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetArtists request) - { - return GetResultSlim(request); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetAlbumArtists request) - { - var result = GetResultSlim(request); - - return ToOptimizedResult(result); - } - - protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) - { - return request is GetAlbumArtists ? LibraryManager.GetAlbumArtists(query) : LibraryManager.GetArtists(query); - } - - /// <summary> - /// Gets all items. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="items">The items.</param> - /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns> - protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items) - { - throw new NotImplementedException(); - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs deleted file mode 100644 index a1ec084679..0000000000 --- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs +++ /dev/null @@ -1,388 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Jellyfin.Data.Entities; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class BaseItemsByNameService - /// </summary> - /// <typeparam name="TItemType">The type of the T item type.</typeparam> - public abstract class BaseItemsByNameService<TItemType> : BaseApiService - where TItemType : BaseItem, IItemByName - { - /// <summary> - /// Initializes a new instance of the <see cref="BaseItemsByNameService{TItemType}" /> class. - /// </summary> - /// <param name="userManager">The user manager.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="userDataRepository">The user data repository.</param> - /// <param name="dtoService">The dto service.</param> - protected BaseItemsByNameService( - ILogger<BaseItemsByNameService<TItemType>> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IUserDataManager userDataRepository, - IDtoService dtoService, - IAuthorizationContext authorizationContext) - : base(logger, serverConfigurationManager, httpResultFactory) - { - UserManager = userManager; - LibraryManager = libraryManager; - UserDataRepository = userDataRepository; - DtoService = dtoService; - AuthorizationContext = authorizationContext; - } - - /// <summary> - /// Gets the _user manager. - /// </summary> - protected IUserManager UserManager { get; } - - /// <summary> - /// Gets the library manager - /// </summary> - protected ILibraryManager LibraryManager { get; } - - protected IUserDataManager UserDataRepository { get; } - - protected IDtoService DtoService { get; } - - protected IAuthorizationContext AuthorizationContext { get; } - - protected BaseItem GetParentItem(GetItemsByName request) - { - BaseItem parentItem; - - if (!request.UserId.Equals(Guid.Empty)) - { - var user = UserManager.GetUserById(request.UserId); - parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId); - } - else - { - parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId); - } - - return parentItem; - } - - protected string GetParentItemViewType(GetItemsByName request) - { - var parent = GetParentItem(request); - - if (parent is IHasCollectionType collectionFolder) - { - return collectionFolder.CollectionType; - } - - return null; - } - - protected QueryResult<BaseItemDto> GetResultSlim(GetItemsByName request) - { - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - - User user = null; - BaseItem parentItem; - - if (!request.UserId.Equals(Guid.Empty)) - { - user = UserManager.GetUserById(request.UserId); - parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId); - } - else - { - parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId); - } - - var excludeItemTypes = request.GetExcludeItemTypes(); - var includeItemTypes = request.GetIncludeItemTypes(); - var mediaTypes = request.GetMediaTypes(); - - var query = new InternalItemsQuery(user) - { - ExcludeItemTypes = excludeItemTypes, - IncludeItemTypes = includeItemTypes, - MediaTypes = mediaTypes, - StartIndex = request.StartIndex, - Limit = request.Limit, - IsFavorite = request.IsFavorite, - NameLessThan = request.NameLessThan, - NameStartsWith = request.NameStartsWith, - NameStartsWithOrGreater = request.NameStartsWithOrGreater, - Tags = request.GetTags(), - OfficialRatings = request.GetOfficialRatings(), - Genres = request.GetGenres(), - GenreIds = GetGuids(request.GenreIds), - StudioIds = GetGuids(request.StudioIds), - Person = request.Person, - PersonIds = GetGuids(request.PersonIds), - PersonTypes = request.GetPersonTypes(), - Years = request.GetYears(), - MinCommunityRating = request.MinCommunityRating, - DtoOptions = dtoOptions, - SearchTerm = request.SearchTerm, - EnableTotalRecordCount = request.EnableTotalRecordCount - }; - - if (!string.IsNullOrWhiteSpace(request.ParentId)) - { - if (parentItem is Folder) - { - query.AncestorIds = new[] { new Guid(request.ParentId) }; - } - else - { - query.ItemIds = new[] { new Guid(request.ParentId) }; - } - } - - // Studios - if (!string.IsNullOrEmpty(request.Studios)) - { - query.StudioIds = request.Studios.Split('|').Select(i => - { - try - { - return LibraryManager.GetStudio(i); - } - catch - { - return null; - } - }).Where(i => i != null).Select(i => i.Id).ToArray(); - } - - 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; - } - } - - var result = GetItems(request, query); - - var dtos = result.Items.Select(i => - { - var dto = DtoService.GetItemByNameDto(i.Item1, dtoOptions, null, user); - - if (!string.IsNullOrWhiteSpace(request.IncludeItemTypes)) - { - SetItemCounts(dto, i.Item2); - } - return dto; - }); - - return new QueryResult<BaseItemDto> - { - Items = dtos.ToArray(), - TotalRecordCount = result.TotalRecordCount - }; - } - - protected virtual QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) - { - return new QueryResult<(BaseItem, ItemCounts)>(); - } - - private void SetItemCounts(BaseItemDto dto, ItemCounts counts) - { - dto.ChildCount = counts.ItemCount; - dto.ProgramCount = counts.ProgramCount; - dto.SeriesCount = counts.SeriesCount; - dto.EpisodeCount = counts.EpisodeCount; - dto.MovieCount = counts.MovieCount; - dto.TrailerCount = counts.TrailerCount; - dto.AlbumCount = counts.AlbumCount; - dto.SongCount = counts.SongCount; - dto.ArtistCount = counts.ArtistCount; - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{ItemsResult}.</returns> - protected QueryResult<BaseItemDto> GetResult(GetItemsByName request) - { - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - - User user = null; - BaseItem parentItem; - - if (!request.UserId.Equals(Guid.Empty)) - { - user = UserManager.GetUserById(request.UserId); - parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.GetUserRootFolder() : LibraryManager.GetItemById(request.ParentId); - } - else - { - parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId); - } - - IList<BaseItem> items; - - var excludeItemTypes = request.GetExcludeItemTypes(); - var includeItemTypes = request.GetIncludeItemTypes(); - var mediaTypes = request.GetMediaTypes(); - - var query = new InternalItemsQuery(user) - { - ExcludeItemTypes = excludeItemTypes, - IncludeItemTypes = includeItemTypes, - MediaTypes = mediaTypes, - DtoOptions = dtoOptions - }; - - bool Filter(BaseItem i) => FilterItem(request, i, excludeItemTypes, includeItemTypes, mediaTypes); - - if (parentItem.IsFolder) - { - var folder = (Folder)parentItem; - - if (!request.UserId.Equals(Guid.Empty)) - { - items = request.Recursive ? - folder.GetRecursiveChildren(user, query).ToList() : - folder.GetChildren(user, true).Where(Filter).ToList(); - } - else - { - items = request.Recursive ? - folder.GetRecursiveChildren(Filter) : - folder.Children.Where(Filter).ToList(); - } - } - else - { - items = new[] { parentItem }.Where(Filter).ToList(); - } - - var extractedItems = GetAllItems(request, items); - - var filteredItems = LibraryManager.Sort(extractedItems, user, request.GetOrderBy()); - - var ibnItemsArray = filteredItems.ToList(); - - IEnumerable<BaseItem> ibnItems = ibnItemsArray; - - var result = new QueryResult<BaseItemDto> - { - TotalRecordCount = ibnItemsArray.Count - }; - - if (request.StartIndex.HasValue || request.Limit.HasValue) - { - if (request.StartIndex.HasValue) - { - ibnItems = ibnItems.Skip(request.StartIndex.Value); - } - - if (request.Limit.HasValue) - { - ibnItems = ibnItems.Take(request.Limit.Value); - } - - } - - var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>())); - - var dtos = tuples.Select(i => DtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, user)); - - result.Items = dtos.Where(i => i != null).ToArray(); - - return result; - } - - /// <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> - /// <param name="mediaTypes">The media types.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - private bool FilterItem(GetItemsByName request, BaseItem f, string[] excludeItemTypes, string[] includeItemTypes, string[] mediaTypes) - { - // Exclude item types - if (excludeItemTypes.Length > 0 && excludeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - - // Include item types - if (includeItemTypes.Length > 0 && !includeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - - // Include MediaTypes - if (mediaTypes.Length > 0 && !mediaTypes.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - - return true; - } - - /// <summary> - /// Gets all items. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="items">The items.</param> - /// <returns>IEnumerable{Task{`0}}.</returns> - protected abstract IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items); - } - - /// <summary> - /// Class GetItemsByName - /// </summary> - public class GetItemsByName : BaseItemsRequest, IReturn<QueryResult<BaseItemDto>> - { - public GetItemsByName() - { - Recursive = true; - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs deleted file mode 100644 index 7561b5c892..0000000000 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ /dev/null @@ -1,475 +0,0 @@ -using System; -using System.Linq; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Api.UserLibrary -{ - public abstract class BaseItemsRequest : IHasDtoOptions - { - protected BaseItemsRequest() - { - EnableImages = true; - EnableTotalRecordCount = true; - } - - /// <summary> - /// Gets or sets the max offical rating. - /// </summary> - /// <value>The max offical rating.</value> - [ApiMember(Name = "MaxOfficialRating", Description = "Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string MaxOfficialRating { get; set; } - - [ApiMember(Name = "HasThemeSong", Description = "Optional filter by items with theme songs.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? HasThemeSong { get; set; } - - [ApiMember(Name = "HasThemeVideo", Description = "Optional filter by items with theme videos.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? HasThemeVideo { get; set; } - - [ApiMember(Name = "HasSubtitles", Description = "Optional filter by items with subtitles.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? HasSubtitles { get; set; } - - [ApiMember(Name = "HasSpecialFeature", Description = "Optional filter by items with special features.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? HasSpecialFeature { get; set; } - - [ApiMember(Name = "HasTrailer", Description = "Optional filter by items with trailers.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? HasTrailer { get; set; } - - [ApiMember(Name = "AdjacentTo", Description = "Optional. Return items that are siblings of a supplied item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string AdjacentTo { get; set; } - - [ApiMember(Name = "MinIndexNumber", Description = "Optional filter by minimum index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? MinIndexNumber { get; set; } - - [ApiMember(Name = "ParentIndexNumber", Description = "Optional filter by parent index number.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ParentIndexNumber { get; set; } - - [ApiMember(Name = "HasParentalRating", Description = "Optional filter by items that have or do not have a parental rating", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? HasParentalRating { get; set; } - - [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; } - - [ApiMember(Name = "ExcludeLocationTypes", 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 ExcludeLocationTypes { get; set; } - - [ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsMissing { get; set; } - - [ApiMember(Name = "IsUnaired", Description = "Optional filter by items that are unaired episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsUnaired { get; set; } - - [ApiMember(Name = "MinCommunityRating", Description = "Optional filter by minimum community rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public double? MinCommunityRating { get; set; } - - [ApiMember(Name = "MinCriticRating", Description = "Optional filter by minimum critic rating.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public double? MinCriticRating { get; set; } - - [ApiMember(Name = "AiredDuringSeason", Description = "Gets all episodes that aired during a season, including specials.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? AiredDuringSeason { get; set; } - - [ApiMember(Name = "MinPremiereDate", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string MinPremiereDate { get; set; } - - [ApiMember(Name = "MinDateLastSaved", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string MinDateLastSaved { get; set; } - - [ApiMember(Name = "MinDateLastSavedForUser", Description = "Optional. The minimum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string MinDateLastSavedForUser { get; set; } - - [ApiMember(Name = "MaxPremiereDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string MaxPremiereDate { get; set; } - - [ApiMember(Name = "HasOverview", Description = "Optional filter by items that have an overview or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? HasOverview { get; set; } - - [ApiMember(Name = "HasImdbId", Description = "Optional filter by items that have an imdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? HasImdbId { get; set; } - - [ApiMember(Name = "HasTmdbId", Description = "Optional filter by items that have a tmdb id or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? HasTmdbId { get; set; } - - [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 = "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; } - - /// <summary> - /// Skips over a given number of items within the results. Use for paging. - /// </summary> - /// <value>The start index.</value> - [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; } - - /// <summary> - /// The maximum number of items to return - /// </summary> - /// <value>The limit.</value> - [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? Limit { get; set; } - - /// <summary> - /// Whether or not to perform the query recursively - /// </summary> - /// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value> - [ApiMember(Name = "Recursive", Description = "When searching within folders, this determines whether or not the search will be recursive. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool Recursive { get; set; } - - public string SearchTerm { get; set; } - - /// <summary> - /// Gets or sets the sort order. - /// </summary> - /// <value>The sort order.</value> - [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string SortOrder { get; set; } - - /// <summary> - /// Specify this to localize the search to a specific item or folder. Omit to use the root. - /// </summary> - /// <value>The parent id.</value> - [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; } - - /// <summary> - /// Fields to return within the items, in addition to basic information - /// </summary> - /// <value>The fields.</value> - [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Fields { get; set; } - - /// <summary> - /// Gets or sets the exclude item types. - /// </summary> - /// <value>The exclude item types.</value> - [ApiMember(Name = "ExcludeItemTypes", 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 ExcludeItemTypes { get; set; } - - /// <summary> - /// Gets or sets the include item types. - /// </summary> - /// <value>The include item types.</value> - [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> - /// Filters to apply to the results - /// </summary> - /// <value>The filters.</value> - [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Filters { get; set; } - - /// <summary> - /// Gets or sets the Isfavorite option - /// </summary> - /// <value>IsFavorite</value> - [ApiMember(Name = "IsFavorite", Description = "Optional filter by items that are marked as favorite, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsFavorite { get; set; } - - /// <summary> - /// Gets or sets the media types. - /// </summary> - /// <value>The media types.</value> - [ApiMember(Name = "MediaTypes", Description = "Optional filter by MediaType. Allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string MediaTypes { get; set; } - - /// <summary> - /// Gets or sets the image types. - /// </summary> - /// <value>The image types.</value> - [ApiMember(Name = "ImageTypes", Description = "Optional. If specified, results will be filtered based on those containing image types. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string ImageTypes { get; set; } - - /// <summary> - /// What to sort the results by - /// </summary> - /// <value>The sort by.</value> - [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string SortBy { get; set; } - - [ApiMember(Name = "IsPlayed", Description = "Optional filter by items that are played, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsPlayed { get; set; } - - /// <summary> - /// Limit results to items containing specific genres - /// </summary> - /// <value>The genres.</value> - [ApiMember(Name = "Genres", Description = "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Genres { get; set; } - - public string GenreIds { get; set; } - - [ApiMember(Name = "OfficialRatings", Description = "Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string OfficialRatings { get; set; } - - [ApiMember(Name = "Tags", Description = "Optional. If specified, results will be filtered based on tag. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Tags { get; set; } - - /// <summary> - /// Limit results to items containing specific years - /// </summary> - /// <value>The years.</value> - [ApiMember(Name = "Years", Description = "Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Years { get; set; } - - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - /// <summary> - /// Limit results to items containing a specific person - /// </summary> - /// <value>The person.</value> - [ApiMember(Name = "Person", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Person { get; set; } - - [ApiMember(Name = "PersonIds", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string PersonIds { get; set; } - - /// <summary> - /// If the Person filter is used, this can also be used to restrict to a specific person type - /// </summary> - /// <value>The type of the person.</value> - [ApiMember(Name = "PersonTypes", Description = "Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string PersonTypes { get; set; } - - /// <summary> - /// Limit results to items containing specific studios - /// </summary> - /// <value>The studios.</value> - [ApiMember(Name = "Studios", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Studios { get; set; } - - [ApiMember(Name = "StudioIds", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string StudioIds { get; set; } - - /// <summary> - /// Gets or sets the studios. - /// </summary> - /// <value>The studios.</value> - [ApiMember(Name = "Artists", 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 Artists { get; set; } - - public string ExcludeArtistIds { get; set; } - - [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; } - - public string AlbumIds { get; set; } - - /// <summary> - /// Gets or sets the item ids. - /// </summary> - /// <value>The item ids.</value> - [ApiMember(Name = "Ids", Description = "Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string Ids { get; set; } - - /// <summary> - /// Gets or sets the video types. - /// </summary> - /// <value>The video types.</value> - [ApiMember(Name = "VideoTypes", Description = "Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string VideoTypes { 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 Guid UserId { get; set; } - - /// <summary> - /// Gets or sets the min offical rating. - /// </summary> - /// <value>The min offical rating.</value> - [ApiMember(Name = "MinOfficialRating", Description = "Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string MinOfficialRating { get; set; } - - [ApiMember(Name = "IsLocked", Description = "Optional filter by items that are locked.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? IsLocked { get; set; } - - [ApiMember(Name = "IsPlaceHolder", Description = "Optional filter by items that are placeholders", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? IsPlaceHolder { get; set; } - - [ApiMember(Name = "HasOfficialRating", Description = "Optional filter by items that have official ratings", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public bool? HasOfficialRating { get; set; } - - [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> - /// <value>The video formats.</value> - [ApiMember(Name = "Is3D", Description = "Optional filter by items that are 3D, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? Is3D { get; set; } - - /// <summary> - /// Gets or sets the series status. - /// </summary> - /// <value>The series status.</value> - [ApiMember(Name = "SeriesStatus", Description = "Optional filter by Series Status. Allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] - public string SeriesStatus { get; set; } - - [ApiMember(Name = "NameStartsWithOrGreater", Description = "Optional filter by items whose name is sorted equally or greater than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string NameStartsWithOrGreater { get; set; } - - [ApiMember(Name = "NameStartsWith", Description = "Optional filter by items whose name is sorted equally than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string NameStartsWith { get; set; } - - [ApiMember(Name = "NameLessThan", Description = "Optional filter by items whose name is equally or lesser than a given input string.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string NameLessThan { get; set; } - - public string[] GetGenres() - { - return (Genres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetTags() - { - return (Tags ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetOfficialRatings() - { - return (OfficialRatings ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetMediaTypes() - { - return (MediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetIncludeItemTypes() - { - return (IncludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetExcludeItemTypes() - { - return (ExcludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - - public int[] GetYears() - { - return (Years ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray(); - } - - public string[] GetStudios() - { - return (Studios ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); - } - - public string[] GetPersonTypes() - { - return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - } - - public VideoType[] GetVideoTypes() - { - return string.IsNullOrEmpty(VideoTypes) - ? Array.Empty<VideoType>() - : VideoTypes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(v => Enum.Parse<VideoType>(v, true)).ToArray(); - } - - /// <summary> - /// Gets the filters. - /// </summary> - /// <returns>IEnumerable{ItemFilter}.</returns> - public ItemFilter[] GetFilters() - { - var val = Filters; - - return string.IsNullOrEmpty(val) - ? Array.Empty<ItemFilter>() - : val.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(v => Enum.Parse<ItemFilter>(v, true)).ToArray(); - } - - /// <summary> - /// Gets the image types. - /// </summary> - /// <returns>IEnumerable{ImageType}.</returns> - public ImageType[] GetImageTypes() - { - var val = ImageTypes; - - return string.IsNullOrEmpty(val) - ? Array.Empty<ImageType>() - : val.Split(',').Select(v => Enum.Parse<ImageType>(v, true)).ToArray(); - } - - /// <summary> - /// Gets the order by. - /// </summary> - /// <returns>IEnumerable{ItemSortBy}.</returns> - public ValueTuple<string, SortOrder>[] GetOrderBy() - { - return GetOrderBy(SortBy, SortOrder); - } - - public static ValueTuple<string, SortOrder>[] GetOrderBy(string sortBy, string requestedSortOrder) - { - var val = sortBy; - - if (string.IsNullOrEmpty(val)) - { - return Array.Empty<ValueTuple<string, SortOrder>>(); - } - - var vals = val.Split(','); - if (string.IsNullOrWhiteSpace(requestedSortOrder)) - { - requestedSortOrder = "Ascending"; - } - - var sortOrders = requestedSortOrder.Split(','); - - var result = new ValueTuple<string, SortOrder>[vals.Length]; - - for (var i = 0; i < vals.Length; i++) - { - var sortOrderIndex = sortOrders.Length > i ? i : 0; - - 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 ValueTuple<string, SortOrder>(vals[i], sortOrder); - } - - return result; - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs deleted file mode 100644 index 1fa272a5f7..0000000000 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class GetGenres - /// </summary> - [Route("/Genres", "GET", Summary = "Gets all genres from a given item, folder, or the entire library")] - public class GetGenres : GetItemsByName - { - } - - /// <summary> - /// Class GetGenre - /// </summary> - [Route("/Genres/{Name}", "GET", Summary = "Gets a genre, by name")] - public class GetGenre : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the name. - /// </summary> - /// <value>The name.</value> - [ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - - /// <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 Guid UserId { get; set; } - } - - /// <summary> - /// Class GenresService - /// </summary> - [Authenticated] - public class GenresService : BaseItemsByNameService<Genre> - { - public GenresService( - ILogger<GenresService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IUserDataManager userDataRepository, - IDtoService dtoService, - IAuthorizationContext authorizationContext) - : base( - logger, - serverConfigurationManager, - httpResultFactory, - userManager, - libraryManager, - userDataRepository, - dtoService, - authorizationContext) - { - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetGenre request) - { - var result = GetItem(request); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the item. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{BaseItemDto}.</returns> - private BaseItemDto GetItem(GetGenre request) - { - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - - var item = GetGenre(request.Name, LibraryManager, dtoOptions); - - if (!request.UserId.Equals(Guid.Empty)) - { - var user = UserManager.GetUserById(request.UserId); - - return DtoService.GetBaseItemDto(item, dtoOptions, user); - } - - return DtoService.GetBaseItemDto(item, dtoOptions); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetGenres request) - { - var result = GetResultSlim(request); - - return ToOptimizedResult(result); - } - - protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) - { - var viewType = GetParentItemViewType(request); - - if (string.Equals(viewType, CollectionType.Music) || string.Equals(viewType, CollectionType.MusicVideos)) - { - return LibraryManager.GetMusicGenres(query); - } - - return LibraryManager.GetGenres(query); - } - - /// <summary> - /// Gets all items. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="items">The items.</param> - /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns> - protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items) - { - throw new NotImplementedException(); - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs deleted file mode 100644 index 49d534c364..0000000000 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ /dev/null @@ -1,514 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Jellyfin.Data.Entities; -using Jellyfin.Data.Enums; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; -using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class GetItems - /// </summary> - [Route("/Items", "GET", Summary = "Gets items based on a query.")] - [Route("/Users/{UserId}/Items", "GET", Summary = "Gets items based on a query.")] - public class GetItems : BaseItemsRequest, IReturn<QueryResult<BaseItemDto>> - { - } - - [Route("/Users/{UserId}/Items/Resume", "GET", Summary = "Gets items based on a query.")] - public class GetResumeItems : BaseItemsRequest, IReturn<QueryResult<BaseItemDto>> - { - } - - /// <summary> - /// Class ItemsService - /// </summary> - [Authenticated] - public class ItemsService : BaseApiService - { - /// <summary> - /// The _user manager - /// </summary> - private readonly IUserManager _userManager; - - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly ILocalizationManager _localization; - - private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; - - /// <summary> - /// Initializes a new instance of the <see cref="ItemsService" /> class. - /// </summary> - /// <param name="userManager">The user manager.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="localization">The localization.</param> - /// <param name="dtoService">The dto service.</param> - public ItemsService( - ILogger<ItemsService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - ILocalizationManager localization, - IDtoService dtoService, - IAuthorizationContext authContext) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _userManager = userManager; - _libraryManager = libraryManager; - _localization = localization; - _dtoService = dtoService; - _authContext = authContext; - } - - public object Get(GetResumeItems request) - { - var user = _userManager.GetUserById(request.UserId); - - var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId); - - var options = GetDtoOptions(_authContext, request); - - var ancestorIds = Array.Empty<Guid>(); - - var excludeFolderIds = user.GetPreference(PreferenceKind.LatestItemExcludes); - if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0) - { - ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) - .Where(i => i is Folder) - .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) - .Select(i => i.Id) - .ToArray(); - } - - var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) - { - OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) }, - IsResumable = true, - StartIndex = request.StartIndex, - Limit = request.Limit, - ParentId = parentIdGuid, - Recursive = true, - DtoOptions = options, - MediaTypes = request.GetMediaTypes(), - IsVirtualItem = false, - CollapseBoxSetItems = false, - EnableTotalRecordCount = request.EnableTotalRecordCount, - AncestorIds = ancestorIds, - IncludeItemTypes = request.GetIncludeItemTypes(), - ExcludeItemTypes = request.GetExcludeItemTypes(), - SearchTerm = request.SearchTerm - }); - - var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user); - - var result = new QueryResult<BaseItemDto> - { - StartIndex = request.StartIndex.GetValueOrDefault(), - TotalRecordCount = itemsResult.TotalRecordCount, - Items = returnItems - }; - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetItems request) - { - if (request == null) - { - throw new ArgumentNullException(nameof(request)); - } - - var result = GetItems(request); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the items. - /// </summary> - /// <param name="request">The request.</param> - private QueryResult<BaseItemDto> GetItems(GetItems request) - { - var user = request.UserId == Guid.Empty ? null : _userManager.GetUserById(request.UserId); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var result = GetQueryResult(request, dtoOptions, user); - - if (result == null) - { - throw new InvalidOperationException("GetItemsToSerialize returned null"); - } - - if (result.Items == null) - { - throw new InvalidOperationException("GetItemsToSerialize result.Items returned null"); - } - - var dtoList = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user); - - return new QueryResult<BaseItemDto> - { - StartIndex = request.StartIndex.GetValueOrDefault(), - TotalRecordCount = result.TotalRecordCount, - Items = dtoList - }; - } - - /// <summary> - /// Gets the items to serialize. - /// </summary> - private QueryResult<BaseItem> GetQueryResult(GetItems request, DtoOptions dtoOptions, User user) - { - if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) - || string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase)) - { - request.ParentId = null; - } - - BaseItem item = null; - - if (!string.IsNullOrEmpty(request.ParentId)) - { - item = _libraryManager.GetItemById(request.ParentId); - } - - if (item == null) - { - item = _libraryManager.GetUserRootFolder(); - } - - if (!(item is Folder folder)) - { - folder = _libraryManager.GetUserRootFolder(); - } - - if (folder is IHasCollectionType hasCollectionType - && string.Equals(hasCollectionType.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) - { - request.Recursive = true; - request.IncludeItemTypes = "Playlist"; - } - - bool isInEnabledFolder = user.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id) - // Assume all folders inside an EnabledChannel are enabled - || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id); - - var collectionFolders = _libraryManager.GetCollectionFolders(item); - foreach (var collectionFolder in collectionFolders) - { - if (user.GetPreference(PreferenceKind.EnabledFolders).Contains( - collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture), - StringComparer.OrdinalIgnoreCase)) - { - isInEnabledFolder = true; - } - } - - if (!(item is UserRootFolder) - && !isInEnabledFolder - && !user.HasPermission(PermissionKind.EnableAllFolders) - && !user.HasPermission(PermissionKind.EnableAllChannels)) - { - Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name); - return new QueryResult<BaseItem> - { - Items = Array.Empty<BaseItem>(), - TotalRecordCount = 0, - StartIndex = 0 - }; - } - - if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || !(item is UserRootFolder)) - { - return folder.GetItems(GetItemsQuery(request, dtoOptions, user)); - } - - var itemsArray = folder.GetChildren(user, true); - return new QueryResult<BaseItem> - { - Items = itemsArray, - TotalRecordCount = itemsArray.Count, - StartIndex = 0 - }; - } - - private InternalItemsQuery GetItemsQuery(GetItems request, DtoOptions dtoOptions, User user) - { - var query = new InternalItemsQuery(user) - { - IsPlayed = request.IsPlayed, - MediaTypes = request.GetMediaTypes(), - IncludeItemTypes = request.GetIncludeItemTypes(), - ExcludeItemTypes = request.GetExcludeItemTypes(), - Recursive = request.Recursive, - OrderBy = request.GetOrderBy(), - - IsFavorite = request.IsFavorite, - Limit = request.Limit, - StartIndex = request.StartIndex, - IsMissing = request.IsMissing, - IsUnaired = request.IsUnaired, - CollapseBoxSetItems = request.CollapseBoxSetItems, - NameLessThan = request.NameLessThan, - NameStartsWith = request.NameStartsWith, - NameStartsWithOrGreater = request.NameStartsWithOrGreater, - HasImdbId = request.HasImdbId, - IsPlaceHolder = request.IsPlaceHolder, - IsLocked = request.IsLocked, - MinWidth = request.MinWidth, - MinHeight = request.MinHeight, - MaxWidth = request.MaxWidth, - MaxHeight = request.MaxHeight, - Is3D = request.Is3D, - HasTvdbId = request.HasTvdbId, - HasTmdbId = request.HasTmdbId, - HasOverview = request.HasOverview, - HasOfficialRating = request.HasOfficialRating, - HasParentalRating = request.HasParentalRating, - HasSpecialFeature = request.HasSpecialFeature, - HasSubtitles = request.HasSubtitles, - 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 = GetGuids(request.ArtistIds), - AlbumArtistIds = GetGuids(request.AlbumArtistIds), - ContributingArtistIds = GetGuids(request.ContributingArtistIds), - GenreIds = GetGuids(request.GenreIds), - StudioIds = GetGuids(request.StudioIds), - Person = request.Person, - PersonIds = GetGuids(request.PersonIds), - PersonTypes = request.GetPersonTypes(), - Years = request.GetYears(), - ImageTypes = request.GetImageTypes(), - VideoTypes = request.GetVideoTypes(), - AdjacentTo = request.AdjacentTo, - ItemIds = GetGuids(request.Ids), - MinCommunityRating = request.MinCommunityRating, - MinCriticRating = request.MinCriticRating, - ParentId = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId), - ParentIndexNumber = request.ParentIndexNumber, - EnableTotalRecordCount = request.EnableTotalRecordCount, - ExcludeItemIds = GetGuids(request.ExcludeItemIds), - DtoOptions = dtoOptions, - SearchTerm = request.SearchTerm - }; - - if (!string.IsNullOrWhiteSpace(request.Ids) || !string.IsNullOrWhiteSpace(request.SearchTerm)) - { - query.CollapseBoxSetItems = false; - } - - 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; - } - } - - if (!string.IsNullOrEmpty(request.MinDateLastSaved)) - { - query.MinDateLastSaved = DateTime.Parse(request.MinDateLastSaved, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - } - - if (!string.IsNullOrEmpty(request.MinDateLastSavedForUser)) - { - query.MinDateLastSavedForUser = DateTime.Parse(request.MinDateLastSavedForUser, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - } - - if (!string.IsNullOrEmpty(request.MinPremiereDate)) - { - query.MinPremiereDate = DateTime.Parse(request.MinPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - } - - if (!string.IsNullOrEmpty(request.MaxPremiereDate)) - { - query.MaxPremiereDate = DateTime.Parse(request.MaxPremiereDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); - } - - // Filter by Series Status - if (!string.IsNullOrEmpty(request.SeriesStatus)) - { - query.SeriesStatuses = request.SeriesStatus.Split(',').Select(d => (SeriesStatus)Enum.Parse(typeof(SeriesStatus), d, true)).ToArray(); - } - - // ExcludeLocationTypes - if (!string.IsNullOrEmpty(request.ExcludeLocationTypes)) - { - var excludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray(); - if (excludeLocationTypes.Contains(LocationType.Virtual)) - { - query.IsVirtualItem = false; - } - } - - if (!string.IsNullOrEmpty(request.LocationTypes)) - { - var requestedLocationTypes = - request.LocationTypes.Split(','); - - if (requestedLocationTypes.Length > 0 && requestedLocationTypes.Length < 4) - { - query.IsVirtualItem = requestedLocationTypes.Contains(LocationType.Virtual.ToString()); - } - } - - // Min official rating - if (!string.IsNullOrWhiteSpace(request.MinOfficialRating)) - { - query.MinParentalRating = _localization.GetRatingLevel(request.MinOfficialRating); - } - - // Max official rating - if (!string.IsNullOrWhiteSpace(request.MaxOfficialRating)) - { - query.MaxParentalRating = _localization.GetRatingLevel(request.MaxOfficialRating); - } - - // Artists - if (!string.IsNullOrEmpty(request.Artists)) - { - query.ArtistIds = request.Artists.Split('|').Select(i => - { - try - { - return _libraryManager.GetArtist(i, new DtoOptions(false)); - } - catch - { - return null; - } - }).Where(i => i != null).Select(i => i.Id).ToArray(); - } - - // ExcludeArtistIds - if (!string.IsNullOrWhiteSpace(request.ExcludeArtistIds)) - { - query.ExcludeArtistIds = GetGuids(request.ExcludeArtistIds); - } - - if (!string.IsNullOrWhiteSpace(request.AlbumIds)) - { - query.AlbumIds = GetGuids(request.AlbumIds); - } - - // Albums - if (!string.IsNullOrEmpty(request.Albums)) - { - query.AlbumIds = request.Albums.Split('|').SelectMany(i => - { - return _libraryManager.GetItemIds(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, - Name = i, - Limit = 1 - }); - }).ToArray(); - } - - // Studios - if (!string.IsNullOrEmpty(request.Studios)) - { - query.StudioIds = request.Studios.Split('|').Select(i => - { - try - { - return _libraryManager.GetStudio(i); - } - catch - { - return null; - } - }).Where(i => i != null).Select(i => i.Id).ToArray(); - } - - // Apply default sorting if none requested - if (query.OrderBy.Count == 0) - { - // Albums by artist - if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], "MusicAlbum", StringComparison.OrdinalIgnoreCase)) - { - query.OrderBy = new[] - { - new ValueTuple<string, SortOrder>(ItemSortBy.ProductionYear, SortOrder.Descending), - new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) - }; - } - } - - return query; - } - } - - /// <summary> - /// Class DateCreatedComparer - /// </summary> - public class DateCreatedComparer : IComparer<BaseItem> - { - /// <summary> - /// Compares the specified x. - /// </summary> - /// <param name="x">The x.</param> - /// <param name="y">The y.</param> - /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) - { - return x.DateCreated.CompareTo(y.DateCreated); - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs deleted file mode 100644 index e9caca14aa..0000000000 --- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - [Route("/MusicGenres", "GET", Summary = "Gets all music genres from a given item, folder, or the entire library")] - public class GetMusicGenres : GetItemsByName - { - } - - [Route("/MusicGenres/{Name}", "GET", Summary = "Gets a music genre, by name")] - public class GetMusicGenre : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the name. - /// </summary> - /// <value>The name.</value> - [ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - - /// <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 Guid UserId { get; set; } - } - - [Authenticated] - public class MusicGenresService : BaseItemsByNameService<MusicGenre> - { - public MusicGenresService( - ILogger<MusicGenresService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IUserDataManager userDataRepository, - IDtoService dtoService, - IAuthorizationContext authorizationContext) - : base( - logger, - serverConfigurationManager, - httpResultFactory, - userManager, - libraryManager, - userDataRepository, - dtoService, - authorizationContext) - { - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetMusicGenre request) - { - var result = GetItem(request); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the item. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{BaseItemDto}.</returns> - private BaseItemDto GetItem(GetMusicGenre request) - { - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - - var item = GetMusicGenre(request.Name, LibraryManager, dtoOptions); - - if (!request.UserId.Equals(Guid.Empty)) - { - var user = UserManager.GetUserById(request.UserId); - - return DtoService.GetBaseItemDto(item, dtoOptions, user); - } - - return DtoService.GetBaseItemDto(item, dtoOptions); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetMusicGenres request) - { - var result = GetResultSlim(request); - - return ToOptimizedResult(result); - } - - protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) - { - return LibraryManager.GetMusicGenres(query); - } - - /// <summary> - /// Gets all items. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="items">The items.</param> - /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns> - protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items) - { - throw new NotImplementedException(); - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs deleted file mode 100644 index 3204e5219f..0000000000 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class GetPersons - /// </summary> - [Route("/Persons", "GET", Summary = "Gets all persons from a given item, folder, or the entire library")] - public class GetPersons : GetItemsByName - { - } - - /// <summary> - /// Class GetPerson - /// </summary> - [Route("/Persons/{Name}", "GET", Summary = "Gets a person, by name")] - public class GetPerson : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the name. - /// </summary> - /// <value>The name.</value> - [ApiMember(Name = "Name", Description = "The person name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - - /// <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 Guid UserId { get; set; } - } - - /// <summary> - /// Class PersonsService - /// </summary> - [Authenticated] - public class PersonsService : BaseItemsByNameService<Person> - { - public PersonsService( - ILogger<PersonsService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IUserDataManager userDataRepository, - IDtoService dtoService, - IAuthorizationContext authorizationContext) - : base( - logger, - serverConfigurationManager, - httpResultFactory, - userManager, - libraryManager, - userDataRepository, - dtoService, - authorizationContext) - { - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetPerson request) - { - var result = GetItem(request); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the item. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{BaseItemDto}.</returns> - private BaseItemDto GetItem(GetPerson request) - { - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - - var item = GetPerson(request.Name, LibraryManager, dtoOptions); - - if (!request.UserId.Equals(Guid.Empty)) - { - var user = UserManager.GetUserById(request.UserId); - - return DtoService.GetBaseItemDto(item, dtoOptions, user); - } - - return DtoService.GetBaseItemDto(item, dtoOptions); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetPersons request) - { - return GetResultSlim(request); - } - - /// <summary> - /// Gets all items. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="items">The items.</param> - /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns> - protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items) - { - throw new NotImplementedException(); - } - - protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) - { - var items = LibraryManager.GetPeopleItems(new InternalPeopleQuery - { - PersonTypes = query.PersonTypes, - NameContains = query.NameContains ?? query.SearchTerm - }); - - if ((query.IsFavorite ?? false) && query.User != null) - { - items = items.Where(i => UserDataRepository.GetUserData(query.User, i).IsFavorite).ToList(); - } - - return new QueryResult<(BaseItem, ItemCounts)> - { - TotalRecordCount = items.Count, - Items = items.Take(query.Limit ?? int.MaxValue).Select(i => (i as BaseItem, new ItemCounts())).ToArray() - }; - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs deleted file mode 100644 index ab231626bb..0000000000 --- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs +++ /dev/null @@ -1,456 +0,0 @@ -using System; -using System.Globalization; -using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Services; -using MediaBrowser.Model.Session; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class MarkPlayedItem - /// </summary> - [Route("/Users/{UserId}/PlayedItems/{Id}", "POST", Summary = "Marks an item as played")] - public class MarkPlayedItem : IReturn<UserItemDataDto> - { - /// <summary> - /// Gets or sets the user id. - /// </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; } - - [ApiMember(Name = "DatePlayed", Description = "The date the item was played (if any). Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string DatePlayed { 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; } - } - - /// <summary> - /// Class MarkUnplayedItem - /// </summary> - [Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE", Summary = "Marks an item as unplayed")] - public class MarkUnplayedItem : IReturn<UserItemDataDto> - { - /// <summary> - /// Gets or sets the user id. - /// </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; } - - /// <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; } - } - - [Route("/Sessions/Playing", "POST", Summary = "Reports playback has started within a session")] - public class ReportPlaybackStart : PlaybackStartInfo, IReturnVoid - { - } - - [Route("/Sessions/Playing/Progress", "POST", Summary = "Reports playback progress within a session")] - public class ReportPlaybackProgress : PlaybackProgressInfo, IReturnVoid - { - } - - [Route("/Sessions/Playing/Ping", "POST", Summary = "Pings a playback session")] - public class PingPlaybackSession : IReturnVoid - { - [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string PlaySessionId { get; set; } - } - - [Route("/Sessions/Playing/Stopped", "POST", Summary = "Reports playback has stopped within a session")] - public class ReportPlaybackStopped : PlaybackStopInfo, IReturnVoid - { - } - - /// <summary> - /// Class OnPlaybackStart - /// </summary> - [Route("/Users/{UserId}/PlayingItems/{Id}", "POST", Summary = "Reports that a user has begun playing an item")] - public class OnPlaybackStart : IReturnVoid - { - /// <summary> - /// Gets or sets the user id. - /// </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; } - - /// <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; } - - [ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string MediaSourceId { get; set; } - - [ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")] - public bool CanSeek { get; set; } - - [ApiMember(Name = "AudioStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int? AudioStreamIndex { get; set; } - - [ApiMember(Name = "SubtitleStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int? SubtitleStreamIndex { get; set; } - - [ApiMember(Name = "PlayMethod", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public PlayMethod PlayMethod { get; set; } - - [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string LiveStreamId { get; set; } - - [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string PlaySessionId { get; set; } - } - - /// <summary> - /// Class OnPlaybackProgress - /// </summary> - [Route("/Users/{UserId}/PlayingItems/{Id}/Progress", "POST", Summary = "Reports a user's playback progress")] - public class OnPlaybackProgress : IReturnVoid - { - /// <summary> - /// Gets or sets the user id. - /// </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; } - - /// <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; } - - [ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] - public string MediaSourceId { get; set; } - - /// <summary> - /// Gets or sets the position ticks. - /// </summary> - /// <value>The position ticks.</value> - [ApiMember(Name = "PositionTicks", Description = "Optional. The current position, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public long? PositionTicks { get; set; } - - [ApiMember(Name = "IsPaused", Description = "Indicates if the player is paused.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")] - public bool IsPaused { get; set; } - - [ApiMember(Name = "IsMuted", Description = "Indicates if the player is muted.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")] - public bool IsMuted { get; set; } - - [ApiMember(Name = "AudioStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int? AudioStreamIndex { get; set; } - - [ApiMember(Name = "SubtitleStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int? SubtitleStreamIndex { get; set; } - - [ApiMember(Name = "VolumeLevel", Description = "Scale of 0-100", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")] - public int? VolumeLevel { get; set; } - - [ApiMember(Name = "PlayMethod", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public PlayMethod PlayMethod { get; set; } - - [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string LiveStreamId { get; set; } - - [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string PlaySessionId { get; set; } - - [ApiMember(Name = "RepeatMode", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public RepeatMode RepeatMode { get; set; } - } - - /// <summary> - /// Class OnPlaybackStopped - /// </summary> - [Route("/Users/{UserId}/PlayingItems/{Id}", "DELETE", Summary = "Reports that a user has stopped playing an item")] - public class OnPlaybackStopped : IReturnVoid - { - /// <summary> - /// Gets or sets the user id. - /// </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; } - - /// <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; } - - [ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string MediaSourceId { get; set; } - - [ApiMember(Name = "NextMediaType", Description = "The next media type that will play", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")] - public string NextMediaType { get; set; } - - /// <summary> - /// Gets or sets the position ticks. - /// </summary> - /// <value>The position ticks.</value> - [ApiMember(Name = "PositionTicks", Description = "Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "DELETE")] - public long? PositionTicks { get; set; } - - [ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string LiveStreamId { get; set; } - - [ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] - public string PlaySessionId { get; set; } - } - - [Authenticated] - public class PlaystateService : BaseApiService - { - private readonly IUserManager _userManager; - private readonly IUserDataManager _userDataRepository; - private readonly ILibraryManager _libraryManager; - private readonly ISessionManager _sessionManager; - private readonly ISessionContext _sessionContext; - private readonly IAuthorizationContext _authContext; - - public PlaystateService( - ILogger<PlaystateService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - IUserDataManager userDataRepository, - ILibraryManager libraryManager, - ISessionManager sessionManager, - ISessionContext sessionContext, - IAuthorizationContext authContext) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _userManager = userManager; - _userDataRepository = userDataRepository; - _libraryManager = libraryManager; - _sessionManager = sessionManager; - _sessionContext = sessionContext; - _authContext = authContext; - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - public object Post(MarkPlayedItem request) - { - var result = MarkPlayed(request); - - return ToOptimizedResult(result); - } - - private UserItemDataDto MarkPlayed(MarkPlayedItem request) - { - var user = _userManager.GetUserById(Guid.Parse(request.UserId)); - - DateTime? datePlayed = null; - - if (!string.IsNullOrEmpty(request.DatePlayed)) - { - datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); - } - - var session = GetSession(_sessionContext); - - var dto = UpdatePlayedStatus(user, request.Id, true, datePlayed); - - foreach (var additionalUserInfo in session.AdditionalUsers) - { - var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId); - - UpdatePlayedStatus(additionalUser, request.Id, true, datePlayed); - } - - return dto; - } - - private PlayMethod ValidatePlayMethod(PlayMethod method, string playSessionId) - { - if (method == PlayMethod.Transcode) - { - var job = string.IsNullOrWhiteSpace(playSessionId) ? null : ApiEntryPoint.Instance.GetTranscodingJob(playSessionId); - if (job == null) - { - return PlayMethod.DirectPlay; - } - } - - return method; - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - public void Post(OnPlaybackStart request) - { - Post(new ReportPlaybackStart - { - CanSeek = request.CanSeek, - ItemId = new Guid(request.Id), - MediaSourceId = request.MediaSourceId, - AudioStreamIndex = request.AudioStreamIndex, - SubtitleStreamIndex = request.SubtitleStreamIndex, - PlayMethod = request.PlayMethod, - PlaySessionId = request.PlaySessionId, - LiveStreamId = request.LiveStreamId - }); - } - - public void Post(ReportPlaybackStart request) - { - request.PlayMethod = ValidatePlayMethod(request.PlayMethod, request.PlaySessionId); - - request.SessionId = GetSession(_sessionContext).Id; - - var task = _sessionManager.OnPlaybackStart(request); - - Task.WaitAll(task); - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - public void Post(OnPlaybackProgress request) - { - Post(new ReportPlaybackProgress - { - ItemId = new Guid(request.Id), - PositionTicks = request.PositionTicks, - IsMuted = request.IsMuted, - IsPaused = request.IsPaused, - MediaSourceId = request.MediaSourceId, - AudioStreamIndex = request.AudioStreamIndex, - SubtitleStreamIndex = request.SubtitleStreamIndex, - VolumeLevel = request.VolumeLevel, - PlayMethod = request.PlayMethod, - PlaySessionId = request.PlaySessionId, - LiveStreamId = request.LiveStreamId, - RepeatMode = request.RepeatMode - }); - } - - public void Post(ReportPlaybackProgress request) - { - request.PlayMethod = ValidatePlayMethod(request.PlayMethod, request.PlaySessionId); - - request.SessionId = GetSession(_sessionContext).Id; - - var task = _sessionManager.OnPlaybackProgress(request); - - Task.WaitAll(task); - } - - public void Post(PingPlaybackSession request) - { - ApiEntryPoint.Instance.PingTranscodingJob(request.PlaySessionId, null); - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - public Task Delete(OnPlaybackStopped request) - { - return Post(new ReportPlaybackStopped - { - ItemId = new Guid(request.Id), - PositionTicks = request.PositionTicks, - MediaSourceId = request.MediaSourceId, - PlaySessionId = request.PlaySessionId, - LiveStreamId = request.LiveStreamId, - NextMediaType = request.NextMediaType - }); - } - - public async Task Post(ReportPlaybackStopped request) - { - Logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", request.PlaySessionId ?? string.Empty); - - if (!string.IsNullOrWhiteSpace(request.PlaySessionId)) - { - await ApiEntryPoint.Instance.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true); - } - - request.SessionId = GetSession(_sessionContext).Id; - - await _sessionManager.OnPlaybackStopped(request); - } - - /// <summary> - /// Deletes the specified request. - /// </summary> - /// <param name="request">The request.</param> - public object Delete(MarkUnplayedItem request) - { - var task = MarkUnplayed(request); - - return ToOptimizedResult(task); - } - - private UserItemDataDto MarkUnplayed(MarkUnplayedItem request) - { - var user = _userManager.GetUserById(Guid.Parse(request.UserId)); - - var session = GetSession(_sessionContext); - - var dto = UpdatePlayedStatus(user, request.Id, false, null); - - foreach (var additionalUserInfo in session.AdditionalUsers) - { - var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId); - - UpdatePlayedStatus(additionalUser, request.Id, false, null); - } - - return dto; - } - - /// <summary> - /// Updates the played status. - /// </summary> - /// <param name="user">The user.</param> - /// <param name="itemId">The item id.</param> - /// <param name="wasPlayed">if set to <c>true</c> [was played].</param> - /// <param name="datePlayed">The date played.</param> - /// <returns>Task.</returns> - private UserItemDataDto UpdatePlayedStatus(User user, string itemId, bool wasPlayed, DateTime? datePlayed) - { - var item = _libraryManager.GetItemById(itemId); - - if (wasPlayed) - { - item.MarkPlayed(user, datePlayed, true); - } - else - { - item.MarkUnplayed(user); - } - - return _userDataRepository.GetUserDataDto(item, user); - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs deleted file mode 100644 index 683ce5d09d..0000000000 --- a/MediaBrowser.Api/UserLibrary/StudiosService.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class GetStudios - /// </summary> - [Route("/Studios", "GET", Summary = "Gets all studios from a given item, folder, or the entire library")] - public class GetStudios : GetItemsByName - { - } - - /// <summary> - /// Class GetStudio - /// </summary> - [Route("/Studios/{Name}", "GET", Summary = "Gets a studio, by name")] - public class GetStudio : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the name. - /// </summary> - /// <value>The name.</value> - [ApiMember(Name = "Name", Description = "The studio name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Name { get; set; } - - /// <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 Guid UserId { get; set; } - } - - /// <summary> - /// Class StudiosService - /// </summary> - [Authenticated] - public class StudiosService : BaseItemsByNameService<Studio> - { - public StudiosService( - ILogger<StudiosService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IUserDataManager userDataRepository, - IDtoService dtoService, - IAuthorizationContext authorizationContext) - : base( - logger, - serverConfigurationManager, - httpResultFactory, - userManager, - libraryManager, - userDataRepository, - dtoService, - authorizationContext) - { - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetStudio request) - { - var result = GetItem(request); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the item. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{BaseItemDto}.</returns> - private BaseItemDto GetItem(GetStudio request) - { - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - - var item = GetStudio(request.Name, LibraryManager, dtoOptions); - - if (!request.UserId.Equals(Guid.Empty)) - { - var user = UserManager.GetUserById(request.UserId); - - return DtoService.GetBaseItemDto(item, dtoOptions, user); - } - - return DtoService.GetBaseItemDto(item, dtoOptions); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetStudios request) - { - var result = GetResultSlim(request); - - return ToOptimizedResult(result); - } - - protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query) - { - return LibraryManager.GetStudios(query); - } - - /// <summary> - /// Gets all items. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="items">The items.</param> - /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns> - protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items) - { - throw new NotImplementedException(); - } - } -} diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs deleted file mode 100644 index f758528859..0000000000 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ /dev/null @@ -1,575 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class GetItem - /// </summary> - [Route("/Users/{UserId}/Items/{Id}", "GET", Summary = "Gets an item from a user's library")] - public class GetItem : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - 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 = "GET")] - public string Id { get; set; } - } - - /// <summary> - /// Class GetItem - /// </summary> - [Route("/Users/{UserId}/Items/Root", "GET", Summary = "Gets the root folder from a user's library")] - public class GetRootFolder : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } - } - - /// <summary> - /// Class GetIntros - /// </summary> - [Route("/Users/{UserId}/Items/{Id}/Intros", "GET", Summary = "Gets intros to play before the main media item plays")] - public class GetIntros : IReturn<QueryResult<BaseItemDto>> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } - - /// <summary> - /// Gets or sets the item id. - /// </summary> - /// <value>The item id.</value> - [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - /// <summary> - /// Class MarkFavoriteItem - /// </summary> - [Route("/Users/{UserId}/FavoriteItems/{Id}", "POST", Summary = "Marks an item as a favorite")] - public class MarkFavoriteItem : IReturn<UserItemDataDto> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - 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 Guid Id { get; set; } - } - - /// <summary> - /// Class UnmarkFavoriteItem - /// </summary> - [Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE", Summary = "Unmarks an item as a favorite")] - public class UnmarkFavoriteItem : IReturn<UserItemDataDto> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - 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 Guid Id { get; set; } - } - - /// <summary> - /// Class ClearUserItemRating - /// </summary> - [Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE", Summary = "Deletes a user's saved personal rating for an item")] - public class DeleteUserItemRating : IReturn<UserItemDataDto> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] - 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 Guid Id { get; set; } - } - - /// <summary> - /// Class UpdateUserItemRating - /// </summary> - [Route("/Users/{UserId}/Items/{Id}/Rating", "POST", Summary = "Updates a user's rating for an item")] - public class UpdateUserItemRating : IReturn<UserItemDataDto> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - 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 Guid Id { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes. - /// </summary> - /// <value><c>true</c> if likes; otherwise, <c>false</c>.</value> - [ApiMember(Name = "Likes", Description = "Whether the user likes the item or not. true/false", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")] - public bool Likes { get; set; } - } - - /// <summary> - /// Class GetLocalTrailers - /// </summary> - [Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET", Summary = "Gets local trailers for an item")] - public class GetLocalTrailers : IReturn<BaseItemDto[]> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - 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 = "GET")] - public string Id { get; set; } - } - - /// <summary> - /// Class GetSpecialFeatures - /// </summary> - [Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET", Summary = "Gets special features for an item")] - public class GetSpecialFeatures : IReturn<BaseItemDto[]> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } - - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - [ApiMember(Name = "Id", Description = "Movie Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - } - - [Route("/Users/{UserId}/Items/Latest", "GET", Summary = "Gets latest media")] - public class GetLatestMedia : IReturn<BaseItemDto[]>, IHasDtoOptions - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - 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 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; } - - [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; } - - [ApiMember(Name = "IsFolder", Description = "Filter by items that are folders, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsFolder { get; set; } - - [ApiMember(Name = "IsPlayed", Description = "Filter by items that are played, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool? IsPlayed { get; set; } - - [ApiMember(Name = "GroupItems", Description = "Whether or not to group items into a parent container.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool GroupItems { get; set; } - - [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableImages { get; set; } - - [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] - public int? ImageTypeLimit { get; set; } - - [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string EnableImageTypes { get; set; } - - [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] - public bool? EnableUserData { get; set; } - - public GetLatestMedia() - { - Limit = 20; - GroupItems = true; - } - } - - /// <summary> - /// Class UserLibraryService - /// </summary> - [Authenticated] - public class UserLibraryService : BaseApiService - { - private readonly IUserManager _userManager; - private readonly IUserDataManager _userDataRepository; - private readonly ILibraryManager _libraryManager; - private readonly IDtoService _dtoService; - private readonly IUserViewManager _userViewManager; - private readonly IFileSystem _fileSystem; - private readonly IAuthorizationContext _authContext; - - public UserLibraryService( - ILogger<UserLibraryService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IUserDataManager userDataRepository, - IDtoService dtoService, - IUserViewManager userViewManager, - IFileSystem fileSystem, - IAuthorizationContext authContext) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _userManager = userManager; - _libraryManager = libraryManager; - _userDataRepository = userDataRepository; - _dtoService = dtoService; - _userViewManager = userViewManager; - _fileSystem = fileSystem; - _authContext = authContext; - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetSpecialFeatures request) - { - var result = GetAsync(request); - - return ToOptimizedResult(result); - } - - public object Get(GetLatestMedia request) - { - var user = _userManager.GetUserById(request.UserId); - - if (!request.IsPlayed.HasValue) - { - if (user.HidePlayedInLatest) - { - request.IsPlayed = false; - } - } - - var dtoOptions = GetDtoOptions(_authContext, request); - - var list = _userViewManager.GetLatestItems(new LatestItemsQuery - { - GroupItems = request.GroupItems, - IncludeItemTypes = ApiEntryPoint.Split(request.IncludeItemTypes, ',', true), - IsPlayed = request.IsPlayed, - Limit = request.Limit, - ParentId = request.ParentId, - UserId = request.UserId, - }, dtoOptions); - - var dtos = list.Select(i => - { - var item = i.Item2[0]; - var childCount = 0; - - if (i.Item1 != null && (i.Item2.Count > 1 || i.Item1 is MusicAlbum)) - { - item = i.Item1; - childCount = i.Item2.Count; - } - - var dto = _dtoService.GetBaseItemDto(item, dtoOptions, user); - - dto.ChildCount = childCount; - - return dto; - }); - - return ToOptimizedResult(dtos.ToArray()); - } - - private BaseItemDto[] GetAsync(GetSpecialFeatures request) - { - var user = _userManager.GetUserById(request.UserId); - - var item = string.IsNullOrEmpty(request.Id) ? - _libraryManager.GetUserRootFolder() : - _libraryManager.GetItemById(request.Id); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var dtos = item - .GetExtras(BaseItem.DisplayExtraTypes) - .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)); - - return dtos.ToArray(); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetLocalTrailers request) - { - var user = _userManager.GetUserById(request.UserId); - - var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var dtosExtras = item.GetExtras(new[] { ExtraType.Trailer }) - .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) - .ToArray(); - - if (item is IHasTrailers hasTrailers) - { - var trailers = hasTrailers.GetTrailers(); - var dtosTrailers = _dtoService.GetBaseItemDtos(trailers, dtoOptions, user, item); - var allTrailers = new BaseItemDto[dtosExtras.Length + dtosTrailers.Count]; - dtosExtras.CopyTo(allTrailers, 0); - dtosTrailers.CopyTo(allTrailers, dtosExtras.Length); - return ToOptimizedResult(allTrailers); - } - - return ToOptimizedResult(dtosExtras); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public async Task<object> Get(GetItem request) - { - var user = _userManager.GetUserById(request.UserId); - - var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); - - await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); - - return ToOptimizedResult(result); - } - - private async Task RefreshItemOnDemandIfNeeded(BaseItem item) - { - if (item is Person) - { - var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview) && item.HasImage(ImageType.Primary); - var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 3; - - if (!hasMetdata) - { - var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem)) - { - MetadataRefreshMode = MetadataRefreshMode.FullRefresh, - ImageRefreshMode = MetadataRefreshMode.FullRefresh, - ForceSave = performFullRefresh - }; - - await item.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false); - } - } - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetRootFolder request) - { - var user = _userManager.GetUserById(request.UserId); - - var item = _libraryManager.GetUserRootFolder(); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var result = _dtoService.GetBaseItemDto(item, dtoOptions, user); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public async Task<object> Get(GetIntros request) - { - var user = _userManager.GetUserById(request.UserId); - - var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id); - - var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); - - var dtoOptions = GetDtoOptions(_authContext, request); - - var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray(); - - var result = new QueryResult<BaseItemDto> - { - Items = dtos, - TotalRecordCount = dtos.Length - }; - - return ToOptimizedResult(result); - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - public object Post(MarkFavoriteItem request) - { - var dto = MarkFavorite(request.UserId, request.Id, true); - - return ToOptimizedResult(dto); - } - - /// <summary> - /// Deletes the specified request. - /// </summary> - /// <param name="request">The request.</param> - public object Delete(UnmarkFavoriteItem request) - { - var dto = MarkFavorite(request.UserId, request.Id, false); - - return ToOptimizedResult(dto); - } - - /// <summary> - /// Marks the favorite. - /// </summary> - /// <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(Guid userId, Guid itemId, bool isFavorite) - { - var user = _userManager.GetUserById(userId); - - var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); - - // Get the user data for this item - var data = _userDataRepository.GetUserData(user, item); - - // Set favorite status - data.IsFavorite = isFavorite; - - _userDataRepository.SaveUserData(user, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None); - - return _userDataRepository.GetUserDataDto(item, user); - } - - /// <summary> - /// Deletes the specified request. - /// </summary> - /// <param name="request">The request.</param> - public object Delete(DeleteUserItemRating request) - { - var dto = UpdateUserItemRating(request.UserId, request.Id, null); - - return ToOptimizedResult(dto); - } - - /// <summary> - /// Posts the specified request. - /// </summary> - /// <param name="request">The request.</param> - public object Post(UpdateUserItemRating request) - { - var dto = UpdateUserItemRating(request.UserId, request.Id, request.Likes); - - return ToOptimizedResult(dto); - } - - /// <summary> - /// Updates the user item rating. - /// </summary> - /// <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(Guid userId, Guid itemId, bool? likes) - { - var user = _userManager.GetUserById(userId); - - 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, 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 deleted file mode 100644 index 0fffb06223..0000000000 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Library; -using MediaBrowser.Model.Querying; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - [Route("/Users/{UserId}/Views", "GET")] - public class GetUserViews : IReturn<QueryResult<BaseItemDto>> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - 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 = "GET")] - public bool? IncludeExternalContent { get; set; } - public bool IncludeHidden { get; set; } - - public string PresetViews { get; set; } - } - - [Route("/Users/{UserId}/GroupingOptions", "GET")] - public class GetGroupingOptions : IReturn<SpecialViewOption[]> - { - /// <summary> - /// Gets or sets the user id. - /// </summary> - /// <value>The user id.</value> - [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public Guid UserId { get; set; } - } - - public class UserViewsService : BaseApiService - { - private readonly IUserManager _userManager; - private readonly IUserViewManager _userViewManager; - private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; - private readonly ILibraryManager _libraryManager; - - public UserViewsService( - ILogger<UserViewsService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - IUserViewManager userViewManager, - IDtoService dtoService, - IAuthorizationContext authContext, - ILibraryManager libraryManager) - : base(logger, serverConfigurationManager, httpResultFactory) - { - _userManager = userManager; - _userViewManager = userViewManager; - _dtoService = dtoService; - _authContext = authContext; - _libraryManager = libraryManager; - } - - public object Get(GetUserViews request) - { - var query = new UserViewQuery - { - UserId = request.UserId - }; - - if (request.IncludeExternalContent.HasValue) - { - query.IncludeExternalContent = request.IncludeExternalContent.Value; - } - query.IncludeHidden = request.IncludeHidden; - - if (!string.IsNullOrWhiteSpace(request.PresetViews)) - { - query.PresetViews = request.PresetViews.Split(','); - } - - var app = _authContext.GetAuthorizationInfo(Request).Client ?? string.Empty; - if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1) - { - query.PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows }; - } - - 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.BasicSyncInfo); - dtoOptions.Fields = fields.ToArray(); - - var user = _userManager.GetUserById(request.UserId); - - var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)) - .ToArray(); - - var result = new QueryResult<BaseItemDto> - { - Items = dtos, - TotalRecordCount = dtos.Length - }; - - return ToOptimizedResult(result); - } - - public object Get(GetGroupingOptions request) - { - var user = _userManager.GetUserById(request.UserId); - - var list = _libraryManager.GetUserRootFolder() - .GetChildren(user, true) - .OfType<Folder>() - .Where(UserView.IsEligibleForGrouping) - .Select(i => new SpecialViewOption - { - Name = i.Name, - Id = i.Id.ToString("N", CultureInfo.InvariantCulture) - - }) - .OrderBy(i => i.Name) - .ToArray(); - - return ToOptimizedResult(list); - } - } - - class SpecialViewOption - { - public string Name { get; set; } - public string Id { get; set; } - } -} diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs deleted file mode 100644 index d023ee90ab..0000000000 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Api.UserLibrary -{ - /// <summary> - /// Class GetYears - /// </summary> - [Route("/Years", "GET", Summary = "Gets all years from a given item, folder, or the entire library")] - public class GetYears : GetItemsByName - { - } - - /// <summary> - /// Class GetYear - /// </summary> - [Route("/Years/{Year}", "GET", Summary = "Gets a year")] - public class GetYear : IReturn<BaseItemDto> - { - /// <summary> - /// Gets or sets the year. - /// </summary> - /// <value>The year.</value> - [ApiMember(Name = "Year", Description = "The year", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] - public int Year { get; set; } - - /// <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 Guid UserId { get; set; } - } - - /// <summary> - /// Class YearsService - /// </summary> - [Authenticated] - public class YearsService : BaseItemsByNameService<Year> - { - public YearsService( - ILogger<YearsService> logger, - IServerConfigurationManager serverConfigurationManager, - IHttpResultFactory httpResultFactory, - IUserManager userManager, - ILibraryManager libraryManager, - IUserDataManager userDataRepository, - IDtoService dtoService, - IAuthorizationContext authorizationContext) - : base( - logger, - serverConfigurationManager, - httpResultFactory, - userManager, - libraryManager, - userDataRepository, - dtoService, - authorizationContext) - { - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetYear request) - { - var result = GetItem(request); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the item. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>Task{BaseItemDto}.</returns> - private BaseItemDto GetItem(GetYear request) - { - var item = LibraryManager.GetYear(request.Year); - - var dtoOptions = GetDtoOptions(AuthorizationContext, request); - - if (!request.UserId.Equals(Guid.Empty)) - { - var user = UserManager.GetUserById(request.UserId); - - return DtoService.GetBaseItemDto(item, dtoOptions, user); - } - - return DtoService.GetBaseItemDto(item, dtoOptions); - } - - /// <summary> - /// Gets the specified request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>System.Object.</returns> - public object Get(GetYears request) - { - var result = GetResult(request); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets all items. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="items">The items.</param> - /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns> - protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items) - { - return items - .Select(i => i.ProductionYear ?? 0) - .Where(i => i > 0) - .Distinct() - .Select(year => LibraryManager.GetYear(year)); - } - } -} |
