changes from merge

This commit is contained in:
crobibero 2020-07-20 14:43:54 -06:00
parent 1385064497
commit 5c66f9e471
56 changed files with 43 additions and 12822 deletions

View file

@ -1,5 +1,4 @@
using System.Threading.Tasks;
using Jellyfin.Api.Auth.DefaultAuthorizationPolicy;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Library;
using Microsoft.AspNetCore.Authorization;

View file

@ -1,5 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Data.Entities;
@ -35,6 +34,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="minDate">Optional. The minimum date. Format = ISO.</param>
/// <param name="hasUserId">Optional. Filter log entries if it has user id, or not.</param>
/// <response code="200">Activity log returned.</response>
/// <returns>A <see cref="QueryResult{ActivityLogEntry}"/> containing the log entries.</returns>
[HttpGet("Entries")]
@ -42,10 +42,14 @@ namespace Jellyfin.Api.Controllers
public ActionResult<QueryResult<ActivityLogEntry>> GetLogEntries(
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] DateTime? minDate)
[FromQuery] DateTime? minDate,
[FromQuery] bool? hasUserId)
{
var filterFunc = new Func<IQueryable<ActivityLog>, IQueryable<ActivityLog>>(
entries => entries.Where(entry => entry.DateCreated >= minDate));
entries => entries.Where(entry => entry.DateCreated >= minDate
&& (!hasUserId.HasValue || (hasUserId.Value
? entry.UserId != Guid.Empty
: entry.UserId == Guid.Empty))));
return _activityManager.GetPagedResult(filterFunc, startIndex, limit);
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Jellyfin.Api.Models;

View file

@ -1,5 +1,4 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Jellyfin.Api.Constants;
using MediaBrowser.Controller.Dto;

View file

@ -1,6 +1,5 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Jellyfin.Api.Constants;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using Jellyfin.Api.Constants;

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Jellyfin.Api.Models.NotificationDtos;

View file

@ -5,6 +5,7 @@ using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Updates;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@ -20,14 +21,17 @@ namespace Jellyfin.Api.Controllers
public class PackageController : BaseJellyfinApiController
{
private readonly IInstallationManager _installationManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
/// <summary>
/// Initializes a new instance of the <see cref="PackageController"/> class.
/// </summary>
/// <param name="installationManager">Instance of <see cref="IInstallationManager"/>Installation Manager.</param>
public PackageController(IInstallationManager installationManager)
/// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
/// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
public PackageController(IInstallationManager installationManager, IServerConfigurationManager serverConfigurationManager)
{
_installationManager = installationManager;
_serverConfigurationManager = serverConfigurationManager;
}
/// <summary>
@ -110,11 +114,39 @@ namespace Jellyfin.Api.Controllers
[HttpDelete("/Installing/{packageId}")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public IActionResult CancelPackageInstallation(
public ActionResult CancelPackageInstallation(
[FromRoute] [Required] Guid packageId)
{
_installationManager.CancelInstallation(packageId);
return NoContent();
}
/// <summary>
/// Gets all package repositories.
/// </summary>
/// <response code="200">Package repositories returned.</response>
/// <returns>An <see cref="OkResult"/> containing the list of package repositories.</returns>
[HttpGet("/Repositories")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<RepositoryInfo>> GetRepositories()
{
return _serverConfigurationManager.Configuration.PluginRepositories;
}
/// <summary>
/// Sets the enabled and existing package repositories.
/// </summary>
/// <param name="repositoryInfos">The list of package repositories.</param>
/// <response code="204">Package repositories saved.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpOptions("/Repositories")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SetRepositories([FromBody] List<RepositoryInfo> repositoryInfos)
{
_serverConfigurationManager.Configuration.PluginRepositories = repositoryInfos;
return NoContent();
}
}
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using Jellyfin.Api.Constants;

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Api.Constants;
@ -137,14 +136,8 @@ namespace Jellyfin.Api.Controllers
public ActionResult DeleteUser([FromRoute] Guid userId)
{
var user = _userManager.GetUserById(userId);
if (user == null)
{
return NotFound("User not found");
}
_sessionManager.RevokeUserTokens(user.Id, null);
_userManager.DeleteUser(user);
_userManager.DeleteUser(userId);
return NoContent();
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;

View file

@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dto;
using Microsoft.Extensions.Logging;

View file

@ -1,340 +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);
}
}
}

View file

@ -1,144 +0,0 @@
using System.IO;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
/// Class GetConfiguration.
/// </summary>
[Route("/System/Configuration", "GET", Summary = "Gets application configuration")]
[Authenticated]
public class GetConfiguration : IReturn<ServerConfiguration>
{
}
[Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")]
[Authenticated(AllowBeforeStartupWizard = true)]
public class GetNamedConfiguration
{
[ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Key { get; set; }
}
/// <summary>
/// Class UpdateConfiguration.
/// </summary>
[Route("/System/Configuration", "POST", Summary = "Updates application configuration")]
[Authenticated(Roles = "Admin")]
public class UpdateConfiguration : ServerConfiguration, IReturnVoid
{
}
[Route("/System/Configuration/{Key}", "POST", Summary = "Updates named configuration")]
[Authenticated(Roles = "Admin")]
public class UpdateNamedConfiguration : IReturnVoid, IRequiresRequestStream
{
[ApiMember(Name = "Key", Description = "Key", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Key { get; set; }
public Stream RequestStream { get; set; }
}
[Route("/System/Configuration/MetadataOptions/Default", "GET", Summary = "Gets a default MetadataOptions object")]
[Authenticated(Roles = "Admin")]
public class GetDefaultMetadataOptions : IReturn<MetadataOptions>
{
}
[Route("/System/MediaEncoder/Path", "POST", Summary = "Updates the path to the media encoder")]
[Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)]
public class UpdateMediaEncoderPath : IReturnVoid
{
[ApiMember(Name = "Path", Description = "Path", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Path { get; set; }
[ApiMember(Name = "PathType", Description = "PathType", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string PathType { get; set; }
}
public class ConfigurationService : BaseApiService
{
/// <summary>
/// The _json serializer.
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
/// The _configuration manager.
/// </summary>
private readonly IServerConfigurationManager _configurationManager;
private readonly IMediaEncoder _mediaEncoder;
public ConfigurationService(
ILogger<ConfigurationService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IJsonSerializer jsonSerializer,
IServerConfigurationManager configurationManager,
IMediaEncoder mediaEncoder)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_jsonSerializer = jsonSerializer;
_configurationManager = configurationManager;
_mediaEncoder = mediaEncoder;
}
public void Post(UpdateMediaEncoderPath request)
{
_mediaEncoder.UpdateEncoderPath(request.Path, request.PathType);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetConfiguration request)
{
return ToOptimizedResult(_configurationManager.Configuration);
}
public object Get(GetNamedConfiguration request)
{
var result = _configurationManager.GetConfiguration(request.Key);
return ToOptimizedResult(result);
}
/// <summary>
/// Posts the specified configuraiton.
/// </summary>
/// <param name="request">The request.</param>
public void Post(UpdateConfiguration request)
{
// Silly, but we need to serialize and deserialize or the XmlSerializer will write the xml with an element name of UpdateConfiguration
var json = _jsonSerializer.SerializeToString(request);
var config = _jsonSerializer.DeserializeFromString<ServerConfiguration>(json);
_configurationManager.ReplaceConfiguration(config);
}
public async Task Post(UpdateNamedConfiguration request)
{
var key = GetPathValue(2).ToString();
var configurationType = _configurationManager.GetConfigurationType(key);
var configuration = await _jsonSerializer.DeserializeFromStreamAsync(request.RequestStream, configurationType).ConfigureAwait(false);
_configurationManager.SaveConfiguration(key, configuration);
}
public object Get(GetDefaultMetadataOptions request)
{
return ToOptimizedResult(new MetadataOptions());
}
}
}

View file

@ -1,103 +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);
}
}
}
}

View file

@ -1,101 +0,0 @@
using System.Threading;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
/// Class UpdateDisplayPreferences.
/// </summary>
[Route("/DisplayPreferences/{DisplayPreferencesId}", "POST", Summary = "Updates a user's display preferences for an item")]
public class UpdateDisplayPreferences : DisplayPreferences, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "DisplayPreferencesId", Description = "DisplayPreferences Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string DisplayPreferencesId { get; set; }
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string UserId { get; set; }
}
[Route("/DisplayPreferences/{Id}", "GET", Summary = "Gets a user's display preferences for an item")]
public class GetDisplayPreferences : IReturn<DisplayPreferences>
{
/// <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; }
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UserId { get; set; }
[ApiMember(Name = "Client", Description = "Client", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Client { get; set; }
}
/// <summary>
/// Class DisplayPreferencesService.
/// </summary>
[Authenticated]
public class DisplayPreferencesService : BaseApiService
{
/// <summary>
/// The _display preferences manager.
/// </summary>
private readonly IDisplayPreferencesRepository _displayPreferencesManager;
/// <summary>
/// The _json serializer.
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
/// Initializes a new instance of the <see cref="DisplayPreferencesService" /> class.
/// </summary>
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="displayPreferencesManager">The display preferences manager.</param>
public DisplayPreferencesService(
ILogger<DisplayPreferencesService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IJsonSerializer jsonSerializer,
IDisplayPreferencesRepository displayPreferencesManager)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_jsonSerializer = jsonSerializer;
_displayPreferencesManager = displayPreferencesManager;
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
public object Get(GetDisplayPreferences request)
{
var result = _displayPreferencesManager.GetDisplayPreferences(request.Id, request.UserId, request.Client);
return ToOptimizedResult(result);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(UpdateDisplayPreferences request)
{
// Serialize to json and then back so that the core doesn't see the request dto type
var displayPreferences = _jsonSerializer.DeserializeFromString<DisplayPreferences>(_jsonSerializer.SerializeToString(request));
_displayPreferencesManager.SaveDisplayPreferences(displayPreferences, request.UserId, request.Client, CancellationToken.None);
}
}
}

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;
}
}
}

View file

