Merge remote-tracking branch 'upstream/api-migration' into api-image-service

This commit is contained in:
crobibero
2020-07-21 08:03:09 -06:00
764 changed files with 16382 additions and 10607 deletions
+2 -2
View File
@@ -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>
+1 -4
View File
@@ -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;
-341
View File
@@ -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);
}
}
}
-104
View File
@@ -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);
}
}
}
}
-285
View File
@@ -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;
}
}
}
+1
View File
@@ -3,6 +3,7 @@ namespace MediaBrowser.Api
public interface IHasDtoOptions : IHasItemFields
{
bool? EnableImages { get; set; }
bool? EnableUserData { get; set; }
int? ImageTypeLimit { get; set; }
+1 -2
View File
@@ -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();
}
}
+6 -6
View File
@@ -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
{
+8 -4
View File
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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;
}
}
}
}
}
}
-4
View File
@@ -14,10 +14,6 @@
<Compile Include="..\SharedVersion.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="Library" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
-418
View File
@@ -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();
}
}
}
@@ -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);
}
}
}
-197
View File
@@ -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;
}
}
}
@@ -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)
@@ -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;
}
@@ -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))
@@ -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]
@@ -22,7 +22,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
/// <summary>
/// Class VideoHlsService
/// Class VideoHlsService.
/// </summary>
[Authenticated]
public class VideoHlsService : BaseHlsService
@@ -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();
}
@@ -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]
@@ -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);
@@ -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
@@ -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]
@@ -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>();
+5 -1
View File
@@ -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; }
}
@@ -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
@@ -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();
}
}
}
@@ -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>
{
@@ -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;
-223
View File
@@ -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;
}
}
}
+32 -72
View File
@@ -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);
@@ -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;
+8
View File
@@ -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)
@@ -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();
}
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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();
}
}
}
@@ -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);
}
}
}
@@ -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();
}
}
}
@@ -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()
};
}
}
}
@@ -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);
}
}
}
@@ -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();
}
}
}
@@ -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);
}
}
}
@@ -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; }
}
}
@@ -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));
}
}
}