@ -1,248 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
[Route("/Items/Filters", "GET", Summary = "Gets branding configuration")]
public class GetQueryFiltersLegacy : IReturn<QueryFiltersLegacy>
{
/// <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; }
[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 = "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 = "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; }
public string[] GetMediaTypes()
{
return (MediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
public string[] GetIncludeItemTypes()
{
return (IncludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
}
[Route("/Items/Filters2", "GET", Summary = "Gets branding configuration")]
public class GetQueryFilters : IReturn<QueryFilters>
{
/// <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; }
[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 = "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 = "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; }
public string[] GetMediaTypes()
{
return (MediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
public string[] GetIncludeItemTypes()
{
return (IncludeItemTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
public bool? IsAiring { get; set; }
public bool? IsMovie { get; set; }
public bool? IsSports { get; set; }
public bool? IsKids { get; set; }
public bool? IsNews { get; set; }
public bool? IsSeries { get; set; }
public bool? Recursive { get; set; }
}
[Authenticated]
public class FilterService : BaseApiService
{
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
public FilterService(
ILogger<FilterService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
ILibraryManager libraryManager,
IUserManager userManager)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_libraryManager = libraryManager;
_userManager = userManager;
}
public object Get(GetQueryFilters request)
{
var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId);
var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, typeof(Trailer).Name, StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "Program", StringComparison.OrdinalIgnoreCase))
{
parentItem = null;
}
var filters = new QueryFilters();
var genreQuery = new InternalItemsQuery(user)
{
IncludeItemTypes = request.GetIncludeItemTypes(),
DtoOptions = new Controller.Dto.DtoOptions
{
Fields = Array.Empty<ItemFields>(),
EnableImages = false,
EnableUserData = false
},
IsAiring = request.IsAiring,
IsMovie = request.IsMovie,
IsSports = request.IsSports,
IsKids = request.IsKids,
IsNews = request.IsNews,
IsSeries = request.IsSeries
};
// Non recursive not yet supported for library folders
if ((request.Recursive ?? true) || parentItem is UserView || parentItem is ICollectionFolder)
{
genreQuery.AncestorIds = parentItem == null ? Array.Empty<Guid>() : new[] { parentItem.Id };
}
else
{
genreQuery.Parent = parentItem;
}
if (string.Equals(request.IncludeItemTypes, "MusicAlbum", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "MusicVideo", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "MusicArtist", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "Audio", StringComparison.OrdinalIgnoreCase))
{
filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameGuidPair
{
Name = i.Item1.Name,
Id = i.Item1.Id
}).ToArray();
}
else
{
filters.Genres = _libraryManager.GetGenres(genreQuery).Items.Select(i => new NameGuidPair
{
Name = i.Item1.Name,
Id = i.Item1.Id
}).ToArray();
}
return ToOptimizedResult(filters);
}
public object Get(GetQueryFiltersLegacy request)
{
var parentItem = string.IsNullOrEmpty(request.ParentId) ? null : _libraryManager.GetItemById(request.ParentId);
var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
if (string.Equals(request.IncludeItemTypes, "BoxSet", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, typeof(Trailer).Name, StringComparison.OrdinalIgnoreCase) ||
string.Equals(request.IncludeItemTypes, "Program", StringComparison.OrdinalIgnoreCase))
{
parentItem = null;
}
var item = string.IsNullOrEmpty(request.ParentId) ?
user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder() :
parentItem;
var result = ((Folder)item).GetItemList(GetItemsQuery(request, user));
var filters = GetFilters(result);
return ToOptimizedResult(filters);
}
private QueryFiltersLegacy GetFilters(IReadOnlyCollection<BaseItem> items)
{
var result = new QueryFiltersLegacy();
result.Years = items.Select(i => i.ProductionYear ?? -1)
.Where(i => i > 0)
.Distinct()
.OrderBy(i => i)
.ToArray();
result.Genres = items.SelectMany(i => i.Genres)
.DistinctNames()
.OrderBy(i => i)
.ToArray();
result.Tags = items
.SelectMany(i => i.Tags)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(i => i)
.ToArray();
result.OfficialRatings = items
.Select(i => i.OfficialRating)
.Where(i => !string.IsNullOrWhiteSpace(i))
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(i => i)
.ToArray();
return result;
}
private InternalItemsQuery GetItemsQuery(GetQueryFiltersLegacy request, User user)
{
var query = new InternalItemsQuery
{
User = user,
MediaTypes = request.GetMediaTypes(),
IncludeItemTypes = request.GetIncludeItemTypes(),
Recursive = true,
EnableTotalRecordCount = false,
DtoOptions = new Controller.Dto.DtoOptions
{
Fields = new[] { ItemFields.Genres, ItemFields.Tags },
EnableImages = false,
EnableUserData = false
}
};
return query;
}
}
}

View file

@ -1,277 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Images
{
/// <summary>
/// Class GetGeneralImage.
/// </summary>
[Route("/Images/General/{Name}/{Type}", "GET", Summary = "Gets a general image by name")]
public class GetGeneralImage
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "Name", Description = "The name of the image", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Name { get; set; }
[ApiMember(Name = "Type", Description = "Image Type (primary, backdrop, logo, etc).", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Type { get; set; }
}
/// <summary>
/// Class GetRatingImage.
/// </summary>
[Route("/Images/Ratings/{Theme}/{Name}", "GET", Summary = "Gets a rating image by name")]
public class GetRatingImage
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "Name", Description = "The name of the image", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the theme.
/// </summary>
/// <value>The theme.</value>
[ApiMember(Name = "Theme", Description = "The theme to get the image from", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Theme { get; set; }
}
/// <summary>
/// Class GetMediaInfoImage.
/// </summary>
[Route("/Images/MediaInfo/{Theme}/{Name}", "GET", Summary = "Gets a media info image by name")]
public class GetMediaInfoImage
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "Name", Description = "The name of the image", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the theme.
/// </summary>
/// <value>The theme.</value>
[ApiMember(Name = "Theme", Description = "The theme to get the image from", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Theme { get; set; }
}
[Route("/Images/MediaInfo", "GET", Summary = "Gets all media info image by name")]
[Authenticated]
public class GetMediaInfoImages : IReturn<List<ImageByNameInfo>>
{
}
[Route("/Images/Ratings", "GET", Summary = "Gets all rating images by name")]
[Authenticated]
public class GetRatingImages : IReturn<List<ImageByNameInfo>>
{
}
[Route("/Images/General", "GET", Summary = "Gets all general images by name")]
[Authenticated]
public class GetGeneralImages : IReturn<List<ImageByNameInfo>>
{
}
/// <summary>
/// Class ImageByNameService.
/// </summary>
public class ImageByNameService : BaseApiService
{
/// <summary>
/// The _app paths.
/// </summary>
private readonly IServerApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="ImageByNameService" /> class.
/// </summary>
public ImageByNameService(
ILogger<ImageByNameService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory resultFactory,
IFileSystem fileSystem)
: base(logger, serverConfigurationManager, resultFactory)
{
_appPaths = serverConfigurationManager.ApplicationPaths;
_fileSystem = fileSystem;
}
public object Get(GetMediaInfoImages request)
{
return ToOptimizedResult(GetImageList(_appPaths.MediaInfoImagesPath, true));
}
public object Get(GetRatingImages request)
{
return ToOptimizedResult(GetImageList(_appPaths.RatingsPath, true));
}
public object Get(GetGeneralImages request)
{
return ToOptimizedResult(GetImageList(_appPaths.GeneralPath, false));
}
private List<ImageByNameInfo> GetImageList(string path, bool supportsThemes)
{
try
{
return _fileSystem.GetFiles(path, BaseItem.SupportedImageExtensions, false, true)
.Select(i => new ImageByNameInfo
{
Name = _fileSystem.GetFileNameWithoutExtension(i),
FileLength = i.Length,
// For themeable images, use the Theme property
// For general images, the same object structure is fine,
// but it's not owned by a theme, so call it Context
Theme = supportsThemes ? GetThemeName(i.FullName, path) : null,
Context = supportsThemes ? null : GetThemeName(i.FullName, path),
Format = i.Extension.ToLowerInvariant().TrimStart('.')
})
.OrderBy(i => i.Name)
.ToList();
}
catch (IOException)
{
return new List<ImageByNameInfo>();
}
}
private string GetThemeName(string path, string rootImagePath)
{
var parentName = Path.GetDirectoryName(path);
if (string.Equals(parentName, rootImagePath, StringComparison.OrdinalIgnoreCase))
{
return null;
}
parentName = Path.GetFileName(parentName);
return string.Equals(parentName, "all", StringComparison.OrdinalIgnoreCase) ?
null :
parentName;
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public Task<object> Get(GetGeneralImage request)
{
var filename = string.Equals(request.Type, "primary", StringComparison.OrdinalIgnoreCase)
? "folder"
: request.Type;
var paths = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(_appPaths.GeneralPath, request.Name, filename + i)).ToList();
var path = paths.FirstOrDefault(File.Exists) ?? paths.FirstOrDefault();
return ResultFactory.GetStaticFileResult(Request, path);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetRatingImage request)
{
var themeFolder = Path.Combine(_appPaths.RatingsPath, request.Theme);
if (Directory.Exists(themeFolder))
{
var path = BaseItem.SupportedImageExtensions
.Select(i => Path.Combine(themeFolder, request.Name + i))
.FirstOrDefault(File.Exists);
if (!string.IsNullOrEmpty(path))
{
return ResultFactory.GetStaticFileResult(Request, path);
}
}
var allFolder = Path.Combine(_appPaths.RatingsPath, "all");
if (Directory.Exists(allFolder))
{
// Avoid implicitly captured closure
var currentRequest = request;
var path = BaseItem.SupportedImageExtensions
.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
.FirstOrDefault(File.Exists);
if (!string.IsNullOrEmpty(path))
{
return ResultFactory.GetStaticFileResult(Request, path);
}
}
throw new ResourceNotFoundException("MediaInfo image not found: " + request.Name);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public Task<object> Get(GetMediaInfoImage request)
{
var themeFolder = Path.Combine(_appPaths.MediaInfoImagesPath, request.Theme);
if (Directory.Exists(themeFolder))
{
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i))
.FirstOrDefault(File.Exists);
if (!string.IsNullOrEmpty(path))
{
return ResultFactory.GetStaticFileResult(Request, path);
}
}
var allFolder = Path.Combine(_appPaths.MediaInfoImagesPath, "all");
if (Directory.Exists(allFolder))
{
// Avoid implicitly captured closure
var currentRequest = request;
var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i))
.FirstOrDefault(File.Exists);
if (!string.IsNullOrEmpty(path))
{
return ResultFactory.GetStaticFileResult(Request, path);
}
}
throw new ResourceNotFoundException("MediaInfo image not found: " + request.Name);
}
}
}

View file

@ -1,296 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Images
{
public class BaseRemoteImageRequest : IReturn<RemoteImageResult>
{
[ApiMember(Name = "Type", Description = "The image type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public ImageType? Type { 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 = "ProviderName", Description = "Optional. The image provider to use", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ProviderName { get; set; }
[ApiMember(Name = "IncludeAllLanguages", Description = "Optional.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeAllLanguages { get; set; }
}
[Route("/Items/{Id}/RemoteImages", "GET", Summary = "Gets available remote images for an item")]
[Authenticated]
public class GetRemoteImages : BaseRemoteImageRequest
{
/// <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; }
}
[Route("/Items/{Id}/RemoteImages/Providers", "GET", Summary = "Gets available remote image providers for an item")]
[Authenticated]
public class GetRemoteImageProviders : IReturn<List<ImageProviderInfo>>
{
/// <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 class BaseDownloadRemoteImage : IReturnVoid
{
[ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public ImageType Type { get; set; }
[ApiMember(Name = "ProviderName", Description = "The image provider", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string ProviderName { get; set; }
[ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
public string ImageUrl { get; set; }
}
[Route("/Items/{Id}/RemoteImages/Download", "POST", Summary = "Downloads a remote image for an item")]
[Authenticated(Roles = "Admin")]
public class DownloadRemoteImage : BaseDownloadRemoteImage
{
/// <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; }
}
[Route("/Images/Remote", "GET", Summary = "Gets a remote image")]
public class GetRemoteImage
{
[ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ImageUrl { get; set; }
}
public class RemoteImageService : BaseApiService
{
private readonly IProviderManager _providerManager;
private readonly IServerApplicationPaths _appPaths;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
public RemoteImageService(
ILogger<RemoteImageService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IProviderManager providerManager,
IServerApplicationPaths appPaths,
IHttpClient httpClient,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_providerManager = providerManager;
_appPaths = appPaths;
_httpClient = httpClient;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
}
public object Get(GetRemoteImageProviders request)
{
var item = _libraryManager.GetItemById(request.Id);
var result = GetImageProviders(item);
return ToOptimizedResult(result);
}
private List<ImageProviderInfo> GetImageProviders(BaseItem item)
{
return _providerManager.GetRemoteImageProviderInfo(item).ToList();
}
public async Task<object> Get(GetRemoteImages request)
{
var item = _libraryManager.GetItemById(request.Id);
var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery(request.ProviderName)
{
IncludeAllLanguages = request.IncludeAllLanguages,
IncludeDisabledProviders = true,
ImageType = request.Type
}, CancellationToken.None).ConfigureAwait(false);
var imagesList = images.ToArray();
var allProviders = _providerManager.GetRemoteImageProviderInfo(item);
if (request.Type.HasValue)
{
allProviders = allProviders.Where(i => i.SupportedImages.Contains(request.Type.Value));
}
var result = new RemoteImageResult
{
TotalRecordCount = imagesList.Length,
Providers = allProviders.Select(i => i.Name)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray()
};
if (request.StartIndex.HasValue)
{
imagesList = imagesList.Skip(request.StartIndex.Value)
.ToArray();
}
if (request.Limit.HasValue)
{
imagesList = imagesList.Take(request.Limit.Value)
.ToArray();
}
result.Images = imagesList;
return result;
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Post(DownloadRemoteImage request)
{
var item = _libraryManager.GetItemById(request.Id);
return DownloadRemoteImage(item, request);
}
/// <summary>
/// Downloads the remote image.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="request">The request.</param>
/// <returns>Task.</returns>
private async Task DownloadRemoteImage(BaseItem item, BaseDownloadRemoteImage request)
{
await _providerManager.SaveImage(item, request.ImageUrl, request.Type, null, CancellationToken.None).ConfigureAwait(false);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public async Task<object> Get(GetRemoteImage request)
{
var urlHash = request.ImageUrl.GetMD5();
var pointerCachePath = GetFullCachePath(urlHash.ToString());
string contentPath;
try
{
contentPath = File.ReadAllText(pointerCachePath);
if (File.Exists(contentPath))
{
return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false);
}
}
catch (FileNotFoundException)
{
// Means the file isn't cached yet
}
catch (IOException)
{
// Means the file isn't cached yet
}
await DownloadImage(request.ImageUrl, urlHash, pointerCachePath).ConfigureAwait(false);
// Read the pointer file again
contentPath = File.ReadAllText(pointerCachePath);
return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false);
}
/// <summary>
/// Downloads the image.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="urlHash">The URL hash.</param>
/// <param name="pointerCachePath">The pointer cache path.</param>
/// <returns>Task.</returns>
private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath)
{
using var result = await _httpClient.GetResponse(new HttpRequestOptions
{
Url = url,
BufferContent = false
}).ConfigureAwait(false);
var ext = result.ContentType.Split('/')[^1];
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
var stream = result.Content;
await using (stream.ConfigureAwait(false))
{
var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
await using (filestream.ConfigureAwait(false))
{
await stream.CopyToAsync(filestream).ConfigureAwait(false);
}
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
File.WriteAllText(pointerCachePath, fullCachePath);
}
/// <summary>
/// Gets the full cache path.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>System.String.</returns>
private string GetFullCachePath(string filename)
{
return Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename);
}
}
}

View file

@ -1,336 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
[Route("/Items/{Id}/ExternalIdInfos", "GET", Summary = "Gets external id infos for an item")]
[Authenticated(Roles = "Admin")]
public class GetExternalIdInfos : IReturn<List<ExternalIdInfo>>
{
/// <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 Guid Id { get; set; }
}
[Route("/Items/RemoteSearch/Movie", "POST")]
[Authenticated]
public class GetMovieRemoteSearchResults : RemoteSearchQuery<MovieInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/Trailer", "POST")]
[Authenticated]
public class GetTrailerRemoteSearchResults : RemoteSearchQuery<TrailerInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/MusicVideo", "POST")]
[Authenticated]
public class GetMusicVideoRemoteSearchResults : RemoteSearchQuery<MusicVideoInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/Series", "POST")]
[Authenticated]
public class GetSeriesRemoteSearchResults : RemoteSearchQuery<SeriesInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/BoxSet", "POST")]
[Authenticated]
public class GetBoxSetRemoteSearchResults : RemoteSearchQuery<BoxSetInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/MusicArtist", "POST")]
[Authenticated]
public class GetMusicArtistRemoteSearchResults : RemoteSearchQuery<ArtistInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/MusicAlbum", "POST")]
[Authenticated]
public class GetMusicAlbumRemoteSearchResults : RemoteSearchQuery<AlbumInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/Person", "POST")]
[Authenticated(Roles = "Admin")]
public class GetPersonRemoteSearchResults : RemoteSearchQuery<PersonLookupInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/Book", "POST")]
[Authenticated]
public class GetBookRemoteSearchResults : RemoteSearchQuery<BookInfo>, IReturn<List<RemoteSearchResult>>
{
}
[Route("/Items/RemoteSearch/Image", "GET", Summary = "Gets a remote image")]
public class GetRemoteSearchImage
{
[ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ImageUrl { get; set; }
[ApiMember(Name = "ProviderName", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ProviderName { get; set; }
}
[Route("/Items/RemoteSearch/Apply/{Id}", "POST", Summary = "Applies search criteria to an item and refreshes metadata")]
[Authenticated(Roles = "Admin")]
public class ApplySearchCriteria : RemoteSearchResult, IReturnVoid
{
[ApiMember(Name = "Id", Description = "The item id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Id { get; set; }
[ApiMember(Name = "ReplaceAllImages", Description = "Whether or not to replace all images", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool ReplaceAllImages { get; set; }
public ApplySearchCriteria()
{
ReplaceAllImages = true;
}
}
public class ItemLookupService : BaseApiService
{
private readonly IProviderManager _providerManager;
private readonly IServerApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly ILibraryManager _libraryManager;
private readonly IJsonSerializer _json;
public ItemLookupService(
ILogger<ItemLookupService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager,
IJsonSerializer json)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_providerManager = providerManager;
_appPaths = serverConfigurationManager.ApplicationPaths;
_fileSystem = fileSystem;
_libraryManager = libraryManager;
_json = json;
}
public object Get(GetExternalIdInfos request)
{
var item = _libraryManager.GetItemById(request.Id);
var infos = _providerManager.GetExternalIdInfos(item).ToList();
return ToOptimizedResult(infos);
}
public async Task<object> Post(GetTrailerRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<Trailer, TrailerInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetBookRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<Book, BookInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetMovieRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<Movie, MovieInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetSeriesRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<Series, SeriesInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetBoxSetRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<BoxSet, BoxSetInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetMusicVideoRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<MusicVideo, MusicVideoInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetPersonRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<Person, PersonLookupInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetMusicAlbumRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<MusicAlbum, AlbumInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Post(GetMusicArtistRemoteSearchResults request)
{
var result = await _providerManager.GetRemoteSearchResults<MusicArtist, ArtistInfo>(request, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public Task<object> Get(GetRemoteSearchImage request)
{
return GetRemoteImage(request);
}
public Task Post(ApplySearchCriteria request)
{
var item = _libraryManager.GetItemById(new Guid(request.Id));
// foreach (var key in request.ProviderIds)
//{
// var value = key.Value;
// if (!string.IsNullOrWhiteSpace(value))
// {
// item.SetProviderId(key.Key, value);
// }
//}
Logger.LogInformation("Setting provider id's to item {0}-{1}: {2}", item.Id, item.Name, _json.SerializeToString(request.ProviderIds));
// Since the refresh process won't erase provider Ids, we need to set this explicitly now.
item.ProviderIds = request.ProviderIds;
// item.ProductionYear = request.ProductionYear;
// item.Name = request.Name;
return _providerManager.RefreshFullItem(
item,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllMetadata = true,
ReplaceAllImages = request.ReplaceAllImages,
SearchResult = request
},
CancellationToken.None);
}
/// <summary>
/// Gets the remote image.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task{System.Object}.</returns>
private async Task<object> GetRemoteImage(GetRemoteSearchImage request)
{
var urlHash = request.ImageUrl.GetMD5();
var pointerCachePath = GetFullCachePath(urlHash.ToString());
string contentPath;
try
{
contentPath = File.ReadAllText(pointerCachePath);
if (File.Exists(contentPath))
{
return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false);
}
}
catch (FileNotFoundException)
{
// Means the file isn't cached yet
}
catch (IOException)
{
// Means the file isn't cached yet
}
await DownloadImage(request.ProviderName, request.ImageUrl, urlHash, pointerCachePath).ConfigureAwait(false);
// Read the pointer file again
contentPath = File.ReadAllText(pointerCachePath);
return await ResultFactory.GetStaticFileResult(Request, contentPath).ConfigureAwait(false);
}
/// <summary>
/// Downloads the image.
/// </summary>
/// <param name="providerName">Name of the provider.</param>
/// <param name="url">The URL.</param>
/// <param name="urlHash">The URL hash.</param>
/// <param name="pointerCachePath">The pointer cache path.</param>
/// <returns>Task.</returns>
private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath)
{
var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false);
var ext = result.ContentType.Split('/')[^1];
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
var stream = result.Content;
await using (stream.ConfigureAwait(false))
{
var fileStream = new FileStream(
fullCachePath,
FileMode.Create,
FileAccess.Write,
FileShare.Read,
IODefaults.FileStreamBufferSize,
true);
await using (fileStream.ConfigureAwait(false))
{
await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
File.WriteAllText(pointerCachePath, fullCachePath);
}
/// <summary>
/// Gets the full cache path.
/// </summary>
/// <param name="filename">The filename.</param>
/// <returns>System.String.</returns>
private string GetFullCachePath(string filename)
=> Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,412 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Library
{
/// <summary>
/// Class GetDefaultVirtualFolders.
/// </summary>
[Route("/Library/VirtualFolders", "GET")]
public class GetVirtualFolders : IReturn<List<VirtualFolderInfo>>
{
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
public string UserId { get; set; }
}
[Route("/Library/VirtualFolders", "POST")]
public class AddVirtualFolder : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the type of the collection.
/// </summary>
/// <value>The type of the collection.</value>
public string CollectionType { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [refresh library].
/// </summary>
/// <value><c>true</c> if [refresh library]; otherwise, <c>false</c>.</value>
public bool RefreshLibrary { get; set; }
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
public string[] Paths { get; set; }
public LibraryOptions LibraryOptions { get; set; }
}
[Route("/Library/VirtualFolders", "DELETE")]
public class RemoveVirtualFolder : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [refresh library].
/// </summary>
/// <value><c>true</c> if [refresh library]; otherwise, <c>false</c>.</value>
public bool RefreshLibrary { get; set; }
}
[Route("/Library/VirtualFolders/Name", "POST")]
public class RenameVirtualFolder : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string NewName { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [refresh library].
/// </summary>
/// <value><c>true</c> if [refresh library]; otherwise, <c>false</c>.</value>
public bool RefreshLibrary { get; set; }
}
[Route("/Library/VirtualFolders/Paths", "POST")]
public class AddMediaPath : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Path { get; set; }
public MediaPathInfo PathInfo { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [refresh library].
/// </summary>
/// <value><c>true</c> if [refresh library]; otherwise, <c>false</c>.</value>
public bool RefreshLibrary { get; set; }
}
[Route("/Library/VirtualFolders/Paths/Update", "POST")]
public class UpdateMediaPath : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
public MediaPathInfo PathInfo { get; set; }
}
[Route("/Library/VirtualFolders/Paths", "DELETE")]
public class RemoveMediaPath : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [refresh library].
/// </summary>
/// <value><c>true</c> if [refresh library]; otherwise, <c>false</c>.</value>
public bool RefreshLibrary { get; set; }
}
[Route("/Library/VirtualFolders/LibraryOptions", "POST")]
public class UpdateLibraryOptions : IReturnVoid
{
public string Id { get; set; }
public LibraryOptions LibraryOptions { get; set; }
}
/// <summary>
/// Class LibraryStructureService.
/// </summary>
[Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)]
public class LibraryStructureService : BaseApiService
{
/// <summary>
/// The _app paths.
/// </summary>
private readonly IServerApplicationPaths _appPaths;
/// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILibraryMonitor _libraryMonitor;
/// <summary>
/// Initializes a new instance of the <see cref="LibraryStructureService" /> class.
/// </summary>
public LibraryStructureService(
ILogger<LibraryStructureService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
ILibraryManager libraryManager,
ILibraryMonitor libraryMonitor)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_appPaths = serverConfigurationManager.ApplicationPaths;
_libraryManager = libraryManager;
_libraryMonitor = libraryMonitor;
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetVirtualFolders request)
{
var result = _libraryManager.GetVirtualFolders(true);
return ToOptimizedResult(result);
}
public void Post(UpdateLibraryOptions request)
{
var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(request.Id);
collectionFolder.UpdateLibraryOptions(request.LibraryOptions);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Post(AddVirtualFolder request)
{
var libraryOptions = request.LibraryOptions ?? new LibraryOptions();
if (request.Paths != null && request.Paths.Length > 0)
{
libraryOptions.PathInfos = request.Paths.Select(i => new MediaPathInfo { Path = i }).ToArray();
}
return _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, libraryOptions, request.RefreshLibrary);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(RenameVirtualFolder request)
{
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new ArgumentNullException(nameof(request));
}
if (string.IsNullOrWhiteSpace(request.NewName))
{
throw new ArgumentNullException(nameof(request));
}
var rootFolderPath = _appPaths.DefaultUserViewsPath;
var currentPath = Path.Combine(rootFolderPath, request.Name);
var newPath = Path.Combine(rootFolderPath, request.NewName);
if (!Directory.Exists(currentPath))
{
throw new FileNotFoundException("The media collection does not exist");
}
if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && Directory.Exists(newPath))
{
throw new ArgumentException("Media library already exists at " + newPath + ".");
}
_libraryMonitor.Stop();
try
{
// Changing capitalization. Handle windows case insensitivity
if (string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase))
{
var tempPath = Path.Combine(rootFolderPath, Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture));
Directory.Move(currentPath, tempPath);
currentPath = tempPath;
}
Directory.Move(currentPath, newPath);
}
finally
{
CollectionFolder.OnCollectionFolderChange();
Task.Run(() =>
{
// No need to start if scanning the library because it will handle it
if (request.RefreshLibrary)
{
_libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
}
else
{
// Need to add a delay here or directory watchers may still pick up the changes
var task = Task.Delay(1000);
// Have to block here to allow exceptions to bubble
Task.WaitAll(task);
_libraryMonitor.Start();
}
});
}
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Delete(RemoveVirtualFolder request)
{
return _libraryManager.RemoveVirtualFolder(request.Name, request.RefreshLibrary);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(AddMediaPath request)
{
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new ArgumentNullException(nameof(request));
}
_libraryMonitor.Stop();
try
{
var mediaPath = request.PathInfo ?? new MediaPathInfo
{
Path = request.Path
};
_libraryManager.AddMediaPath(request.Name, mediaPath);
}
finally
{
Task.Run(() =>
{
// No need to start if scanning the library because it will handle it
if (request.RefreshLibrary)
{
_libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
}
else
{
// Need to add a delay here or directory watchers may still pick up the changes
var task = Task.Delay(1000);
// Have to block here to allow exceptions to bubble
Task.WaitAll(task);
_libraryMonitor.Start();
}
});
}
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(UpdateMediaPath request)
{
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new ArgumentNullException(nameof(request));
}
_libraryManager.UpdateMediaPath(request.Name, request.PathInfo);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Delete(RemoveMediaPath request)
{
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new ArgumentNullException(nameof(request));
}
_libraryMonitor.Stop();
try
{
_libraryManager.RemoveMediaPath(request.Name, request.Path);
}
finally
{
Task.Run(() =>
{
// No need to start if scanning the library because it will handle it
if (request.RefreshLibrary)
{
_libraryManager.ValidateMediaLibrary(new SimpleProgress<double>(), CancellationToken.None);
}
else
{
// Need to add a delay here or directory watchers may still pick up the changes
var task = Task.Delay(1000);
// Have to block here to allow exceptions to bubble
Task.WaitAll(task);
_libraryMonitor.Start();
}
});
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,111 +0,0 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
/// Class GetCultures.
/// </summary>
[Route("/Localization/Cultures", "GET", Summary = "Gets known cultures")]
public class GetCultures : IReturn<CultureDto[]>
{
}
/// <summary>
/// Class GetCountries.
/// </summary>
[Route("/Localization/Countries", "GET", Summary = "Gets known countries")]
public class GetCountries : IReturn<CountryInfo[]>
{
}
/// <summary>
/// Class ParentalRatings.
/// </summary>
[Route("/Localization/ParentalRatings", "GET", Summary = "Gets known parental ratings")]
public class GetParentalRatings : IReturn<ParentalRating[]>
{
}
/// <summary>
/// Class ParentalRatings.
/// </summary>
[Route("/Localization/Options", "GET", Summary = "Gets localization options")]
public class GetLocalizationOptions : IReturn<LocalizationOption[]>
{
}
/// <summary>
/// Class CulturesService.
/// </summary>
[Authenticated(AllowBeforeStartupWizard = true)]
public class LocalizationService : BaseApiService
{
/// <summary>
/// The _localization.
/// </summary>
private readonly ILocalizationManager _localization;
/// <summary>
/// Initializes a new instance of the <see cref="LocalizationService"/> class.
/// </summary>
/// <param name="localization">The localization.</param>
public LocalizationService(
ILogger<LocalizationService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
ILocalizationManager localization)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_localization = localization;
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetParentalRatings request)
{
var result = _localization.GetParentalRatings();
return ToOptimizedResult(result);
}
public object Get(GetLocalizationOptions request)
{
var result = _localization.GetLocalizationOptions();
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetCountries request)
{
var result = _localization.GetCountries();
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetCultures request)
{
var result = _localization.GetCultures();
return ToOptimizedResult(result);
}
}
}

View file

@ -1,104 +0,0 @@
using System;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Collections;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Movies
{
[Route("/Collections", "POST", Summary = "Creates a new collection")]
public class CreateCollection : IReturn<CollectionCreationResult>
{
[ApiMember(Name = "IsLocked", Description = "Whether or not to lock the new collection.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool IsLocked { get; set; }
[ApiMember(Name = "Name", Description = "The name of the new collection.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Name { get; set; }
[ApiMember(Name = "ParentId", Description = "Optional - create the collection within a specific folder", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string ParentId { get; set; }
[ApiMember(Name = "Ids", Description = "Item Ids to add to the collection", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
public string Ids { get; set; }
}
[Route("/Collections/{Id}/Items", "POST", Summary = "Adds items to a collection")]
public class AddToCollection : IReturnVoid
{
[ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Ids { get; set; }
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
}
[Route("/Collections/{Id}/Items", "DELETE", Summary = "Removes items from a collection")]
public class RemoveFromCollection : IReturnVoid
{
[ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
public string Ids { get; set; }
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
[Authenticated]
public class CollectionService : BaseApiService
{
private readonly ICollectionManager _collectionManager;
private readonly IDtoService _dtoService;
private readonly IAuthorizationContext _authContext;
public CollectionService(
ILogger<CollectionService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
ICollectionManager collectionManager,
IDtoService dtoService,
IAuthorizationContext authContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_collectionManager = collectionManager;
_dtoService = dtoService;
_authContext = authContext;
}
public object Post(CreateCollection request)
{
var userId = _authContext.GetAuthorizationInfo(Request).UserId;
var parentId = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
var item = _collectionManager.CreateCollection(new CollectionCreationOptions
{
IsLocked = request.IsLocked,
Name = request.Name,
ParentId = parentId,
ItemIdList = SplitValue(request.Ids, ','),
UserIds = new[] { userId }
});
var dtoOptions = GetDtoOptions(_authContext, request);
var dto = _dtoService.GetBaseItemDto(item, dtoOptions);
return new CollectionCreationResult
{
Id = dto.Id
};
}
public void Post(AddToCollection request)
{
_collectionManager.AddToCollection(new Guid(request.Id), SplitValue(request.Ids, ','));
}
public void Delete(RemoveFromCollection request)
{
_collectionManager.RemoveFromCollection(new Guid(request.Id), SplitValue(request.Ids, ','));
}
}
}

View file

@ -1,414 +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();
}
}
}

View file

@ -1,88 +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);
}
}
}

View file

@ -1,132 +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.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Music
{
[Route("/Albums/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")]
public class GetSimilarAlbums : BaseGetSimilarItemsFromItem
{
}
[Route("/Artists/{Id}/Similar", "GET", Summary = "Finds albums similar to a given album.")]
public class GetSimilarArtists : BaseGetSimilarItemsFromItem
{
}
[Authenticated]
public class AlbumsService : BaseApiService
{
/// <summary>
/// The _user manager.
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _user data repository.
/// </summary>
private readonly IUserDataManager _userDataRepository;
/// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
private readonly IAuthorizationContext _authContext;
public AlbumsService(
ILogger<AlbumsService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
IUserDataManager userDataRepository,
ILibraryManager libraryManager,
IItemRepository itemRepo,
IDtoService dtoService,
IAuthorizationContext authContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_dtoService = dtoService;
_authContext = authContext;
}
public object Get(GetSimilarArtists request)
{
var dtoOptions = GetDtoOptions(_authContext, request);
var result = SimilarItemsHelper.GetSimilarItemsResult(
dtoOptions,
_userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
_dtoService,
request, new[] { typeof(MusicArtist) },
SimilarItemsHelper.GetSimiliarityScore);
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetSimilarAlbums request)
{
var dtoOptions = GetDtoOptions(_authContext, request);
var result = SimilarItemsHelper.GetSimilarItemsResult(
dtoOptions,
_userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
_dtoService,
request, new[] { typeof(MusicAlbum) },
GetAlbumSimilarityScore);
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the album similarity 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>
private int GetAlbumSimilarityScore(BaseItem item1, List<PersonInfo> item1People, List<PersonInfo> allPeople, BaseItem item2)
{
var points = SimilarItemsHelper.GetSimiliarityScore(item1, item1People, allPeople, item2);
var album1 = (MusicAlbum)item1;
var album2 = (MusicAlbum)item2;
var artists1 = album1
.GetAllArtists()
.DistinctNames()
.ToList();
var artists2 = new HashSet<string>(
album2.GetAllArtists().DistinctNames(),
StringComparer.OrdinalIgnoreCase);
return points + artists1.Where(artists2.Contains).Sum(i => 5);
}
}
}

View file

@ -1,196 +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;
}
}
}

View file

@ -1,197 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
[Route("/Repositories", "GET", Summary = "Gets all package repositories")]
[Authenticated]
public class GetRepositories : IReturnVoid
{
}
[Route("/Repositories", "POST", Summary = "Sets the enabled and existing package repositories")]
[Authenticated]
public class SetRepositories : List<RepositoryInfo>, IReturnVoid
{
}
/// <summary>
/// Class GetPackage.
/// </summary>
[Route("/Packages/{Name}", "GET", Summary = "Gets a package, by name or assembly guid")]
[Authenticated]
public class GetPackage : IReturn<PackageInfo>
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "Name", Description = "The name of the package", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "AssemblyGuid", Description = "The guid of the associated assembly", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string AssemblyGuid { get; set; }
}
/// <summary>
/// Class GetPackages.
/// </summary>
[Route("/Packages", "GET", Summary = "Gets available packages")]
[Authenticated]
public class GetPackages : IReturn<PackageInfo[]>
{
}
/// <summary>
/// Class InstallPackage.
/// </summary>
[Route("/Packages/Installed/{Name}", "POST", Summary = "Installs a package")]
[Authenticated(Roles = "Admin")]
public class InstallPackage : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "Name", Description = "Package name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "AssemblyGuid", Description = "Guid of the associated assembly", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string AssemblyGuid { get; set; }
/// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
[ApiMember(Name = "Version", Description = "Optional version. Defaults to latest version.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Version { get; set; }
}
/// <summary>
/// Class CancelPackageInstallation.
/// </summary>
[Route("/Packages/Installing/{Id}", "DELETE", Summary = "Cancels a package installation")]
[Authenticated(Roles = "Admin")]
public class CancelPackageInstallation : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Installation Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
/// <summary>
/// Class PackageService.
/// </summary>
public class PackageService : BaseApiService
{
private readonly IInstallationManager _installationManager;
private readonly IServerConfigurationManager _serverConfigurationManager;
public PackageService(
ILogger<PackageService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IInstallationManager installationManager)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_installationManager = installationManager;
_serverConfigurationManager = serverConfigurationManager;
}
public object Get(GetRepositories request)
{
var result = _serverConfigurationManager.Configuration.PluginRepositories;
return ToOptimizedResult(result);
}
public void Post(SetRepositories request)
{
_serverConfigurationManager.Configuration.PluginRepositories = request;
_serverConfigurationManager.SaveConfiguration();
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetPackage request)
{
var packages = _installationManager.GetAvailablePackages().GetAwaiter().GetResult();
var result = _installationManager.FilterPackages(
packages,
request.Name,
string.IsNullOrEmpty(request.AssemblyGuid) ? default : Guid.Parse(request.AssemblyGuid)).FirstOrDefault();
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public async Task<object> Get(GetPackages request)
{
IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
return ToOptimizedResult(packages.ToArray());
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <exception cref="ResourceNotFoundException"></exception>
public async Task Post(InstallPackage request)
{
var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
var package = _installationManager.GetCompatibleVersions(
packages,
request.Name,
string.IsNullOrEmpty(request.AssemblyGuid) ? Guid.Empty : Guid.Parse(request.AssemblyGuid),
string.IsNullOrEmpty(request.Version) ? null : Version.Parse(request.Version)).FirstOrDefault();
if (package == null)
{
throw new ResourceNotFoundException(
string.Format(
CultureInfo.InvariantCulture,
"Package not found: {0}",
request.Name));
}
await _installationManager.InstallPackage(package);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Delete(CancelPackageInstallation request)
{
_installationManager.CancelInstallation(new Guid(request.Id));
}
}
}

View file

@ -1,216 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Playlists;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
[Route("/Playlists", "POST", Summary = "Creates a new playlist")]
public class CreatePlaylist : IReturn<PlaylistCreationResult>
{
[ApiMember(Name = "Name", Description = "The name of the new playlist.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Name { get; set; }
[ApiMember(Name = "Ids", Description = "Item Ids to add to the playlist", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
public string Ids { get; set; }
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public Guid UserId { get; set; }
[ApiMember(Name = "MediaType", Description = "The playlist media type", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string MediaType { get; set; }
}
[Route("/Playlists/{Id}/Items", "POST", Summary = "Adds items to a playlist")]
public class AddToPlaylist : IReturnVoid
{
[ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Ids { get; set; }
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public Guid UserId { get; set; }
}
[Route("/Playlists/{Id}/Items/{ItemId}/Move/{NewIndex}", "POST", Summary = "Moves a playlist item")]
public class MoveItem : IReturnVoid
{
[ApiMember(Name = "ItemId", Description = "ItemId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string ItemId { get; set; }
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "NewIndex", Description = "NewIndex", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public int NewIndex { get; set; }
}
[Route("/Playlists/{Id}/Items", "DELETE", Summary = "Removes items from a playlist")]
public class RemoveFromPlaylist : IReturnVoid
{
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
[ApiMember(Name = "EntryIds", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
public string EntryIds { get; set; }
}
[Route("/Playlists/{Id}/Items", "GET", Summary = "Gets the original items of a playlist")]
public class GetPlaylistItems : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
{
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public 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; }
/// <summary>
/// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Fields { get; set; }
[ApiMember(Name = "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; }
}
[Authenticated]
public class PlaylistService : BaseApiService
{
private readonly IPlaylistManager _playlistManager;
private readonly IDtoService _dtoService;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
private readonly IAuthorizationContext _authContext;
public PlaylistService(
ILogger<PlaylistService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IDtoService dtoService,
IPlaylistManager playlistManager,
IUserManager userManager,
ILibraryManager libraryManager,
IAuthorizationContext authContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_dtoService = dtoService;
_playlistManager = playlistManager;
_userManager = userManager;
_libraryManager = libraryManager;
_authContext = authContext;
}
public void Post(MoveItem request)
{
_playlistManager.MoveItem(request.Id, request.ItemId, request.NewIndex);
}
public async Task<object> Post(CreatePlaylist request)
{
var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
{
Name = request.Name,
ItemIdList = GetGuids(request.Ids),
UserId = request.UserId,
MediaType = request.MediaType
}).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public void Post(AddToPlaylist request)
{
_playlistManager.AddToPlaylist(request.Id, GetGuids(request.Ids), request.UserId);
}
public void Delete(RemoveFromPlaylist request)
{
_playlistManager.RemoveFromPlaylist(request.Id, request.EntryIds.Split(','));
}
public object Get(GetPlaylistItems request)
{
var playlist = (Playlist)_libraryManager.GetItemById(request.Id);
var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var items = playlist.GetManageableItems().ToArray();
var count = items.Length;
if (request.StartIndex.HasValue)
{
items = items.Skip(request.StartIndex.Value).ToArray();
}
if (request.Limit.HasValue)
{
items = items.Take(request.Limit.Value).ToArray();
}
var dtoOptions = GetDtoOptions(_authContext, request);
var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user);
for (int index = 0; index < dtos.Count; index++)
{
dtos[index].PlaylistItemId = items[index].Item1.Id;
}
var result = new QueryResult<BaseItemDto>
{
Items = dtos,
TotalRecordCount = count
};
return ToOptimizedResult(result);
}
}
}

View file

@ -1,277 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
/// Class Plugins.
/// </summary>
[Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")]
[Authenticated]
public class GetPlugins : IReturn<PluginInfo[]>
{
public bool? IsAppStoreEnabled { get; set; }
}
/// <summary>
/// Class UninstallPlugin.
/// </summary>
[Route("/Plugins/{Id}", "DELETE", Summary = "Uninstalls a plugin")]
[Authenticated(Roles = "Admin")]
public class UninstallPlugin : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
/// <summary>
/// Class GetPluginConfiguration.
/// </summary>
[Route("/Plugins/{Id}/Configuration", "GET", Summary = "Gets a plugin's configuration")]
[Authenticated]
public class GetPluginConfiguration
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
/// <summary>
/// Class UpdatePluginConfiguration.
/// </summary>
[Route("/Plugins/{Id}/Configuration", "POST", Summary = "Updates a plugin's configuration")]
[Authenticated]
public class UpdatePluginConfiguration : IRequiresRequestStream, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// The raw Http Request Input Stream.
/// </summary>
/// <value>The request stream.</value>
public Stream RequestStream { get; set; }
}
// TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins,
// delete all these registration endpoints. They are only kept for compatibility.
[Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)]
[Authenticated]
public class GetRegistration : IReturn<RegistrationInfo>
{
[ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Name { get; set; }
}
/// <summary>
/// Class GetPluginSecurityInfo.
/// </summary>
[Route("/Plugins/SecurityInfo", "GET", Summary = "Gets plugin registration information", IsHidden = true)]
[Authenticated]
public class GetPluginSecurityInfo : IReturn<PluginSecurityInfo>
{
}
/// <summary>
/// Class UpdatePluginSecurityInfo.
/// </summary>
[Route("/Plugins/SecurityInfo", "POST", Summary = "Updates plugin registration information", IsHidden = true)]
[Authenticated(Roles = "Admin")]
public class UpdatePluginSecurityInfo : PluginSecurityInfo, IReturnVoid
{
}
[Route("/Plugins/RegistrationRecords/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)]
[Authenticated]
public class GetRegistrationStatus
{
[ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Name { get; set; }
}
// TODO these two classes are only kept for compability with paid plugins and should be removed
public class RegistrationInfo
{
public string Name { get; set; }
public DateTime ExpirationDate { get; set; }
public bool IsTrial { get; set; }
public bool IsRegistered { get; set; }
}
public class MBRegistrationRecord
{
public DateTime ExpirationDate { get; set; }
public bool IsRegistered { get; set; }
public bool RegChecked { get; set; }
public bool RegError { get; set; }
public bool TrialVersion { get; set; }
public bool IsValid { get; set; }
}
public class PluginSecurityInfo
{
public string SupporterKey { get; set; }
public bool IsMBSupporter { get; set; }
}
/// <summary>
/// Class PluginsService.
/// </summary>
public class PluginService : BaseApiService
{
/// <summary>
/// The _json serializer.
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
/// The _app host.
/// </summary>
private readonly IApplicationHost _appHost;
private readonly IInstallationManager _installationManager;
public PluginService(
ILogger<PluginService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IJsonSerializer jsonSerializer,
IApplicationHost appHost,
IInstallationManager installationManager)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_appHost = appHost;
_installationManager = installationManager;
_jsonSerializer = jsonSerializer;
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetRegistrationStatus request)
{
var record = new MBRegistrationRecord
{
IsRegistered = true,
RegChecked = true,
TrialVersion = false,
IsValid = true,
RegError = false
};
return ToOptimizedResult(record);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetPlugins request)
{
var result = _appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToArray();
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetPluginConfiguration request)
{
var guid = new Guid(request.Id);
var plugin = _appHost.Plugins.First(p => p.Id == guid) as IHasPluginConfiguration;
return ToOptimizedResult(plugin.Configuration);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetPluginSecurityInfo request)
{
var result = new PluginSecurityInfo
{
IsMBSupporter = true,
SupporterKey = "IAmTotallyLegit"
};
return ToOptimizedResult(result);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Post(UpdatePluginSecurityInfo request)
{
return Task.CompletedTask;
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public async Task Post(UpdatePluginConfiguration request)
{
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var id = Guid.Parse(GetPathValue(1));
if (!(_appHost.Plugins.First(p => p.Id == id) is IHasPluginConfiguration plugin))
{
throw new FileNotFoundException();
}
var configuration = (await _jsonSerializer.DeserializeFromStreamAsync(request.RequestStream, plugin.ConfigurationType).ConfigureAwait(false)) as BasePluginConfiguration;
plugin.UpdateConfiguration(configuration);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Delete(UninstallPlugin request)
{
var guid = new Guid(request.Id);
var plugin = _appHost.Plugins.First(p => p.Id == guid);
_installationManager.UninstallPlugin(plugin);
}
}
}

View file

@ -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();
}
}
}

View file

@ -1,332 +0,0 @@
using System;
using System.Globalization;
using System.Linq;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Search;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
/// Class GetSearchHints.
/// </summary>
[Route("/Search/Hints", "GET", Summary = "Gets search hints based on a search term")]
public class GetSearchHints : IReturn<SearchHintResult>
{
/// <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>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "Optional. Supply a user id to search within a user's library or omit to search all.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid UserId { get; set; }
/// <summary>
/// Search characters used to find items.
/// </summary>
/// <value>The index by.</value>
[ApiMember(Name = "SearchTerm", Description = "The search term to filter on", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string SearchTerm { get; set; }
[ApiMember(Name = "IncludePeople", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludePeople { get; set; }
[ApiMember(Name = "IncludeMedia", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeMedia { get; set; }
[ApiMember(Name = "IncludeGenres", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeGenres { get; set; }
[ApiMember(Name = "IncludeStudios", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeStudios { get; set; }
[ApiMember(Name = "IncludeArtists", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool IncludeArtists { 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 = "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; }
[ApiMember(Name = "MediaTypes", 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 MediaTypes { get; set; }
public string ParentId { get; set; }
[ApiMember(Name = "IsMovie", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsMovie { get; set; }
[ApiMember(Name = "IsSeries", Description = "Optional filter for movies.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSeries { get; set; }
[ApiMember(Name = "IsNews", Description = "Optional filter for news.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsNews { get; set; }
[ApiMember(Name = "IsKids", Description = "Optional filter for kids.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsKids { get; set; }
[ApiMember(Name = "IsSports", Description = "Optional filter for sports.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET,POST")]
public bool? IsSports { get; set; }
public GetSearchHints()
{
IncludeArtists = true;
IncludeGenres = true;
IncludeMedia = true;
IncludePeople = true;
IncludeStudios = true;
}
}
/// <summary>
/// Class SearchService.
/// </summary>
[Authenticated]
public class SearchService : BaseApiService
{
/// <summary>
/// The _search engine.
/// </summary>
private readonly ISearchEngine _searchEngine;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
/// <summary>
/// Initializes a new instance of the <see cref="SearchService" /> class.
/// </summary>
/// <param name="searchEngine">The search engine.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="dtoService">The dto service.</param>
/// <param name="imageProcessor">The image processor.</param>
public SearchService(
ILogger<SearchService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
ISearchEngine searchEngine,
ILibraryManager libraryManager,
IDtoService dtoService,
IImageProcessor imageProcessor)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_searchEngine = searchEngine;
_libraryManager = libraryManager;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetSearchHints request)
{
var result = GetSearchHintsAsync(request);
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the search hints async.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task{IEnumerable{SearchHintResult}}.</returns>
private SearchHintResult GetSearchHintsAsync(GetSearchHints request)
{
var result = _searchEngine.GetSearchHints(new SearchQuery
{
Limit = request.Limit,
SearchTerm = request.SearchTerm,
IncludeArtists = request.IncludeArtists,
IncludeGenres = request.IncludeGenres,
IncludeMedia = request.IncludeMedia,
IncludePeople = request.IncludePeople,
IncludeStudios = request.IncludeStudios,
StartIndex = request.StartIndex,
UserId = request.UserId,
IncludeItemTypes = ApiEntryPoint.Split(request.IncludeItemTypes, ',', true),
ExcludeItemTypes = ApiEntryPoint.Split(request.ExcludeItemTypes, ',', true),
MediaTypes = ApiEntryPoint.Split(request.MediaTypes, ',', true),
ParentId = request.ParentId,
IsKids = request.IsKids,
IsMovie = request.IsMovie,
IsNews = request.IsNews,
IsSeries = request.IsSeries,
IsSports = request.IsSports
});
return new SearchHintResult
{
TotalRecordCount = result.TotalRecordCount,
SearchHints = result.Items.Select(GetSearchHintResult).ToArray()
};
}
/// <summary>
/// Gets the search hint result.
/// </summary>
/// <param name="hintInfo">The hint info.</param>
/// <returns>SearchHintResult.</returns>
private SearchHint GetSearchHintResult(SearchHintInfo hintInfo)
{
var item = hintInfo.Item;
var result = new SearchHint
{
Name = item.Name,
IndexNumber = item.IndexNumber,
ParentIndexNumber = item.ParentIndexNumber,
Id = item.Id,
Type = item.GetClientTypeName(),
MediaType = item.MediaType,
MatchedTerm = hintInfo.MatchedTerm,
RunTimeTicks = item.RunTimeTicks,
ProductionYear = item.ProductionYear,
ChannelId = item.ChannelId,
EndDate = item.EndDate
};
// legacy
result.ItemId = result.Id;
if (item.IsFolder)
{
result.IsFolder = true;
}
var primaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary);
if (primaryImageTag != null)
{
result.PrimaryImageTag = primaryImageTag;
result.PrimaryImageAspectRatio = _dtoService.GetPrimaryImageAspectRatio(item);
}
SetThumbImageInfo(result, item);
SetBackdropImageInfo(result, item);
switch (item)
{
case IHasSeries hasSeries:
result.Series = hasSeries.SeriesName;
break;
case LiveTvProgram program:
result.StartDate = program.StartDate;
break;
case Series series:
if (series.Status.HasValue)
{
result.Status = series.Status.Value.ToString();
}
break;
case MusicAlbum album:
result.Artists = album.Artists;
result.AlbumArtist = album.AlbumArtist;
break;
case Audio song:
result.AlbumArtist = song.AlbumArtists.FirstOrDefault();
result.Artists = song.Artists;
MusicAlbum musicAlbum = song.AlbumEntity;
if (musicAlbum != null)
{
result.Album = musicAlbum.Name;
result.AlbumId = musicAlbum.Id;
}
else
{
result.Album = song.Album;
}
break;
}
if (!item.ChannelId.Equals(Guid.Empty))
{
var channel = _libraryManager.GetItemById(item.ChannelId);
result.ChannelName = channel?.Name;
}
return result;
}
private void SetThumbImageInfo(SearchHint hint, BaseItem item)
{
var itemWithImage = item.HasImage(ImageType.Thumb) ? item : null;
if (itemWithImage == null && item is Episode)
{
itemWithImage = GetParentWithImage<Series>(item, ImageType.Thumb);
}
if (itemWithImage == null)
{
itemWithImage = GetParentWithImage<BaseItem>(item, ImageType.Thumb);
}
if (itemWithImage != null)
{
var tag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Thumb);
if (tag != null)
{
hint.ThumbImageTag = tag;
hint.ThumbImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture);
}
}
}
private void SetBackdropImageInfo(SearchHint hint, BaseItem item)
{
var itemWithImage = (item.HasImage(ImageType.Backdrop) ? item : null)
?? GetParentWithImage<BaseItem>(item, ImageType.Backdrop);
if (itemWithImage != null)
{
var tag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Backdrop);
if (tag != null)
{
hint.BackdropImageTag = tag;
hint.BackdropImageItemId = itemWithImage.Id.ToString("N", CultureInfo.InvariantCulture);
}
}
}
private T GetParentWithImage<T>(BaseItem item, ImageType type)
where T : BaseItem
{
return item.GetParents().OfType<T>().FirstOrDefault(i => i.HasImage(type));
}
}
}

View file

@ -1,499 +0,0 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
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.Sessions
{
/// <summary>
/// Class GetSessions.
/// </summary>
[Route("/Sessions", "GET", Summary = "Gets a list of sessions")]
[Authenticated]
public class GetSessions : IReturn<SessionInfo[]>
{
[ApiMember(Name = "ControllableByUserId", Description = "Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid ControllableByUserId { get; set; }
[ApiMember(Name = "DeviceId", Description = "Filter by device Id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceId { get; set; }
public int? ActiveWithinSeconds { get; set; }
}
/// <summary>
/// Class DisplayContent.
/// </summary>
[Route("/Sessions/{Id}/Viewing", "POST", Summary = "Instructs a session to browse to an item or view")]
[Authenticated]
public class DisplayContent : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// Artist, Genre, Studio, Person, or any kind of BaseItem.
/// </summary>
/// <value>The type of the item.</value>
[ApiMember(Name = "ItemType", Description = "The type of item to browse to.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string ItemType { get; set; }
/// <summary>
/// Artist name, genre name, item Id, etc.
/// </summary>
/// <value>The item identifier.</value>
[ApiMember(Name = "ItemId", Description = "The Id of the item.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string ItemId { get; set; }
/// <summary>
/// Gets or sets the name of the item.
/// </summary>
/// <value>The name of the item.</value>
[ApiMember(Name = "ItemName", Description = "The name of the item.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string ItemName { get; set; }
}
[Route("/Sessions/{Id}/Playing", "POST", Summary = "Instructs a session to play an item")]
[Authenticated]
public class Play : PlayRequest
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
}
[Route("/Sessions/{Id}/Playing/{Command}", "POST", Summary = "Issues a playstate command to a client")]
[Authenticated]
public class SendPlaystateCommand : PlaystateRequest, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
}
[Route("/Sessions/{Id}/System/{Command}", "POST", Summary = "Issues a system command to a client")]
[Authenticated]
public class SendSystemCommand : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the command.
/// </summary>
/// <value>The play command.</value>
[ApiMember(Name = "Command", Description = "The command to send.", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Command { get; set; }
}
[Route("/Sessions/{Id}/Command/{Command}", "POST", Summary = "Issues a system command to a client")]
[Authenticated]
public class SendGeneralCommand : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the command.
/// </summary>
/// <value>The play command.</value>
[ApiMember(Name = "Command", Description = "The command to send.", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Command { get; set; }
}
[Route("/Sessions/{Id}/Command", "POST", Summary = "Issues a system command to a client")]
[Authenticated]
public class SendFullGeneralCommand : GeneralCommand, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
}
[Route("/Sessions/{Id}/Message", "POST", Summary = "Issues a command to a client to display a message to the user")]
[Authenticated]
public class SendMessageCommand : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
[ApiMember(Name = "Text", Description = "The message text.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Text { get; set; }
[ApiMember(Name = "Header", Description = "The message header.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Header { get; set; }
[ApiMember(Name = "TimeoutMs", Description = "The message timeout. If omitted the user will have to confirm viewing the message.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public long? TimeoutMs { get; set; }
}
[Route("/Sessions/{Id}/Users/{UserId}", "POST", Summary = "Adds an additional user to a session")]
[Authenticated]
public class AddUserToSession : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
[ApiMember(Name = "UserId", Description = "UserId Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string UserId { get; set; }
}
[Route("/Sessions/{Id}/Users/{UserId}", "DELETE", Summary = "Removes an additional user from a session")]
[Authenticated]
public class RemoveUserFromSession : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string UserId { get; set; }
}
[Route("/Sessions/Capabilities", "POST", Summary = "Updates capabilities for a device")]
[Authenticated]
public class PostCapabilities : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Id { get; set; }
[ApiMember(Name = "PlayableMediaTypes", Description = "A list of playable media types, comma delimited. Audio, Video, Book, Photo.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string PlayableMediaTypes { get; set; }
[ApiMember(Name = "SupportedCommands", Description = "A list of supported remote control commands, comma delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string SupportedCommands { get; set; }
[ApiMember(Name = "SupportsMediaControl", Description = "Determines whether media can be played remotely.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsMediaControl { get; set; }
[ApiMember(Name = "SupportsSync", Description = "Determines whether sync is supported.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsSync { get; set; }
[ApiMember(Name = "SupportsPersistentIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
public bool SupportsPersistentIdentifier { get; set; }
public PostCapabilities()
{
SupportsPersistentIdentifier = true;
}
}
[Route("/Sessions/Capabilities/Full", "POST", Summary = "Updates capabilities for a device")]
[Authenticated]
public class PostFullCapabilities : ClientCapabilities, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Session Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Id { get; set; }
}
[Route("/Sessions/Viewing", "POST", Summary = "Reports that a session is viewing an item")]
[Authenticated]
public class ReportViewing : IReturnVoid
{
[ApiMember(Name = "SessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string SessionId { get; set; }
[ApiMember(Name = "ItemId", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string ItemId { get; set; }
}
[Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")]
[Authenticated]
public class ReportSessionEnded : IReturnVoid
{
}
[Route("/Auth/Providers", "GET")]
[Authenticated(Roles = "Admin")]
public class GetAuthProviders : IReturn<NameIdPair[]>
{
}
[Route("/Auth/PasswordResetProviders", "GET")]
[Authenticated(Roles = "Admin")]
public class GetPasswordResetProviders : IReturn<NameIdPair[]>
{
}
/// <summary>
/// Class SessionsService.
/// </summary>
public class SessionService : BaseApiService
{
/// <summary>
/// The session manager.
/// </summary>
private readonly ISessionManager _sessionManager;
private readonly IUserManager _userManager;
private readonly IAuthorizationContext _authContext;
private readonly IDeviceManager _deviceManager;
private readonly ISessionContext _sessionContext;
public SessionService(
ILogger<SessionService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
ISessionManager sessionManager,
IUserManager userManager,
IAuthorizationContext authContext,
IDeviceManager deviceManager,
ISessionContext sessionContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_sessionManager = sessionManager;
_userManager = userManager;
_authContext = authContext;
_deviceManager = deviceManager;
_sessionContext = sessionContext;
}
public object Get(GetAuthProviders request)
{
return _userManager.GetAuthenticationProviders();
}
public object Get(GetPasswordResetProviders request)
{
return _userManager.GetPasswordResetProviders();
}
public void Post(ReportSessionEnded request)
{
var auth = _authContext.GetAuthorizationInfo(Request);
_sessionManager.Logout(auth.Token);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetSessions request)
{
var result = _sessionManager.Sessions;
if (!string.IsNullOrEmpty(request.DeviceId))
{
result = result.Where(i => string.Equals(i.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase));
}
if (!request.ControllableByUserId.Equals(Guid.Empty))
{
result = result.Where(i => i.SupportsRemoteControl);
var user = _userManager.GetUserById(request.ControllableByUserId);
if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
{
result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(request.ControllableByUserId));
}
if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl))
{
result = result.Where(i => !i.UserId.Equals(Guid.Empty));
}
if (request.ActiveWithinSeconds.HasValue && request.ActiveWithinSeconds.Value > 0)
{
var minActiveDate = DateTime.UtcNow.AddSeconds(0 - request.ActiveWithinSeconds.Value);
result = result.Where(i => i.LastActivityDate >= minActiveDate);
}
result = result.Where(i =>
{
var deviceId = i.DeviceId;
if (!string.IsNullOrWhiteSpace(deviceId))
{
if (!_deviceManager.CanAccessDevice(user, deviceId))
{
return false;
}
}
return true;
});
}
return ToOptimizedResult(result.ToArray());
}
public Task Post(SendPlaystateCommand request)
{
return _sessionManager.SendPlaystateCommand(GetSession(_sessionContext).Id, request.Id, request, CancellationToken.None);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Post(DisplayContent request)
{
var command = new BrowseRequest
{
ItemId = request.ItemId,
ItemName = request.ItemName,
ItemType = request.ItemType
};
return _sessionManager.SendBrowseCommand(GetSession(_sessionContext).Id, request.Id, command, CancellationToken.None);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Post(SendSystemCommand request)
{
var name = request.Command;
if (Enum.TryParse(name, true, out GeneralCommandType commandType))
{
name = commandType.ToString();
}
var currentSession = GetSession(_sessionContext);
var command = new GeneralCommand
{
Name = name,
ControllingUserId = currentSession.UserId
};
return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Post(SendMessageCommand request)
{
var command = new MessageCommand
{
Header = string.IsNullOrEmpty(request.Header) ? "Message from Server" : request.Header,
TimeoutMs = request.TimeoutMs,
Text = request.Text
};
return _sessionManager.SendMessageCommand(GetSession(_sessionContext).Id, request.Id, command, CancellationToken.None);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Post(Play request)
{
return _sessionManager.SendPlayCommand(GetSession(_sessionContext).Id, request.Id, request, CancellationToken.None);
}
public Task Post(SendGeneralCommand request)
{
var currentSession = GetSession(_sessionContext);
var command = new GeneralCommand
{
Name = request.Command,
ControllingUserId = currentSession.UserId
};
return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, command, CancellationToken.None);
}
public Task Post(SendFullGeneralCommand request)
{
var currentSession = GetSession(_sessionContext);
request.ControllingUserId = currentSession.UserId;
return _sessionManager.SendGeneralCommand(currentSession.Id, request.Id, request, CancellationToken.None);
}
public void Post(AddUserToSession request)
{
_sessionManager.AddAdditionalUser(request.Id, new Guid(request.UserId));
}
public void Delete(RemoveUserFromSession request)
{
_sessionManager.RemoveAdditionalUser(request.Id, new Guid(request.UserId));
}
public void Post(PostCapabilities request)
{
if (string.IsNullOrWhiteSpace(request.Id))
{
request.Id = GetSession(_sessionContext).Id;
}
_sessionManager.ReportCapabilities(request.Id, new ClientCapabilities
{
PlayableMediaTypes = SplitValue(request.PlayableMediaTypes, ','),
SupportedCommands = SplitValue(request.SupportedCommands, ','),
SupportsMediaControl = request.SupportsMediaControl,
SupportsSync = request.SupportsSync,
SupportsPersistentIdentifier = request.SupportsPersistentIdentifier
});
}
public void Post(PostFullCapabilities request)
{
if (string.IsNullOrWhiteSpace(request.Id))
{
request.Id = GetSession(_sessionContext).Id;
}
_sessionManager.ReportCapabilities(request.Id, request);
}
public void Post(ReportViewing request)
{
request.SessionId = GetSession(_sessionContext).Id;
_sessionManager.ReportNowViewingItem(request.SessionId, request.ItemId);
}
}
}

View file

@ -1,302 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
namespace MediaBrowser.Api.Subtitles
{
[Route("/Videos/{Id}/Subtitles/{Index}", "DELETE", Summary = "Deletes an external subtitle file")]
[Authenticated(Roles = "Admin")]
public class DeleteSubtitle
{
/// <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; }
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "DELETE")]
public int Index { get; set; }
}
[Route("/Items/{Id}/RemoteSearch/Subtitles/{Language}", "GET")]
[Authenticated]
public class SearchRemoteSubtitles : IReturn<RemoteSubtitleInfo[]>
{
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public Guid Id { get; set; }
[ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Language { get; set; }
public bool? IsPerfectMatch { get; set; }
}
[Route("/Items/{Id}/RemoteSearch/Subtitles/{SubtitleId}", "POST")]
[Authenticated]
public class DownloadRemoteSubtitles : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public Guid Id { get; set; }
[ApiMember(Name = "SubtitleId", Description = "SubtitleId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string SubtitleId { get; set; }
}
[Route("/Providers/Subtitles/Subtitles/{Id}", "GET")]
[Authenticated]
public class GetRemoteSubtitles : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format.")]
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/{StartPositionTicks}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format.")]
public class GetSubtitle
{
/// <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 Guid Id { get; set; }
[ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string MediaSourceId { get; set; }
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
public int Index { get; set; }
[ApiMember(Name = "Format", Description = "Format", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Format { get; set; }
[ApiMember(Name = "StartPositionTicks", Description = "StartPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public long StartPositionTicks { get; set; }
[ApiMember(Name = "EndPositionTicks", Description = "EndPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public long? EndPositionTicks { get; set; }
[ApiMember(Name = "CopyTimestamps", Description = "CopyTimestamps", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool CopyTimestamps { get; set; }
public bool AddVttTimeMap { get; set; }
}
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/subtitles.m3u8", "GET", Summary = "Gets an HLS subtitle playlist.")]
[Authenticated]
public class GetSubtitlePlaylist
{
/// <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; }
[ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string MediaSourceId { get; set; }
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
public int Index { get; set; }
[ApiMember(Name = "SegmentLength", Description = "The subtitle srgment length", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
public int SegmentLength { get; set; }
}
public class SubtitleService : BaseApiService
{
private readonly ILibraryManager _libraryManager;
private readonly ISubtitleManager _subtitleManager;
private readonly ISubtitleEncoder _subtitleEncoder;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
private readonly IAuthorizationContext _authContext;
public SubtitleService(
ILogger<SubtitleService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
ILibraryManager libraryManager,
ISubtitleManager subtitleManager,
ISubtitleEncoder subtitleEncoder,
IMediaSourceManager mediaSourceManager,
IProviderManager providerManager,
IFileSystem fileSystem,
IAuthorizationContext authContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_libraryManager = libraryManager;
_subtitleManager = subtitleManager;
_subtitleEncoder = subtitleEncoder;
_mediaSourceManager = mediaSourceManager;
_providerManager = providerManager;
_fileSystem = fileSystem;
_authContext = authContext;
}
public async Task<object> Get(GetSubtitlePlaylist request)
{
var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, null, false, CancellationToken.None).ConfigureAwait(false);
var builder = new StringBuilder();
var runtime = mediaSource.RunTimeTicks ?? -1;
if (runtime <= 0)
{
throw new ArgumentException("HLS Subtitles are not supported for this media.");
}
var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks;
if (segmentLengthTicks <= 0)
{
throw new ArgumentException("segmentLength was not given, or it was given incorrectly. (It should be bigger than 0)");
}
builder.AppendLine("#EXTM3U");
builder.AppendLine("#EXT-X-TARGETDURATION:" + request.SegmentLength.ToString(CultureInfo.InvariantCulture));
builder.AppendLine("#EXT-X-VERSION:3");
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
long positionTicks = 0;
var accessToken = _authContext.GetAuthorizationInfo(Request).Token;
while (positionTicks < runtime)
{
var remaining = runtime - positionTicks;
var lengthTicks = Math.Min(remaining, segmentLengthTicks);
builder.AppendLine("#EXTINF:" + TimeSpan.FromTicks(lengthTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture) + ",");
var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks);
var url = string.Format("stream.vtt?CopyTimestamps=true&AddVttTimeMap=true&StartPositionTicks={0}&EndPositionTicks={1}&api_key={2}",
positionTicks.ToString(CultureInfo.InvariantCulture),
endPositionTicks.ToString(CultureInfo.InvariantCulture),
accessToken);
builder.AppendLine(url);
positionTicks += segmentLengthTicks;
}
builder.AppendLine("#EXT-X-ENDLIST");
return ResultFactory.GetResult(Request, builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
}
public async Task<object> Get(GetSubtitle request)
{
if (string.Equals(request.Format, "js", StringComparison.OrdinalIgnoreCase))
{
request.Format = "json";
}
if (string.IsNullOrEmpty(request.Format))
{
var item = (Video)_libraryManager.GetItemById(request.Id);
var idString = request.Id.ToString("N", CultureInfo.InvariantCulture);
var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null)
.First(i => string.Equals(i.Id, request.MediaSourceId ?? idString));
var subtitleStream = mediaSource.MediaStreams
.First(i => i.Type == MediaStreamType.Subtitle && i.Index == request.Index);
return await ResultFactory.GetStaticFileResult(Request, subtitleStream.Path).ConfigureAwait(false);
}
if (string.Equals(request.Format, "vtt", StringComparison.OrdinalIgnoreCase) && request.AddVttTimeMap)
{
using var stream = await GetSubtitles(request).ConfigureAwait(false);
using var reader = new StreamReader(stream);
var text = reader.ReadToEnd();
text = text.Replace("WEBVTT", "WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000");
return ResultFactory.GetResult(Request, text, MimeTypes.GetMimeType("file." + request.Format));
}
return ResultFactory.GetResult(Request, await GetSubtitles(request).ConfigureAwait(false), MimeTypes.GetMimeType("file." + request.Format));
}
private Task<Stream> GetSubtitles(GetSubtitle request)
{
var item = _libraryManager.GetItemById(request.Id);
return _subtitleEncoder.GetSubtitles(item,
request.MediaSourceId,
request.Index,
request.Format,
request.StartPositionTicks,
request.EndPositionTicks ?? 0,
request.CopyTimestamps,
CancellationToken.None);
}
public async Task<object> Get(SearchRemoteSubtitles request)
{
var video = (Video)_libraryManager.GetItemById(request.Id);
return await _subtitleManager.SearchSubtitles(video, request.Language, request.IsPerfectMatch, CancellationToken.None).ConfigureAwait(false);
}
public Task Delete(DeleteSubtitle request)
{
var item = _libraryManager.GetItemById(request.Id);
return _subtitleManager.DeleteSubtitles(item, request.Index);
}
public async Task<object> Get(GetRemoteSubtitles request)
{
var result = await _subtitleManager.GetRemoteSubtitles(request.Id, CancellationToken.None).ConfigureAwait(false);
return ResultFactory.GetResult(Request, result.Stream, MimeTypes.GetMimeType("file." + result.Format));
}
public void Post(DownloadRemoteSubtitles request)
{
var video = (Video)_libraryManager.GetItemById(request.Id);
Task.Run(async () =>
{
try
{
await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
.ConfigureAwait(false);
_providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.High);
}
catch (Exception ex)
{
Logger.LogError(ex, "Error downloading subtitles");
}
});
}
}
}

View file

@ -1,103 +0,0 @@
using System;
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.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
[Route("/Users/{UserId}/Suggestions", "GET", Summary = "Gets items based on a query.")]
public class GetSuggestedItems : IReturn<QueryResult<BaseItemDto>>
{
public string MediaType { get; set; }
public string Type { get; set; }
public Guid UserId { get; set; }
public bool EnableTotalRecordCount { get; set; }
public int? StartIndex { get; set; }
public int? Limit { get; set; }
public string[] GetMediaTypes()
{
return (MediaType ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
public string[] GetIncludeItemTypes()
{
return (Type ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
}
public class SuggestionsService : BaseApiService
{
private readonly IDtoService _dtoService;
private readonly IAuthorizationContext _authContext;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
public SuggestionsService(
ILogger<SuggestionsService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IDtoService dtoService,
IAuthorizationContext authContext,
IUserManager userManager,
ILibraryManager libraryManager)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_dtoService = dtoService;
_authContext = authContext;
_userManager = userManager;
_libraryManager = libraryManager;
}
public object Get(GetSuggestedItems request)
{
return GetResultItems(request);
}
private QueryResult<BaseItemDto> GetResultItems(GetSuggestedItems request)
{
var user = !request.UserId.Equals(Guid.Empty) ? _userManager.GetUserById(request.UserId) : null;
var dtoOptions = GetDtoOptions(_authContext, request);
var result = GetItems(request, user, dtoOptions);
var dtoList = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user);
return new QueryResult<BaseItemDto>
{
TotalRecordCount = result.TotalRecordCount,
Items = dtoList
};
}
private QueryResult<BaseItem> GetItems(GetSuggestedItems request, User user, DtoOptions dtoOptions)
{
return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
{
OrderBy = new[] { ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
MediaTypes = request.GetMediaTypes(),
IncludeItemTypes = request.GetIncludeItemTypes(),
IsVirtualItem = false,
StartIndex = request.StartIndex,
Limit = request.Limit,
DtoOptions = dtoOptions,
EnableTotalRecordCount = request.EnableTotalRecordCount,
Recursive = true
});
}
}
}

View file

@ -1,69 +0,0 @@
using System;
using System.Globalization;
using System.Linq;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.System
{
[Route("/System/ActivityLog/Entries", "GET", Summary = "Gets activity log entries")]
public class GetActivityLogs : IReturn<QueryResult<ActivityLogEntry>>
{
/// <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 = "MinDate", Description = "Optional. The minimum date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string MinDate { get; set; }
public bool? HasUserId { get; set; }
}
[Authenticated(Roles = "Admin")]
public class ActivityLogService : BaseApiService
{
private readonly IActivityManager _activityManager;
public ActivityLogService(
ILogger<ActivityLogService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IActivityManager activityManager)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_activityManager = activityManager;
}
public object Get(GetActivityLogs request)
{
DateTime? minDate = string.IsNullOrWhiteSpace(request.MinDate) ?
(DateTime?)null :
DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
var filterFunc = new Func<IQueryable<ActivityLog>, IQueryable<ActivityLog>>(
entries => entries.Where(entry => entry.DateCreated >= minDate
&& (!request.HasUserId.HasValue || (request.HasUserId.Value
? entry.UserId != Guid.Empty
: entry.UserId == Guid.Empty))));
var result = _activityManager.GetPagedResult(filterFunc, request.StartIndex, request.Limit);
return ToOptimizedResult(result);
}
}
}

View file

@ -1,221 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.System
{
/// <summary>
/// Class GetSystemInfo.
/// </summary>
[Route("/System/Info", "GET", Summary = "Gets information about the server")]
[Authenticated(EscapeParentalControl = true, AllowBeforeStartupWizard = true)]
public class GetSystemInfo : IReturn<SystemInfo>
{
}
[Route("/System/Info/Public", "GET", Summary = "Gets public information about the server")]
public class GetPublicSystemInfo : IReturn<PublicSystemInfo>
{
}
[Route("/System/Ping", "POST")]
[Route("/System/Ping", "GET")]
public class PingSystem : IReturnVoid
{
}
/// <summary>
/// Class RestartApplication.
/// </summary>
[Route("/System/Restart", "POST", Summary = "Restarts the application, if needed")]
[Authenticated(Roles = "Admin", AllowLocal = true)]
public class RestartApplication
{
}
/// <summary>
/// This is currently not authenticated because the uninstaller needs to be able to shutdown the server.
/// </summary>
[Route("/System/Shutdown", "POST", Summary = "Shuts down the application")]
[Authenticated(Roles = "Admin", AllowLocal = true)]
public class ShutdownApplication
{
}
[Route("/System/Logs", "GET", Summary = "Gets a list of available server log files")]
[Authenticated(Roles = "Admin")]
public class GetServerLogs : IReturn<LogFile[]>
{
}
[Route("/System/Endpoint", "GET", Summary = "Gets information about the request endpoint")]
[Authenticated]
public class GetEndpointInfo : IReturn<EndPointInfo>
{
public string Endpoint { get; set; }
}
[Route("/System/Logs/Log", "GET", Summary = "Gets a log file")]
[Authenticated(Roles = "Admin")]
public class GetLogFile
{
[ApiMember(Name = "Name", Description = "The log file name.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Name { get; set; }
}
[Route("/System/WakeOnLanInfo", "GET", Summary = "Gets wake on lan information")]
[Authenticated]
public class GetWakeOnLanInfo : IReturn<WakeOnLanInfo[]>
{
}
/// <summary>
/// Class SystemInfoService.
/// </summary>
public class SystemService : BaseApiService
{
/// <summary>
/// The _app host.
/// </summary>
private readonly IServerApplicationHost _appHost;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly INetworkManager _network;
/// <summary>
/// Initializes a new instance of the <see cref="SystemService" /> class.
/// </summary>
/// <param name="appHost">The app host.</param>
/// <param name="fileSystem">The file system.</param>
/// <exception cref="ArgumentNullException">jsonSerializer</exception>
public SystemService(
ILogger<SystemService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IServerApplicationHost appHost,
IFileSystem fileSystem,
INetworkManager network)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_appPaths = serverConfigurationManager.ApplicationPaths;
_appHost = appHost;
_fileSystem = fileSystem;
_network = network;
}
public object Post(PingSystem request)
{
return _appHost.Name;
}
public object Get(GetWakeOnLanInfo request)
{
var result = _appHost.GetWakeOnLanInfo();
return ToOptimizedResult(result);
}
public object Get(GetServerLogs request)
{
IEnumerable<FileSystemMetadata> files;
try
{
files = _fileSystem.GetFiles(_appPaths.LogDirectoryPath, new[] { ".txt", ".log" }, true, false);
}
catch (IOException ex)
{
Logger.LogError(ex, "Error getting logs");
files = Enumerable.Empty<FileSystemMetadata>();
}
var result = files.Select(i => new LogFile
{
DateCreated = _fileSystem.GetCreationTimeUtc(i),
DateModified = _fileSystem.GetLastWriteTimeUtc(i),
Name = i.Name,
Size = i.Length
}).OrderByDescending(i => i.DateModified)
.ThenByDescending(i => i.DateCreated)
.ThenBy(i => i.Name)
.ToArray();
return ToOptimizedResult(result);
}
public Task<object> Get(GetLogFile request)
{
var file = _fileSystem.GetFiles(_appPaths.LogDirectoryPath)
.First(i => string.Equals(i.Name, request.Name, StringComparison.OrdinalIgnoreCase));
// For older files, assume fully static
var fileShare = file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1) ? FileShare.Read : FileShare.ReadWrite;
return ResultFactory.GetStaticFileResult(Request, file.FullName, fileShare);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public async Task<object> Get(GetSystemInfo request)
{
var result = await _appHost.GetSystemInfo(CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
public async Task<object> Get(GetPublicSystemInfo request)
{
var result = await _appHost.GetPublicSystemInfo(CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(RestartApplication request)
{
_appHost.Restart();
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(ShutdownApplication request)
{
Task.Run(async () =>
{
await Task.Delay(100).ConfigureAwait(false);
await _appHost.Shutdown().ConfigureAwait(false);
});
}
public object Get(GetEndpointInfo request)
{
return ToOptimizedResult(new EndPointInfo
{
IsLocal = Request.IsLocal,
IsInNetwork = _network.IsInLocalNetwork(request.Endpoint ?? Request.RemoteIp)
});
}
}
}

View file

@ -1,497 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.TV;
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 GetNextUpEpisodes.
/// </summary>
[Route("/Shows/NextUp", "GET", Summary = "Gets a list of next up episodes")]
public class GetNextUpEpisodes : IReturn<QueryResult<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 = "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; }
/// <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; }
[ApiMember(Name = "SeriesId", Description = "Optional. Filter by series id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string SeriesId { 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 = "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 bool EnableTotalRecordCount { get; set; }
public GetNextUpEpisodes()
{
EnableTotalRecordCount = true;
}
}
[Route("/Shows/Upcoming", "GET", Summary = "Gets a list of upcoming episodes")]
public class GetUpcomingEpisodes : IReturn<QueryResult<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 = "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; }
/// <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>
/// 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 = "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; }
}
[Route("/Shows/{Id}/Episodes", "GET", Summary = "Gets episodes for a tv season")]
public class GetEpisodes : IReturn<QueryResult<BaseItemDto>>, IHasItemFields, 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 = "query", Verb = "GET")]
public Guid UserId { 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; }
[ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "Season", Description = "Optional filter by season number.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public int? Season { get; set; }
[ApiMember(Name = "SeasonId", Description = "Optional. Filter by season id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string SeasonId { 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 = "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 = "StartItemId", Description = "Optional. Skip through the list until a given item is found.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string StartItemId { 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 = "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; }
[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 = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public SortOrder? SortOrder { get; set; }
}
[Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")]
public class GetSeasons : IReturn<QueryResult<BaseItemDto>>, IHasItemFields, 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 = "query", Verb = "GET")]
public Guid UserId { 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; }
[ApiMember(Name = "Id", Description = "The series id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Id { get; set; }
[ApiMember(Name = "IsSpecialSeason", Description = "Optional. Filter by special season.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsSpecialSeason { 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 = "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 = "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; }
}
/// <summary>
/// Class TvShowsService.
/// </summary>
[Authenticated]
public class TvShowsService : BaseApiService
{
/// <summary>
/// The _user manager.
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly ITVSeriesManager _tvSeriesManager;
private readonly IAuthorizationContext _authContext;
/// <summary>
/// Initializes a new instance of the <see cref="TvShowsService" /> class.
/// </summary>
/// <param name="userManager">The user manager.</param>
/// <param name="userDataManager">The user data repository.</param>
/// <param name="libraryManager">The library manager.</param>
public TvShowsService(
ILogger<TvShowsService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
ILibraryManager libraryManager,
IDtoService dtoService,
ITVSeriesManager tvSeriesManager,
IAuthorizationContext authContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_userManager = userManager;
_libraryManager = libraryManager;
_dtoService = dtoService;
_tvSeriesManager = tvSeriesManager;
_authContext = authContext;
}
public object Get(GetUpcomingEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
var minPremiereDate = DateTime.Now.Date.ToUniversalTime().AddDays(-1);
var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? Guid.Empty : new Guid(request.ParentId);
var options = GetDtoOptions(_authContext, request);
var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(Episode).Name },
OrderBy = new[] { ItemSortBy.PremiereDate, ItemSortBy.SortName }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Ascending)).ToArray(),
MinPremiereDate = minPremiereDate,
StartIndex = request.StartIndex,
Limit = request.Limit,
ParentId = parentIdGuid,
Recursive = true,
DtoOptions = options
});
var returnItems = _dtoService.GetBaseItemDtos(itemsResult, options, user);
var result = new QueryResult<BaseItemDto>
{
TotalRecordCount = itemsResult.Count,
Items = returnItems
};
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetNextUpEpisodes request)
{
var options = GetDtoOptions(_authContext, request);
var result = _tvSeriesManager.GetNextUp(new NextUpQuery
{
Limit = request.Limit,
ParentId = request.ParentId,
SeriesId = request.SeriesId,
StartIndex = request.StartIndex,
UserId = request.UserId,
EnableTotalRecordCount = request.EnableTotalRecordCount
}, options);
var user = _userManager.GetUserById(request.UserId);
var returnItems = _dtoService.GetBaseItemDtos(result.Items, options, user);
return ToOptimizedResult(new QueryResult<BaseItemDto>
{
TotalRecordCount = result.TotalRecordCount,
Items = returnItems
});
}
/// <summary>
/// Applies the paging.
/// </summary>
/// <param name="items">The items.</param>
/// <param name="startIndex">The start index.</param>
/// <param name="limit">The limit.</param>
/// <returns>IEnumerable{BaseItem}.</returns>
private IEnumerable<BaseItem> ApplyPaging(IEnumerable<BaseItem> items, int? startIndex, int? limit)
{
// Start at
if (startIndex.HasValue)
{
items = items.Skip(startIndex.Value);
}
// Return limit
if (limit.HasValue)
{
items = items.Take(limit.Value);
}
return items;
}
public object Get(GetSeasons request)
{
var user = _userManager.GetUserById(request.UserId);
var series = GetSeries(request.Id);
if (series == null)
{
throw new ResourceNotFoundException("Series not found");
}
var seasons = series.GetItemList(new InternalItemsQuery(user)
{
IsMissing = request.IsMissing,
IsSpecialSeason = request.IsSpecialSeason,
AdjacentTo = request.AdjacentTo
});
var dtoOptions = GetDtoOptions(_authContext, request);
var returnItems = _dtoService.GetBaseItemDtos(seasons, dtoOptions, user);
return new QueryResult<BaseItemDto>
{
TotalRecordCount = returnItems.Count,
Items = returnItems
};
}
private Series GetSeries(string seriesId)
{
if (!string.IsNullOrWhiteSpace(seriesId))
{
return _libraryManager.GetItemById(seriesId) as Series;
}
return null;
}
public object Get(GetEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
List<BaseItem> episodes;
var dtoOptions = GetDtoOptions(_authContext, request);
if (!string.IsNullOrWhiteSpace(request.SeasonId))
{
if (!(_libraryManager.GetItemById(new Guid(request.SeasonId)) is Season season))
{
throw new ResourceNotFoundException("No season exists with Id " + request.SeasonId);
}
episodes = season.GetEpisodes(user, dtoOptions);
}
else if (request.Season.HasValue)
{
var series = GetSeries(request.Id);
if (series == null)
{
throw new ResourceNotFoundException("Series not found");
}
var season = series.GetSeasons(user, dtoOptions).FirstOrDefault(i => i.IndexNumber == request.Season.Value);
episodes = season == null ? new List<BaseItem>() : ((Season)season).GetEpisodes(user, dtoOptions);
}
else
{
var series = GetSeries(request.Id);
if (series == null)
{
throw new ResourceNotFoundException("Series not found");
}
episodes = series.GetEpisodes(user, dtoOptions).ToList();
}
// Filter after the fact in case the ui doesn't want them
if (request.IsMissing.HasValue)
{
var val = request.IsMissing.Value;
episodes = episodes.Where(i => ((Episode)i).IsMissingEpisode == val).ToList();
}
if (!string.IsNullOrWhiteSpace(request.StartItemId))
{
episodes = episodes.SkipWhile(i => !string.Equals(i.Id.ToString("N", CultureInfo.InvariantCulture), request.StartItemId, StringComparison.OrdinalIgnoreCase)).ToList();
}
// This must be the last filter
if (!string.IsNullOrEmpty(request.AdjacentTo))
{
episodes = UserViewBuilder.FilterForAdjacency(episodes, request.AdjacentTo).ToList();
}
if (string.Equals(request.SortBy, ItemSortBy.Random, StringComparison.OrdinalIgnoreCase))
{
episodes.Shuffle();
}
var returnItems = episodes;
if (request.StartIndex.HasValue || request.Limit.HasValue)
{
returnItems = ApplyPaging(episodes, request.StartIndex, request.Limit).ToList();
}
var dtos = _dtoService.GetBaseItemDtos(returnItems, dtoOptions, user);
return new QueryResult<BaseItemDto>
{
TotalRecordCount = episodes.Count,
Items = dtos
};
}
}
}

View file

@ -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();
}
}
}

View file

@ -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;
}
}
}

View file

@ -1,478 +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;
}
}
}

View file

@ -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();
}
}
}

View file

@ -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);
}
}
}

View file

@ -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()
};
}
}
}

View file

@ -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);
}
}
}

View file

@ -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();
}
}
}

View file

@ -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);
}
}
}

View file

@ -1,148 +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; }
}
}

View file

@ -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));
}
}
}

View file

@ -1,598 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Services;
using MediaBrowser.Model.Users;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
/// Class GetUsers.
/// </summary>
[Route("/Users", "GET", Summary = "Gets a list of users")]
[Authenticated]
public class GetUsers : IReturn<UserDto[]>
{
[ApiMember(Name = "IsHidden", Description = "Optional filter by IsHidden=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsHidden { get; set; }
[ApiMember(Name = "IsDisabled", Description = "Optional filter by IsDisabled=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsDisabled { get; set; }
[ApiMember(Name = "IsGuest", Description = "Optional filter by IsGuest=true or false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsGuest { get; set; }
}
[Route("/Users/Public", "GET", Summary = "Gets a list of publicly visible users for display on a login screen.")]
public class GetPublicUsers : IReturn<UserDto[]>
{
}
/// <summary>
/// Class GetUser.
/// </summary>
[Route("/Users/{Id}", "GET", Summary = "Gets a user by Id")]
[Authenticated(EscapeParentalControl = true)]
public class GetUser : IReturn<UserDto>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public Guid Id { get; set; }
}
/// <summary>
/// Class DeleteUser.
/// </summary>
[Route("/Users/{Id}", "DELETE", Summary = "Deletes a user")]
[Authenticated(Roles = "Admin")]
public class DeleteUser : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public Guid Id { get; set; }
}
/// <summary>
/// Class AuthenticateUser.
/// </summary>
[Route("/Users/{Id}/Authenticate", "POST", Summary = "Authenticates a user")]
public class AuthenticateUser : IReturn<AuthenticationResult>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public Guid Id { get; set; }
[ApiMember(Name = "Pw", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Pw { get; set; }
/// <summary>
/// Gets or sets the password.
/// </summary>
/// <value>The password.</value>
[ApiMember(Name = "Password", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Password { get; set; }
}
/// <summary>
/// Class AuthenticateUser.
/// </summary>
[Route("/Users/AuthenticateByName", "POST", Summary = "Authenticates a user")]
public class AuthenticateUserByName : IReturn<AuthenticationResult>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Username", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Username { get; set; }
/// <summary>
/// Gets or sets the password.
/// </summary>
/// <value>The password.</value>
[ApiMember(Name = "Password", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Password { get; set; }
[ApiMember(Name = "Pw", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Pw { get; set; }
}
/// <summary>
/// Class UpdateUserPassword.
/// </summary>
[Route("/Users/{Id}/Password", "POST", Summary = "Updates a user's password")]
[Authenticated]
public class UpdateUserPassword : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the password.
/// </summary>
/// <value>The password.</value>
public string CurrentPassword { get; set; }
public string CurrentPw { get; set; }
public string NewPw { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [reset password].
/// </summary>
/// <value><c>true</c> if [reset password]; otherwise, <c>false</c>.</value>
public bool ResetPassword { get; set; }
}
/// <summary>
/// Class UpdateUserEasyPassword.
/// </summary>
[Route("/Users/{Id}/EasyPassword", "POST", Summary = "Updates a user's easy password")]
[Authenticated]
public class UpdateUserEasyPassword : IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets the new password.
/// </summary>
/// <value>The new password.</value>
public string NewPassword { get; set; }
public string NewPw { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [reset password].
/// </summary>
/// <value><c>true</c> if [reset password]; otherwise, <c>false</c>.</value>
public bool ResetPassword { get; set; }
}
/// <summary>
/// Class UpdateUser.
/// </summary>
[Route("/Users/{Id}", "POST", Summary = "Updates a user")]
[Authenticated]
public class UpdateUser : UserDto, IReturnVoid
{
}
/// <summary>
/// Class UpdateUser.
/// </summary>
[Route("/Users/{Id}/Policy", "POST", Summary = "Updates a user policy")]
[Authenticated(Roles = "admin")]
public class UpdateUserPolicy : UserPolicy, IReturnVoid
{
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public Guid Id { get; set; }
}
/// <summary>
/// Class UpdateUser.
/// </summary>
[Route("/Users/{Id}/Configuration", "POST", Summary = "Updates a user configuration")]
[Authenticated]
public class UpdateUserConfiguration : UserConfiguration, IReturnVoid
{
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public Guid Id { get; set; }
}
/// <summary>
/// Class CreateUser.
/// </summary>
[Route("/Users/New", "POST", Summary = "Creates a user")]
[Authenticated(Roles = "Admin")]
public class CreateUserByName : IReturn<UserDto>
{
[ApiMember(Name = "Name", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Name { get; set; }
[ApiMember(Name = "Password", IsRequired = false, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Password { get; set; }
}
[Route("/Users/ForgotPassword", "POST", Summary = "Initiates the forgot password process for a local user")]
public class ForgotPassword : IReturn<ForgotPasswordResult>
{
[ApiMember(Name = "EnteredUsername", IsRequired = false, DataType = "string", ParameterType = "body", Verb = "POST")]
public string EnteredUsername { get; set; }
}
[Route("/Users/ForgotPassword/Pin", "POST", Summary = "Redeems a forgot password pin")]
public class ForgotPasswordPin : IReturn<PinRedeemResult>
{
[ApiMember(Name = "Pin", IsRequired = false, DataType = "string", ParameterType = "body", Verb = "POST")]
public string Pin { get; set; }
}
/// <summary>
/// Class UsersService.
/// </summary>
public class UserService : BaseApiService
{
/// <summary>
/// The user manager.
/// </summary>
private readonly IUserManager _userManager;
private readonly ISessionManager _sessionMananger;
private readonly INetworkManager _networkManager;
private readonly IDeviceManager _deviceManager;
private readonly IAuthorizationContext _authContext;
public UserService(
ILogger<UserService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
ISessionManager sessionMananger,
INetworkManager networkManager,
IDeviceManager deviceManager,
IAuthorizationContext authContext)
: base(logger, serverConfigurationManager, httpResultFactory)
{
_userManager = userManager;
_sessionMananger = sessionMananger;
_networkManager = networkManager;
_deviceManager = deviceManager;
_authContext = authContext;
}
public object Get(GetPublicUsers request)
{
// If the startup wizard hasn't been completed then just return all users
if (!ServerConfigurationManager.Configuration.IsStartupWizardCompleted)
{
return Get(new GetUsers
{
IsDisabled = false
});
}
return Get(new GetUsers
{
IsHidden = false,
IsDisabled = false
}, true, true);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetUsers request)
{
return Get(request, false, false);
}
private object Get(GetUsers request, bool filterByDevice, bool filterByNetwork)
{
var users = _userManager.Users;
if (request.IsDisabled.HasValue)
{
users = users.Where(i => i.HasPermission(PermissionKind.IsDisabled) == request.IsDisabled.Value);
}
if (request.IsHidden.HasValue)
{
users = users.Where(i => i.HasPermission(PermissionKind.IsHidden) == request.IsHidden.Value);
}
if (filterByDevice)
{
var deviceId = _authContext.GetAuthorizationInfo(Request).DeviceId;
if (!string.IsNullOrWhiteSpace(deviceId))
{
users = users.Where(i => _deviceManager.CanAccessDevice(i, deviceId));
}
}
if (filterByNetwork)
{
if (!_networkManager.IsInLocalNetwork(Request.RemoteIp))
{
users = users.Where(i => i.HasPermission(PermissionKind.EnableRemoteAccess));
}
}
var result = users
.OrderBy(u => u.Username)
.Select(i => _userManager.GetUserDto(i, Request.RemoteIp))
.ToArray();
return ToOptimizedResult(result);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetUser request)
{
var user = _userManager.GetUserById(request.Id);
if (user == null)
{
throw new ResourceNotFoundException("User not found");
}
var result = _userManager.GetUserDto(user, Request.RemoteIp);
return ToOptimizedResult(result);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Delete(DeleteUser request)
{
return DeleteAsync(request);
}
public Task DeleteAsync(DeleteUser request)
{
_userManager.DeleteUser(request.Id);
_sessionMananger.RevokeUserTokens(request.Id, null);
return Task.CompletedTask;
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public object Post(AuthenticateUser request)
{
var user = _userManager.GetUserById(request.Id);
if (user == null)
{
throw new ResourceNotFoundException("User not found");
}
if (!string.IsNullOrEmpty(request.Password) && string.IsNullOrEmpty(request.Pw))
{
throw new MethodNotAllowedException("Hashed-only passwords are not valid for this API.");
}
// Password should always be null
return Post(new AuthenticateUserByName
{
Username = user.Username,
Password = null,
Pw = request.Pw
});
}
public async Task<object> Post(AuthenticateUserByName request)
{
var auth = _authContext.GetAuthorizationInfo(Request);
try
{
var result = await _sessionMananger.AuthenticateNewSession(new AuthenticationRequest
{
App = auth.Client,
AppVersion = auth.Version,
DeviceId = auth.DeviceId,
DeviceName = auth.Device,
Password = request.Pw,
PasswordSha1 = request.Password,
RemoteEndPoint = Request.RemoteIp,
Username = request.Username
}).ConfigureAwait(false);
return ToOptimizedResult(result);
}
catch (SecurityException e)
{
// rethrow adding IP address to message
throw new SecurityException($"[{Request.RemoteIp}] {e.Message}", e);
}
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public Task Post(UpdateUserPassword request)
{
return PostAsync(request);
}
public async Task PostAsync(UpdateUserPassword request)
{
AssertCanUpdateUser(_authContext, _userManager, request.Id, true);
var user = _userManager.GetUserById(request.Id);
if (user == null)
{
throw new ResourceNotFoundException("User not found");
}
if (request.ResetPassword)
{
await _userManager.ResetPassword(user).ConfigureAwait(false);
}
else
{
var success = await _userManager.AuthenticateUser(
user.Username,
request.CurrentPw,
request.CurrentPassword,
Request.RemoteIp,
false).ConfigureAwait(false);
if (success == null)
{
throw new ArgumentException("Invalid user or password entered.");
}
await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false);
var currentToken = _authContext.GetAuthorizationInfo(Request).Token;
_sessionMananger.RevokeUserTokens(user.Id, currentToken);
}
}
public void Post(UpdateUserEasyPassword request)
{
AssertCanUpdateUser(_authContext, _userManager, request.Id, true);
var user = _userManager.GetUserById(request.Id);
if (user == null)
{
throw new ResourceNotFoundException("User not found");
}
if (request.ResetPassword)
{
_userManager.ResetEasyPassword(user);
}
else
{
_userManager.ChangeEasyPassword(user, request.NewPw, request.NewPassword);
}
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public async Task Post(UpdateUser request)
{
var id = Guid.Parse(GetPathValue(1));
AssertCanUpdateUser(_authContext, _userManager, id, false);
var dtoUser = request;
var user = _userManager.GetUserById(id);
if (string.Equals(user.Username, dtoUser.Name, StringComparison.Ordinal))
{
await _userManager.UpdateUserAsync(user);
_userManager.UpdateConfiguration(user.Id, dtoUser.Configuration);
}
else
{
await _userManager.RenameUser(user, dtoUser.Name).ConfigureAwait(false);
_userManager.UpdateConfiguration(dtoUser.Id, dtoUser.Configuration);
}
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public async Task<object> Post(CreateUserByName request)
{
var newUser = _userManager.CreateUser(request.Name);
// no need to authenticate password for new user
if (request.Password != null)
{
await _userManager.ChangePassword(newUser, request.Password).ConfigureAwait(false);
}
var result = _userManager.GetUserDto(newUser, Request.RemoteIp);
return ToOptimizedResult(result);
}
public async Task<object> Post(ForgotPassword request)
{
var isLocal = Request.IsLocal || _networkManager.IsInLocalNetwork(Request.RemoteIp);
var result = await _userManager.StartForgotPasswordProcess(request.EnteredUsername, isLocal).ConfigureAwait(false);
return result;
}
public async Task<object> Post(ForgotPasswordPin request)
{
var result = await _userManager.RedeemPasswordResetPin(request.Pin).ConfigureAwait(false);
return result;
}
public void Post(UpdateUserConfiguration request)
{
AssertCanUpdateUser(_authContext, _userManager, request.Id, false);
_userManager.UpdateConfiguration(request.Id, request);
}
public void Post(UpdateUserPolicy request)
{
var user = _userManager.GetUserById(request.Id);
// If removing admin access
if (!request.IsAdministrator && user.HasPermission(PermissionKind.IsAdministrator))
{
if (_userManager.Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1)
{
throw new ArgumentException("There must be at least one user in the system with administrative access.");
}
}
// If disabling
if (request.IsDisabled && user.HasPermission(PermissionKind.IsAdministrator))
{
throw new ArgumentException("Administrators cannot be disabled.");
}
// If disabling
if (request.IsDisabled && !user.HasPermission(PermissionKind.IsDisabled))
{
if (_userManager.Users.Count(i => !i.HasPermission(PermissionKind.IsDisabled)) == 1)
{
throw new ArgumentException("There must be at least one enabled user in the system.");
}
var currentToken = _authContext.GetAuthorizationInfo(Request).Token;
_sessionMananger.RevokeUserTokens(user.Id, currentToken);
}
_userManager.UpdatePolicy(request.Id, request);
}
}
}