commit
b2e7a4a1cb
@ -0,0 +1,191 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Jellyfin.Api.Constants;
|
||||
using Jellyfin.Api.Models.EnvironmentDtos;
|
||||
using MediaBrowser.Model.IO;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Jellyfin.Api.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Environment Controller.
|
||||
/// </summary>
|
||||
[Authorize(Policy = Policies.RequiresElevation)]
|
||||
public class EnvironmentController : BaseJellyfinApiController
|
||||
{
|
||||
private const char UncSeparator = '\\';
|
||||
private const string UncStartPrefix = @"\\";
|
||||
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly ILogger<EnvironmentController> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EnvironmentController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
|
||||
/// <param name="logger">Instance of the <see cref="ILogger{EnvironmentController}"/> interface.</param>
|
||||
public EnvironmentController(IFileSystem fileSystem, ILogger<EnvironmentController> logger)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contents of a given directory in the file system.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="includeFiles">An optional filter to include or exclude files from the results. true/false.</param>
|
||||
/// <param name="includeDirectories">An optional filter to include or exclude folders from the results. true/false.</param>
|
||||
/// <response code="200">Directory contents returned.</response>
|
||||
/// <returns>Directory contents.</returns>
|
||||
[HttpGet("DirectoryContents")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public IEnumerable<FileSystemEntryInfo> GetDirectoryContents(
|
||||
[FromQuery, BindRequired] string path,
|
||||
[FromQuery] bool includeFiles = false,
|
||||
[FromQuery] bool includeDirectories = false)
|
||||
{
|
||||
if (path.StartsWith(UncStartPrefix, StringComparison.OrdinalIgnoreCase)
|
||||
&& path.LastIndexOf(UncSeparator) == 1)
|
||||
{
|
||||
return Array.Empty<FileSystemEntryInfo>();
|
||||
}
|
||||
|
||||
var entries =
|
||||
_fileSystem.GetFileSystemEntries(path)
|
||||
.Where(i => (i.IsDirectory && includeDirectories) || (!i.IsDirectory && includeFiles))
|
||||
.OrderBy(i => i.FullName);
|
||||
|
||||
return entries.Select(f => new FileSystemEntryInfo(f.Name, f.FullName, f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates path.
|
||||
/// </summary>
|
||||
/// <param name="validatePathDto">Validate request object.</param>
|
||||
/// <response code="200">Path validated.</response>
|
||||
/// <response code="404">Path not found.</response>
|
||||
/// <returns>Validation status.</returns>
|
||||
[HttpPost("ValidatePath")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult ValidatePath([FromBody, BindRequired] ValidatePathDto validatePathDto)
|
||||
{
|
||||
if (validatePathDto.IsFile.HasValue)
|
||||
{
|
||||
if (validatePathDto.IsFile.Value)
|
||||
{
|
||||
if (!System.IO.File.Exists(validatePathDto.Path))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Directory.Exists(validatePathDto.Path))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!System.IO.File.Exists(validatePathDto.Path) && !Directory.Exists(validatePathDto.Path))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (validatePathDto.ValidateWritable)
|
||||
{
|
||||
var file = Path.Combine(validatePathDto.Path, Guid.NewGuid().ToString());
|
||||
try
|
||||
{
|
||||
System.IO.File.WriteAllText(file, string.Empty);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (System.IO.File.Exists(file))
|
||||
{
|
||||
System.IO.File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets network paths.
|
||||
/// </summary>
|
||||
/// <response code="200">Empty array returned.</response>
|
||||
/// <returns>List of entries.</returns>
|
||||
[Obsolete("This endpoint is obsolete.")]
|
||||
[HttpGet("NetworkShares")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<IEnumerable<FileSystemEntryInfo>> GetNetworkShares()
|
||||
{
|
||||
_logger.LogWarning("Obsolete endpoint accessed: /Environment/NetworkShares");
|
||||
return Array.Empty<FileSystemEntryInfo>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets available drives from the server's file system.
|
||||
/// </summary>
|
||||
/// <response code="200">List of entries returned.</response>
|
||||
/// <returns>List of entries.</returns>
|
||||
[HttpGet("Drives")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public IEnumerable<FileSystemEntryInfo> GetDrives()
|
||||
{
|
||||
return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo(d.Name, d.FullName, FileSystemEntryType.Directory));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent path of a given path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>Parent path.</returns>
|
||||
[HttpGet("ParentPath")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<string?> GetParentPath([FromQuery, BindRequired] string path)
|
||||
{
|
||||
string? parent = Path.GetDirectoryName(path);
|
||||
if (string.IsNullOrEmpty(parent))
|
||||
{
|
||||
// Check if unc share
|
||||
var index = path.LastIndexOf(UncSeparator);
|
||||
|
||||
if (index != -1 && path.IndexOf(UncSeparator, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
parent = path.Substring(0, index);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(parent.TrimStart(UncSeparator)))
|
||||
{
|
||||
parent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Default directory browser.
|
||||
/// </summary>
|
||||
/// <response code="200">Default directory browser returned.</response>
|
||||
/// <returns>Default directory browser.</returns>
|
||||
[HttpGet("DefaultDirectoryBrowser")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public ActionResult<DefaultDirectoryBrowserInfoDto> GetDefaultDirectoryBrowser()
|
||||
{
|
||||
return new DefaultDirectoryBrowserInfoDto();
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Jellyfin.Api.Constants;
|
||||
using MediaBrowser.Model.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Jellyfin.Api.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Scheduled Tasks Controller.
|
||||
/// </summary>
|
||||
[Authorize(Policy = Policies.RequiresElevation)]
|
||||
public class ScheduledTasksController : BaseJellyfinApiController
|
||||
{
|
||||
private readonly ITaskManager _taskManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScheduledTasksController"/> class.
|
||||
/// </summary>
|
||||
/// <param name="taskManager">Instance of the <see cref="ITaskManager"/> interface.</param>
|
||||
public ScheduledTasksController(ITaskManager taskManager)
|
||||
{
|
||||
_taskManager = taskManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get tasks.
|
||||
/// </summary>
|
||||
/// <param name="isHidden">Optional filter tasks that are hidden, or not.</param>
|
||||
/// <param name="isEnabled">Optional filter tasks that are enabled, or not.</param>
|
||||
/// <response code="200">Scheduled tasks retrieved.</response>
|
||||
/// <returns>The list of scheduled tasks.</returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public IEnumerable<IScheduledTaskWorker> GetTasks(
|
||||
[FromQuery] bool? isHidden,
|
||||
[FromQuery] bool? isEnabled)
|
||||
{
|
||||
IEnumerable<IScheduledTaskWorker> tasks = _taskManager.ScheduledTasks.OrderBy(o => o.Name);
|
||||
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
if (task.ScheduledTask is IConfigurableScheduledTask scheduledTask)
|
||||
{
|
||||
if (isHidden.HasValue && isHidden.Value != scheduledTask.IsHidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isEnabled.HasValue && isEnabled.Value != scheduledTask.IsEnabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
yield return task;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get task by id.
|
||||
/// </summary>
|
||||
/// <param name="taskId">Task Id.</param>
|
||||
/// <response code="200">Task retrieved.</response>
|
||||
/// <response code="404">Task not found.</response>
|
||||
/// <returns>An <see cref="OkResult"/> containing the task on success, or a <see cref="NotFoundResult"/> if the task could not be found.</returns>
|
||||
[HttpGet("{taskId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult<TaskInfo> GetTask([FromRoute] string? taskId)
|
||||
{
|
||||
var task = _taskManager.ScheduledTasks.FirstOrDefault(i =>
|
||||
string.Equals(i.Id, taskId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (task == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return ScheduledTaskHelpers.GetTaskInfo(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start specified task.
|
||||
/// </summary>
|
||||
/// <param name="taskId">Task Id.</param>
|
||||
/// <response code="204">Task started.</response>
|
||||
/// <response code="404">Task not found.</response>
|
||||
/// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the file could not be found.</returns>
|
||||
[HttpPost("Running/{taskId}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult StartTask([FromRoute] string? taskId)
|
||||
{
|
||||
var task = _taskManager.ScheduledTasks.FirstOrDefault(o =>
|
||||
o.Id.Equals(taskId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (task == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_taskManager.Execute(task, new TaskOptions());
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop specified task.
|
||||
/// </summary>
|
||||
/// <param name="taskId">Task Id.</param>
|
||||
/// <response code="204">Task stopped.</response>
|
||||
/// <response code="404">Task not found.</response>
|
||||
/// <returns>An <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if the file could not be found.</returns>
|
||||
[HttpDelete("Running/{taskId}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult StopTask([FromRoute] string? taskId)
|
||||
{
|
||||
var task = _taskManager.ScheduledTasks.FirstOrDefault(o =>
|
||||
o.Id.Equals(taskId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (task == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
_taskManager.Cancel(task);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update specified task triggers.
|
||||
/// </summary>
|
||||
/// <param name="taskId">Task Id.</param>
|
||||
/// <param name="triggerInfos">Triggers.</param>
|
||||
/// <response code="204">Task triggers updated.</response>
|
||||
/// <response code="404">Task not found.</response>
|
||||
/// <returns>An <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if the file could not be found.</returns>
|
||||
[HttpPost("{taskId}/Triggers")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public ActionResult UpdateTask(
|
||||
[FromRoute] string? taskId,
|
||||
[FromBody, BindRequired] TaskTriggerInfo[] triggerInfos)
|
||||
{
|
||||
var task = _taskManager.ScheduledTasks.FirstOrDefault(o =>
|
||||
o.Id.Equals(taskId, StringComparison.OrdinalIgnoreCase));
|
||||
if (task == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
task.Triggers = triggerInfos;
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
||||
namespace Jellyfin.Api.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Progressive file copier.
|
||||
/// </summary>
|
||||
public class ProgressiveFileCopier
|
||||
{
|
||||
private readonly string? _path;
|
||||
private readonly IDirectStreamProvider? _directStreamProvider;
|
||||
private readonly IStreamHelper _streamHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProgressiveFileCopier"/> class.
|
||||
/// </summary>
|
||||
/// <param name="streamHelper">Instance of the <see cref="IStreamHelper"/> interface.</param>
|
||||
/// <param name="path">Filepath to stream from.</param>
|
||||
public ProgressiveFileCopier(IStreamHelper streamHelper, string path)
|
||||
{
|
||||
_path = path;
|
||||
_streamHelper = streamHelper;
|
||||
_directStreamProvider = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProgressiveFileCopier"/> class.
|
||||
/// </summary>
|
||||
/// <param name="streamHelper">Instance of the <see cref="IStreamHelper"/> interface.</param>
|
||||
/// <param name="directStreamProvider">Instance of the <see cref="IDirectStreamProvider"/> interface.</param>
|
||||
public ProgressiveFileCopier(IStreamHelper streamHelper, IDirectStreamProvider directStreamProvider)
|
||||
{
|
||||
_directStreamProvider = directStreamProvider;
|
||||
_streamHelper = streamHelper;
|
||||
_path = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write source stream to output.
|
||||
/// </summary>
|
||||
/// <param name="outputStream">Output stream.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A <see cref="Task"/>.</returns>
|
||||
public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_directStreamProvider != null)
|
||||
{
|
||||
await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var fileOptions = FileOptions.SequentialScan;
|
||||
|
||||
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
||||
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
||||
{
|
||||
fileOptions |= FileOptions.Asynchronous;
|
||||
}
|
||||
|
||||
await using var inputStream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, fileOptions);
|
||||
const int emptyReadLimit = 100;
|
||||
var eofCount = 0;
|
||||
while (eofCount < emptyReadLimit)
|
||||
{
|
||||
var bytesRead = await _streamHelper.CopyToAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
eofCount++;
|
||||
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
eofCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
namespace Jellyfin.Api.Models.EnvironmentDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Default directory browser info.
|
||||
/// </summary>
|
||||
public class DefaultDirectoryBrowserInfoDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
public string? Path { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
namespace Jellyfin.Api.Models.EnvironmentDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// Validate path object.
|
||||
/// </summary>
|
||||
public class ValidatePathDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether validate if path is writable.
|
||||
/// </summary>
|
||||
public bool ValidateWritable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path.
|
||||
/// </summary>
|
||||
public string? Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets is path file.
|
||||
/// </summary>
|
||||
public bool? IsFile { get; set; }
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,80 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.LiveTv
|
||||
{
|
||||
public class ProgressiveFileCopier : IAsyncStreamWriter, IHasHeaders
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly string _path;
|
||||
private readonly Dictionary<string, string> _outputHeaders;
|
||||
|
||||
public bool AllowEndOfFile = true;
|
||||
|
||||
private readonly IDirectStreamProvider _directStreamProvider;
|
||||
private IStreamHelper _streamHelper;
|
||||
|
||||
public ProgressiveFileCopier(IStreamHelper streamHelper, string path, Dictionary<string, string> outputHeaders, ILogger logger)
|
||||
{
|
||||
_path = path;
|
||||
_outputHeaders = outputHeaders;
|
||||
_logger = logger;
|
||||
_streamHelper = streamHelper;
|
||||
}
|
||||
|
||||
public ProgressiveFileCopier(IDirectStreamProvider directStreamProvider, IStreamHelper streamHelper, Dictionary<string, string> outputHeaders, ILogger logger)
|
||||
{
|
||||
_directStreamProvider = directStreamProvider;
|
||||
_outputHeaders = outputHeaders;
|
||||
_logger = logger;
|
||||
_streamHelper = streamHelper;
|
||||
}
|
||||
|
||||
public IDictionary<string, string> Headers => _outputHeaders;
|
||||
|
||||
public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_directStreamProvider != null)
|
||||
{
|
||||
await _directStreamProvider.CopyToAsync(outputStream, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var fileOptions = FileOptions.SequentialScan;
|
||||
|
||||
// use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
|
||||
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
|
||||
{
|
||||
fileOptions |= FileOptions.Asynchronous;
|
||||
}
|
||||
|
||||
using (var inputStream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, fileOptions))
|
||||
{
|
||||
var emptyReadLimit = AllowEndOfFile ? 20 : 100;
|
||||
var eofCount = 0;
|
||||
while (eofCount < emptyReadLimit)
|
||||
{
|
||||
int bytesRead;
|
||||
bytesRead = await _streamHelper.CopyToAsync(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
eofCount++;
|
||||
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
eofCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,105 +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, ','));
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Jellyfin.Data.Entities;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Playlists;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.Music
|
||||
{
|
||||
[Route("/Songs/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given song")]
|
||||
public class GetInstantMixFromSong : BaseGetSimilarItemsFromItem
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Albums/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given album")]
|
||||
public class GetInstantMixFromAlbum : BaseGetSimilarItemsFromItem
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Playlists/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given playlist")]
|
||||
public class GetInstantMixFromPlaylist : BaseGetSimilarItemsFromItem
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/MusicGenres/{Name}/InstantMix", "GET", Summary = "Creates an instant playlist based on a music genre")]
|
||||
public class GetInstantMixFromMusicGenre : BaseGetSimilarItems
|
||||
{
|
||||
[ApiMember(Name = "Name", Description = "The genre name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Artists/InstantMix", "GET", Summary = "Creates an instant playlist based on a given artist")]
|
||||
public class GetInstantMixFromArtistId : BaseGetSimilarItems
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "The artist Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/MusicGenres/InstantMix", "GET", Summary = "Creates an instant playlist based on a music genre")]
|
||||
public class GetInstantMixFromMusicGenreId : BaseGetSimilarItems
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "The genre Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Items/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given item")]
|
||||
public class GetInstantMixFromItem : BaseGetSimilarItemsFromItem
|
||||
{
|
||||
}
|
||||
|
||||
[Authenticated]
|
||||
public class InstantMixService : BaseApiService
|
||||
{
|
||||
private readonly IUserManager _userManager;
|
||||
|
||||
private readonly IDtoService _dtoService;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IMusicManager _musicManager;
|
||||
private readonly IAuthorizationContext _authContext;
|
||||
|
||||
public InstantMixService(
|
||||
ILogger<InstantMixService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
IDtoService dtoService,
|
||||
IMusicManager musicManager,
|
||||
ILibraryManager libraryManager,
|
||||
IAuthorizationContext authContext)
|
||||
: base(logger, serverConfigurationManager, httpResultFactory)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_dtoService = dtoService;
|
||||
_musicManager = musicManager;
|
||||
_libraryManager = libraryManager;
|
||||
_authContext = authContext;
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromItem request)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(request.Id);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
|
||||
|
||||
return GetResult(items, user, request, dtoOptions);
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromArtistId request)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(request.Id);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
|
||||
|
||||
return GetResult(items, user, request, dtoOptions);
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromMusicGenreId request)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(request.Id);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
|
||||
|
||||
return GetResult(items, user, request, dtoOptions);
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromSong request)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(request.Id);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
|
||||
|
||||
return GetResult(items, user, request, dtoOptions);
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromAlbum request)
|
||||
{
|
||||
var album = _libraryManager.GetItemById(request.Id);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var items = _musicManager.GetInstantMixFromItem(album, user, dtoOptions);
|
||||
|
||||
return GetResult(items, user, request, dtoOptions);
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromPlaylist request)
|
||||
{
|
||||
var playlist = (Playlist)_libraryManager.GetItemById(request.Id);
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var items = _musicManager.GetInstantMixFromItem(playlist, user, dtoOptions);
|
||||
|
||||
return GetResult(items, user, request, dtoOptions);
|
||||
}
|
||||
|
||||
public object Get(GetInstantMixFromMusicGenre request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var items = _musicManager.GetInstantMixFromGenres(new[] { request.Name }, user, dtoOptions);
|
||||
|
||||
return GetResult(items, user, request, dtoOptions);
|
||||
}
|
||||
|
||||
private object GetResult(List<BaseItem> items, User user, BaseGetSimilarItems request, DtoOptions dtoOptions)
|
||||
{
|
||||
var list = items;
|
||||
|
||||
var result = new QueryResult<BaseItemDto>
|
||||
{
|
||||
TotalRecordCount = list.Count
|
||||
};
|
||||
|
||||
if (request.Limit.HasValue)
|
||||
{
|
||||
list = list.Take(request.Limit.Value).ToList();
|
||||
}
|
||||
|
||||
var returnList = _dtoService.GetBaseItemDtos(list, dtoOptions, user);
|
||||
|
||||
result.Items = returnList;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetArtists
|
||||
/// </summary>
|
||||
[Route("/Artists", "GET", Summary = "Gets all artists from a given item, folder, or the entire library")]
|
||||
public class GetArtists : GetItemsByName
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Artists/AlbumArtists", "GET", Summary = "Gets all album artists from a given item, folder, or the entire library")]
|
||||
public class GetAlbumArtists : GetItemsByName
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Artists/{Name}", "GET", Summary = "Gets an artist, by name")]
|
||||
public class GetArtist : IReturn<BaseItemDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
[ApiMember(Name = "Name", Description = "The artist name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ArtistsService
|
||||
/// </summary>
|
||||
[Authenticated]
|
||||
public class ArtistsService : BaseItemsByNameService<MusicArtist>
|
||||
{
|
||||
public ArtistsService(
|
||||
ILogger<ArtistsService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
ILibraryManager libraryManager,
|
||||
IUserDataManager userDataRepository,
|
||||
IDtoService dtoService,
|
||||
IAuthorizationContext authorizationContext)
|
||||
: base(
|
||||
logger,
|
||||
serverConfigurationManager,
|
||||
httpResultFactory,
|
||||
userManager,
|
||||
libraryManager,
|
||||
userDataRepository,
|
||||
dtoService,
|
||||
authorizationContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetArtist request)
|
||||
{
|
||||
return GetItem(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>Task{BaseItemDto}.</returns>
|
||||
private BaseItemDto GetItem(GetArtist request)
|
||||
{
|
||||
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
|
||||
|
||||
var item = GetArtist(request.Name, LibraryManager, dtoOptions);
|
||||
|
||||
if (!request.UserId.Equals(Guid.Empty))
|
||||
{
|
||||
var user = UserManager.GetUserById(request.UserId);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, dtoOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetArtists request)
|
||||
{
|
||||
return GetResultSlim(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetAlbumArtists request)
|
||||
{
|
||||
var result = GetResultSlim(request);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
|
||||
{
|
||||
return request is GetAlbumArtists ? LibraryManager.GetAlbumArtists(query) : LibraryManager.GetArtists(query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetPersons
|
||||
/// </summary>
|
||||
[Route("/Persons", "GET", Summary = "Gets all persons from a given item, folder, or the entire library")]
|
||||
public class GetPersons : GetItemsByName
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetPerson
|
||||
/// </summary>
|
||||
[Route("/Persons/{Name}", "GET", Summary = "Gets a person, by name")]
|
||||
public class GetPerson : IReturn<BaseItemDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
[ApiMember(Name = "Name", Description = "The person name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class PersonsService
|
||||
/// </summary>
|
||||
[Authenticated]
|
||||
public class PersonsService : BaseItemsByNameService<Person>
|
||||
{
|
||||
public PersonsService(
|
||||
ILogger<PersonsService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
ILibraryManager libraryManager,
|
||||
IUserDataManager userDataRepository,
|
||||
IDtoService dtoService,
|
||||
IAuthorizationContext authorizationContext)
|
||||
: base(
|
||||
logger,
|
||||
serverConfigurationManager,
|
||||
httpResultFactory,
|
||||
userManager,
|
||||
libraryManager,
|
||||
userDataRepository,
|
||||
dtoService,
|
||||
authorizationContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPerson request)
|
||||
{
|
||||
var result = GetItem(request);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>Task{BaseItemDto}.</returns>
|
||||
private BaseItemDto GetItem(GetPerson request)
|
||||
{
|
||||
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
|
||||
|
||||
var item = GetPerson(request.Name, LibraryManager, dtoOptions);
|
||||
|
||||
if (!request.UserId.Equals(Guid.Empty))
|
||||
{
|
||||
var user = UserManager.GetUserById(request.UserId);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, dtoOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPersons request)
|
||||
{
|
||||
return GetResultSlim(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
|
||||
{
|
||||
var items = LibraryManager.GetPeopleItems(new InternalPeopleQuery
|
||||
{
|
||||
PersonTypes = query.PersonTypes,
|
||||
NameContains = query.NameContains ?? query.SearchTerm
|
||||
});
|
||||
|
||||
if ((query.IsFavorite ?? false) && query.User != null)
|
||||
{
|
||||
items = items.Where(i => UserDataRepository.GetUserData(query.User, i).IsFavorite).ToList();
|
||||
}
|
||||
|
||||
return new QueryResult<(BaseItem, ItemCounts)>
|
||||
{
|
||||
TotalRecordCount = items.Count,
|
||||
Items = items.Take(query.Limit ?? int.MaxValue).Select(i => (i as BaseItem, new ItemCounts())).ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,456 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Data.Entities;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Services;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MarkPlayedItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/PlayedItems/{Id}", "POST", Summary = "Marks an item as played")]
|
||||
public class MarkPlayedItem : IReturn<UserItemDataDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
[ApiMember(Name = "DatePlayed", Description = "The date the item was played (if any). Format = yyyyMMddHHmmss", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string DatePlayed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class MarkUnplayedItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE", Summary = "Marks an item as unplayed")]
|
||||
public class MarkUnplayedItem : IReturn<UserItemDataDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Sessions/Playing", "POST", Summary = "Reports playback has started within a session")]
|
||||
public class ReportPlaybackStart : PlaybackStartInfo, IReturnVoid
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Sessions/Playing/Progress", "POST", Summary = "Reports playback progress within a session")]
|
||||
public class ReportPlaybackProgress : PlaybackProgressInfo, IReturnVoid
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Sessions/Playing/Ping", "POST", Summary = "Pings a playback session")]
|
||||
public class PingPlaybackSession : IReturnVoid
|
||||
{
|
||||
[ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string PlaySessionId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Sessions/Playing/Stopped", "POST", Summary = "Reports playback has stopped within a session")]
|
||||
public class ReportPlaybackStopped : PlaybackStopInfo, IReturnVoid
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class OnPlaybackStart
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/PlayingItems/{Id}", "POST", Summary = "Reports that a user has begun playing an item")]
|
||||
public class OnPlaybackStart : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
[ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
|
||||
public bool CanSeek { get; set; }
|
||||
|
||||
[ApiMember(Name = "AudioStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
||||
public int? AudioStreamIndex { get; set; }
|
||||
|
||||
[ApiMember(Name = "SubtitleStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
[ApiMember(Name = "PlayMethod", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public PlayMethod PlayMethod { get; set; }
|
||||
|
||||
[ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string LiveStreamId { get; set; }
|
||||
|
||||
[ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string PlaySessionId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class OnPlaybackProgress
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/PlayingItems/{Id}/Progress", "POST", Summary = "Reports a user's playback progress")]
|
||||
public class OnPlaybackProgress : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position ticks.
|
||||
/// </summary>
|
||||
/// <value>The position ticks.</value>
|
||||
[ApiMember(Name = "PositionTicks", Description = "Optional. The current position, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
||||
public long? PositionTicks { get; set; }
|
||||
|
||||
[ApiMember(Name = "IsPaused", Description = "Indicates if the player is paused.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
|
||||
public bool IsPaused { get; set; }
|
||||
|
||||
[ApiMember(Name = "IsMuted", Description = "Indicates if the player is muted.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
|
||||
public bool IsMuted { get; set; }
|
||||
|
||||
[ApiMember(Name = "AudioStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
||||
public int? AudioStreamIndex { get; set; }
|
||||
|
||||
[ApiMember(Name = "SubtitleStreamIndex", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
||||
public int? SubtitleStreamIndex { get; set; }
|
||||
|
||||
[ApiMember(Name = "VolumeLevel", Description = "Scale of 0-100", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "POST")]
|
||||
public int? VolumeLevel { get; set; }
|
||||
|
||||
[ApiMember(Name = "PlayMethod", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public PlayMethod PlayMethod { get; set; }
|
||||
|
||||
[ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string LiveStreamId { get; set; }
|
||||
|
||||
[ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string PlaySessionId { get; set; }
|
||||
|
||||
[ApiMember(Name = "RepeatMode", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public RepeatMode RepeatMode { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class OnPlaybackStopped
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/PlayingItems/{Id}", "DELETE", Summary = "Reports that a user has stopped playing an item")]
|
||||
public class OnPlaybackStopped : IReturnVoid
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
[ApiMember(Name = "NextMediaType", Description = "The next media type that will play", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
|
||||
public string NextMediaType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position ticks.
|
||||
/// </summary>
|
||||
/// <value>The position ticks.</value>
|
||||
[ApiMember(Name = "PositionTicks", Description = "Optional. The position, in ticks, where playback stopped. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "DELETE")]
|
||||
public long? PositionTicks { get; set; }
|
||||
|
||||
[ApiMember(Name = "LiveStreamId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string LiveStreamId { get; set; }
|
||||
|
||||
[ApiMember(Name = "PlaySessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
|
||||
public string PlaySessionId { get; set; }
|
||||
}
|
||||
|
||||
[Authenticated]
|
||||
public class PlaystateService : BaseApiService
|
||||
{
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly IUserDataManager _userDataRepository;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
private readonly ISessionContext _sessionContext;
|
||||
private readonly IAuthorizationContext _authContext;
|
||||
|
||||
public PlaystateService(
|
||||
ILogger<PlaystateService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
IUserDataManager userDataRepository,
|
||||
ILibraryManager libraryManager,
|
||||
ISessionManager sessionManager,
|
||||
ISessionContext sessionContext,
|
||||
IAuthorizationContext authContext)
|
||||
: base(logger, serverConfigurationManager, httpResultFactory)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_userDataRepository = userDataRepository;
|
||||
_libraryManager = libraryManager;
|
||||
_sessionManager = sessionManager;
|
||||
_sessionContext = sessionContext;
|
||||
_authContext = authContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public object Post(MarkPlayedItem request)
|
||||
{
|
||||
var result = MarkPlayed(request);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
private UserItemDataDto MarkPlayed(MarkPlayedItem request)
|
||||
{
|
||||
var user = _userManager.GetUserById(Guid.Parse(request.UserId));
|
||||
|
||||
DateTime? datePlayed = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(request.DatePlayed))
|
||||
{
|
||||
datePlayed = DateTime.ParseExact(request.DatePlayed, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
|
||||
}
|
||||
|
||||
var session = GetSession(_sessionContext);
|
||||
|
||||
var dto = UpdatePlayedStatus(user, request.Id, true, datePlayed);
|
||||
|
||||
foreach (var additionalUserInfo in session.AdditionalUsers)
|
||||
{
|
||||
var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
|
||||
|
||||
UpdatePlayedStatus(additionalUser, request.Id, true, datePlayed);
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
private PlayMethod ValidatePlayMethod(PlayMethod method, string playSessionId)
|
||||
{
|
||||
if (method == PlayMethod.Transcode)
|
||||
{
|
||||
var job = string.IsNullOrWhiteSpace(playSessionId) ? null : ApiEntryPoint.Instance.GetTranscodingJob(playSessionId);
|
||||
if (job == null)
|
||||
{
|
||||
return PlayMethod.DirectPlay;
|
||||
}
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(OnPlaybackStart request)
|
||||
{
|
||||
Post(new ReportPlaybackStart
|
||||
{
|
||||
CanSeek = request.CanSeek,
|
||||
ItemId = new Guid(request.Id),
|
||||
MediaSourceId = request.MediaSourceId,
|
||||
AudioStreamIndex = request.AudioStreamIndex,
|
||||
SubtitleStreamIndex = request.SubtitleStreamIndex,
|
||||
PlayMethod = request.PlayMethod,
|
||||
PlaySessionId = request.PlaySessionId,
|
||||
LiveStreamId = request.LiveStreamId
|
||||
});
|
||||
}
|
||||
|
||||
public void Post(ReportPlaybackStart request)
|
||||
{
|
||||
request.PlayMethod = ValidatePlayMethod(request.PlayMethod, request.PlaySessionId);
|
||||
|
||||
request.SessionId = GetSession(_sessionContext).Id;
|
||||
|
||||
var task = _sessionManager.OnPlaybackStart(request);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public void Post(OnPlaybackProgress request)
|
||||
{
|
||||
Post(new ReportPlaybackProgress
|
||||
{
|
||||
ItemId = new Guid(request.Id),
|
||||
PositionTicks = request.PositionTicks,
|
||||
IsMuted = request.IsMuted,
|
||||
IsPaused = request.IsPaused,
|
||||
MediaSourceId = request.MediaSourceId,
|
||||
AudioStreamIndex = request.AudioStreamIndex,
|
||||
SubtitleStreamIndex = request.SubtitleStreamIndex,
|
||||
VolumeLevel = request.VolumeLevel,
|
||||
PlayMethod = request.PlayMethod,
|
||||
PlaySessionId = request.PlaySessionId,
|
||||
LiveStreamId = request.LiveStreamId,
|
||||
RepeatMode = request.RepeatMode
|
||||
});
|
||||
}
|
||||
|
||||
public void Post(ReportPlaybackProgress request)
|
||||
{
|
||||
request.PlayMethod = ValidatePlayMethod(request.PlayMethod, request.PlaySessionId);
|
||||
|
||||
request.SessionId = GetSession(_sessionContext).Id;
|
||||
|
||||
var task = _sessionManager.OnPlaybackProgress(request);
|
||||
|
||||
Task.WaitAll(task);
|
||||
}
|
||||
|
||||
public void Post(PingPlaybackSession request)
|
||||
{
|
||||
ApiEntryPoint.Instance.PingTranscodingJob(request.PlaySessionId, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public Task Delete(OnPlaybackStopped request)
|
||||
{
|
||||
return Post(new ReportPlaybackStopped
|
||||
{
|
||||
ItemId = new Guid(request.Id),
|
||||
PositionTicks = request.PositionTicks,
|
||||
MediaSourceId = request.MediaSourceId,
|
||||
PlaySessionId = request.PlaySessionId,
|
||||
LiveStreamId = request.LiveStreamId,
|
||||
NextMediaType = request.NextMediaType
|
||||
});
|
||||
}
|
||||
|
||||
public async Task Post(ReportPlaybackStopped request)
|
||||
{
|
||||
Logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", request.PlaySessionId ?? string.Empty);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.PlaySessionId))
|
||||
{
|
||||
await ApiEntryPoint.Instance.KillTranscodingJobs(_authContext.GetAuthorizationInfo(Request).DeviceId, request.PlaySessionId, s => true);
|
||||
}
|
||||
|
||||
request.SessionId = GetSession(_sessionContext).Id;
|
||||
|
||||
await _sessionManager.OnPlaybackStopped(request);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public object Delete(MarkUnplayedItem request)
|
||||
{
|
||||
var task = MarkUnplayed(request);
|
||||
|
||||
return ToOptimizedResult(task);
|
||||
}
|
||||
|
||||
private UserItemDataDto MarkUnplayed(MarkUnplayedItem request)
|
||||
{
|
||||
var user = _userManager.GetUserById(Guid.Parse(request.UserId));
|
||||
|
||||
var session = GetSession(_sessionContext);
|
||||
|
||||
var dto = UpdatePlayedStatus(user, request.Id, false, null);
|
||||
|
||||
foreach (var additionalUserInfo in session.AdditionalUsers)
|
||||
{
|
||||
var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
|
||||
|
||||
UpdatePlayedStatus(additionalUser, request.Id, false, null);
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the played status.
|
||||
/// </summary>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <param name="itemId">The item id.</param>
|
||||
/// <param name="wasPlayed">if set to <c>true</c> [was played].</param>
|
||||
/// <param name="datePlayed">The date played.</param>
|
||||
/// <returns>Task.</returns>
|
||||
private UserItemDataDto UpdatePlayedStatus(User user, string itemId, bool wasPlayed, DateTime? datePlayed)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(itemId);
|
||||
|
||||
if (wasPlayed)
|
||||
{
|
||||
item.MarkPlayed(user, datePlayed, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.MarkUnplayed(user);
|
||||
}
|
||||
|
||||
return _userDataRepository.GetUserDataDto(item, user);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetStudios
|
||||
/// </summary>
|
||||
[Route("/Studios", "GET", Summary = "Gets all studios from a given item, folder, or the entire library")]
|
||||
public class GetStudios : GetItemsByName
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetStudio
|
||||
/// </summary>
|
||||
[Route("/Studios/{Name}", "GET", Summary = "Gets a studio, by name")]
|
||||
public class GetStudio : IReturn<BaseItemDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
[ApiMember(Name = "Name", Description = "The studio name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class StudiosService
|
||||
/// </summary>
|
||||
[Authenticated]
|
||||
public class StudiosService : BaseItemsByNameService<Studio>
|
||||
{
|
||||
public StudiosService(
|
||||
ILogger<StudiosService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
ILibraryManager libraryManager,
|
||||
IUserDataManager userDataRepository,
|
||||
IDtoService dtoService,
|
||||
IAuthorizationContext authorizationContext)
|
||||
: base(
|
||||
logger,
|
||||
serverConfigurationManager,
|
||||
httpResultFactory,
|
||||
userManager,
|
||||
libraryManager,
|
||||
userDataRepository,
|
||||
dtoService,
|
||||
authorizationContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetStudio request)
|
||||
{
|
||||
var result = GetItem(request);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>Task{BaseItemDto}.</returns>
|
||||
private BaseItemDto GetItem(GetStudio request)
|
||||
{
|
||||
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
|
||||
|
||||
var item = GetStudio(request.Name, LibraryManager, dtoOptions);
|
||||
|
||||
if (!request.UserId.Equals(Guid.Empty))
|
||||
{
|
||||
var user = UserManager.GetUserById(request.UserId);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, dtoOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetStudios request)
|
||||
{
|
||||
var result = GetResultSlim(request);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
protected override QueryResult<(BaseItem, ItemCounts)> GetItems(GetItemsByName request, InternalItemsQuery query)
|
||||
{
|
||||
return LibraryManager.GetStudios(query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,575 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}", "GET", Summary = "Gets an item from a user's library")]
|
||||
public class GetItem : IReturn<BaseItemDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/Root", "GET", Summary = "Gets the root folder from a user's library")]
|
||||
public class GetRootFolder : IReturn<BaseItemDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetIntros
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Intros", "GET", Summary = "Gets intros to play before the main media item plays")]
|
||||
public class GetIntros : IReturn<QueryResult<BaseItemDto>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item id.
|
||||
/// </summary>
|
||||
/// <value>The item id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class MarkFavoriteItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/FavoriteItems/{Id}", "POST", Summary = "Marks an item as a favorite")]
|
||||
public class MarkFavoriteItem : IReturn<UserItemDataDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UnmarkFavoriteItem
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE", Summary = "Unmarks an item as a favorite")]
|
||||
public class UnmarkFavoriteItem : IReturn<UserItemDataDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class ClearUserItemRating
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE", Summary = "Deletes a user's saved personal rating for an item")]
|
||||
public class DeleteUserItemRating : IReturn<UserItemDataDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UpdateUserItemRating
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/Rating", "POST", Summary = "Updates a user's rating for an item")]
|
||||
public class UpdateUserItemRating : IReturn<UserItemDataDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if likes; otherwise, <c>false</c>.</value>
|
||||
[ApiMember(Name = "Likes", Description = "Whether the user likes the item or not. true/false", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")]
|
||||
public bool Likes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetLocalTrailers
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET", Summary = "Gets local trailers for an item")]
|
||||
public class GetLocalTrailers : IReturn<BaseItemDto[]>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetSpecialFeatures
|
||||
/// </summary>
|
||||
[Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET", Summary = "Gets special features for an item")]
|
||||
public class GetSpecialFeatures : IReturn<BaseItemDto[]>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Movie Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Users/{UserId}/Items/Latest", "GET", Summary = "Gets latest media")]
|
||||
public class GetLatestMedia : IReturn<BaseItemDto[]>, IHasDtoOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
[ApiMember(Name = "Limit", Description = "Limit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
|
||||
public string Fields { get; set; }
|
||||
|
||||
[ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
|
||||
public string IncludeItemTypes { get; set; }
|
||||
|
||||
[ApiMember(Name = "IsFolder", Description = "Filter by items that are folders, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool? IsFolder { get; set; }
|
||||
|
||||
[ApiMember(Name = "IsPlayed", Description = "Filter by items that are played, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool? IsPlayed { get; set; }
|
||||
|
||||
[ApiMember(Name = "GroupItems", Description = "Whether or not to group items into a parent container.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool GroupItems { get; set; }
|
||||
|
||||
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
||||
public bool? EnableImages { get; set; }
|
||||
|
||||
[ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int? ImageTypeLimit { get; set; }
|
||||
|
||||
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string EnableImageTypes { get; set; }
|
||||
|
||||
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
||||
public bool? EnableUserData { get; set; }
|
||||
|
||||
public GetLatestMedia()
|
||||
{
|
||||
Limit = 20;
|
||||
GroupItems = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class UserLibraryService
|
||||
/// </summary>
|
||||
[Authenticated]
|
||||
public class UserLibraryService : BaseApiService
|
||||
{
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly IUserDataManager _userDataRepository;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IDtoService _dtoService;
|
||||
private readonly IUserViewManager _userViewManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IAuthorizationContext _authContext;
|
||||
|
||||
public UserLibraryService(
|
||||
ILogger<UserLibraryService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
ILibraryManager libraryManager,
|
||||
IUserDataManager userDataRepository,
|
||||
IDtoService dtoService,
|
||||
IUserViewManager userViewManager,
|
||||
IFileSystem fileSystem,
|
||||
IAuthorizationContext authContext)
|
||||
: base(logger, serverConfigurationManager, httpResultFactory)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_libraryManager = libraryManager;
|
||||
_userDataRepository = userDataRepository;
|
||||
_dtoService = dtoService;
|
||||
_userViewManager = userViewManager;
|
||||
_fileSystem = fileSystem;
|
||||
_authContext = authContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetSpecialFeatures request)
|
||||
{
|
||||
var result = GetAsync(request);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
public object Get(GetLatestMedia request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
if (!request.IsPlayed.HasValue)
|
||||
{
|
||||
if (user.HidePlayedInLatest)
|
||||
{
|
||||
request.IsPlayed = false;
|
||||
}
|
||||
}
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var list = _userViewManager.GetLatestItems(new LatestItemsQuery
|
||||
{
|
||||
GroupItems = request.GroupItems,
|
||||
IncludeItemTypes = ApiEntryPoint.Split(request.IncludeItemTypes, ',', true),
|
||||
IsPlayed = request.IsPlayed,
|
||||
Limit = request.Limit,
|
||||
ParentId = request.ParentId,
|
||||
UserId = request.UserId,
|
||||
}, dtoOptions);
|
||||
|
||||
var dtos = list.Select(i =>
|
||||
{
|
||||
var item = i.Item2[0];
|
||||
var childCount = 0;
|
||||
|
||||
if (i.Item1 != null && (i.Item2.Count > 1 || i.Item1 is MusicAlbum))
|
||||
{
|
||||
item = i.Item1;
|
||||
childCount = i.Item2.Count;
|
||||
}
|
||||
|
||||
var dto = _dtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||
|
||||
dto.ChildCount = childCount;
|
||||
|
||||
return dto;
|
||||
});
|
||||
|
||||
return ToOptimizedResult(dtos.ToArray());
|
||||
}
|
||||
|
||||
private BaseItemDto[] GetAsync(GetSpecialFeatures request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var item = string.IsNullOrEmpty(request.Id) ?
|
||||
_libraryManager.GetUserRootFolder() :
|
||||
_libraryManager.GetItemById(request.Id);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var dtos = item
|
||||
.GetExtras(BaseItem.DisplayExtraTypes)
|
||||
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
|
||||
|
||||
return dtos.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetLocalTrailers request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var dtosExtras = item.GetExtras(new[] { ExtraType.Trailer })
|
||||
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item))
|
||||
.ToArray();
|
||||
|
||||
if (item is IHasTrailers hasTrailers)
|
||||
{
|
||||
var trailers = hasTrailers.GetTrailers();
|
||||
var dtosTrailers = _dtoService.GetBaseItemDtos(trailers, dtoOptions, user, item);
|
||||
var allTrailers = new BaseItemDto[dtosExtras.Length + dtosTrailers.Count];
|
||||
dtosExtras.CopyTo(allTrailers, 0);
|
||||
dtosTrailers.CopyTo(allTrailers, dtosExtras.Length);
|
||||
return ToOptimizedResult(allTrailers);
|
||||
}
|
||||
|
||||
return ToOptimizedResult(dtosExtras);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public async Task<object> Get(GetItem request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
|
||||
|
||||
await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
private async Task RefreshItemOnDemandIfNeeded(BaseItem item)
|
||||
{
|
||||
if (item is Person)
|
||||
{
|
||||
var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview) && item.HasImage(ImageType.Primary);
|
||||
var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 3;
|
||||
|
||||
if (!hasMetdata)
|
||||
{
|
||||
var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
|
||||
{
|
||||
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
|
||||
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
|
||||
ForceSave = performFullRefresh
|
||||
};
|
||||
|
||||
await item.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetRootFolder request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var item = _libraryManager.GetUserRootFolder();
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public async Task<object> Get(GetIntros request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var item = string.IsNullOrEmpty(request.Id) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(request.Id);
|
||||
|
||||
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
|
||||
var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
|
||||
|
||||
var result = new QueryResult<BaseItemDto>
|
||||
{
|
||||
Items = dtos,
|
||||
TotalRecordCount = dtos.Length
|
||||
};
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public object Post(MarkFavoriteItem request)
|
||||
{
|
||||
var dto = MarkFavorite(request.UserId, request.Id, true);
|
||||
|
||||
return ToOptimizedResult(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public object Delete(UnmarkFavoriteItem request)
|
||||
{
|
||||
var dto = MarkFavorite(request.UserId, request.Id, false);
|
||||
|
||||
return ToOptimizedResult(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the favorite.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user id.</param>
|
||||
/// <param name="itemId">The item id.</param>
|
||||
/// <param name="isFavorite">if set to <c>true</c> [is favorite].</param>
|
||||
private UserItemDataDto MarkFavorite(Guid userId, Guid itemId, bool isFavorite)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
|
||||
var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
|
||||
|
||||
// Get the user data for this item
|
||||
var data = _userDataRepository.GetUserData(user, item);
|
||||
|
||||
// Set favorite status
|
||||
data.IsFavorite = isFavorite;
|
||||
|
||||
_userDataRepository.SaveUserData(user, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None);
|
||||
|
||||
return _userDataRepository.GetUserDataDto(item, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public object Delete(DeleteUserItemRating request)
|
||||
{
|
||||
var dto = UpdateUserItemRating(request.UserId, request.Id, null);
|
||||
|
||||
return ToOptimizedResult(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
public object Post(UpdateUserItemRating request)
|
||||
{
|
||||
var dto = UpdateUserItemRating(request.UserId, request.Id, request.Likes);
|
||||
|
||||
return ToOptimizedResult(dto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the user item rating.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user id.</param>
|
||||
/// <param name="itemId">The item id.</param>
|
||||
/// <param name="likes">if set to <c>true</c> [likes].</param>
|
||||
private UserItemDataDto UpdateUserItemRating(Guid userId, Guid itemId, bool? likes)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
|
||||
var item = itemId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId);
|
||||
|
||||
// Get the user data for this item
|
||||
var data = _userDataRepository.GetUserData(user, item);
|
||||
|
||||
data.Likes = likes;
|
||||
|
||||
_userDataRepository.SaveUserData(user, item, data, UserDataSaveReason.UpdateUserRating, CancellationToken.None);
|
||||
|
||||
return _userDataRepository.GetUserDataDto(item, user);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Library;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
[Route("/Users/{UserId}/Views", "GET")]
|
||||
public class GetUserViews : IReturn<QueryResult<BaseItemDto>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
|
||||
[ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "GET")]
|
||||
public bool? IncludeExternalContent { get; set; }
|
||||
public bool IncludeHidden { get; set; }
|
||||
|
||||
public string PresetViews { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Users/{UserId}/GroupingOptions", "GET")]
|
||||
public class GetGroupingOptions : IReturn<SpecialViewOption[]>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
|
||||
public class UserViewsService : BaseApiService
|
||||
{
|
||||
private readonly IUserManager _userManager;
|
||||
private readonly IUserViewManager _userViewManager;
|
||||
private readonly IDtoService _dtoService;
|
||||
private readonly IAuthorizationContext _authContext;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
public UserViewsService(
|
||||
ILogger<UserViewsService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
IUserViewManager userViewManager,
|
||||
IDtoService dtoService,
|
||||
IAuthorizationContext authContext,
|
||||
ILibraryManager libraryManager)
|
||||
: base(logger, serverConfigurationManager, httpResultFactory)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_userViewManager = userViewManager;
|
||||
_dtoService = dtoService;
|
||||
_authContext = authContext;
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
public object Get(GetUserViews request)
|
||||
{
|
||||
var query = new UserViewQuery
|
||||
{
|
||||
UserId = request.UserId
|
||||
};
|
||||
|
||||
if (request.IncludeExternalContent.HasValue)
|
||||
{
|
||||
query.IncludeExternalContent = request.IncludeExternalContent.Value;
|
||||
}
|
||||
query.IncludeHidden = request.IncludeHidden;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.PresetViews))
|
||||
{
|
||||
query.PresetViews = request.PresetViews.Split(',');
|
||||
}
|
||||
|
||||
var app = _authContext.GetAuthorizationInfo(Request).Client ?? string.Empty;
|
||||
if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
query.PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows };
|
||||
}
|
||||
|
||||
var folders = _userViewManager.GetUserViews(query);
|
||||
|
||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||
var fields = dtoOptions.Fields.ToList();
|
||||
|
||||
fields.Add(ItemFields.PrimaryImageAspectRatio);
|
||||
fields.Add(ItemFields.DisplayPreferencesId);
|
||||
fields.Remove(ItemFields.BasicSyncInfo);
|
||||
dtoOptions.Fields = fields.ToArray();
|
||||
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
|
||||
.ToArray();
|
||||
|
||||
var result = new QueryResult<BaseItemDto>
|
||||
{
|
||||
Items = dtos,
|
||||
TotalRecordCount = dtos.Length
|
||||
};
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
public object Get(GetGroupingOptions request)
|
||||
{
|
||||
var user = _userManager.GetUserById(request.UserId);
|
||||
|
||||
var list = _libraryManager.GetUserRootFolder()
|
||||
.GetChildren(user, true)
|
||||
.OfType<Folder>()
|
||||
.Where(UserView.IsEligibleForGrouping)
|
||||
.Select(i => new SpecialViewOption
|
||||
{
|
||||
Name = i.Name,
|
||||
Id = i.Id.ToString("N", CultureInfo.InvariantCulture)
|
||||
|
||||
})
|
||||
.OrderBy(i => i.Name)
|
||||
.ToArray();
|
||||
|
||||
return ToOptimizedResult(list);
|
||||
}
|
||||
}
|
||||
|
||||
class SpecialViewOption
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.UserLibrary
|
||||
{
|
||||
/// <summary>
|
||||
/// Class GetYears
|
||||
/// </summary>
|
||||
[Route("/Years", "GET", Summary = "Gets all years from a given item, folder, or the entire library")]
|
||||
public class GetYears : GetItemsByName
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class GetYear
|
||||
/// </summary>
|
||||
[Route("/Years/{Year}", "GET", Summary = "Gets a year")]
|
||||
public class GetYear : IReturn<BaseItemDto>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the year.
|
||||
/// </summary>
|
||||
/// <value>The year.</value>
|
||||
[ApiMember(Name = "Year", Description = "The year", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
|
||||
public int Year { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user id.
|
||||
/// </summary>
|
||||
/// <value>The user id.</value>
|
||||
[ApiMember(Name = "UserId", Description = "Optional. Filter by user id, and attach user data", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class YearsService
|
||||
/// </summary>
|
||||
[Authenticated]
|
||||
public class YearsService : BaseItemsByNameService<Year>
|
||||
{
|
||||
public YearsService(
|
||||
ILogger<YearsService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
ILibraryManager libraryManager,
|
||||
IUserDataManager userDataRepository,
|
||||
IDtoService dtoService,
|
||||
IAuthorizationContext authorizationContext)
|
||||
: base(
|
||||
logger,
|
||||
serverConfigurationManager,
|
||||
httpResultFactory,
|
||||
userManager,
|
||||
libraryManager,
|
||||
userDataRepository,
|
||||
dtoService,
|
||||
authorizationContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetYear request)
|
||||
{
|
||||
var result = GetItem(request);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>Task{BaseItemDto}.</returns>
|
||||
private BaseItemDto GetItem(GetYear request)
|
||||
{
|
||||
var item = LibraryManager.GetYear(request.Year);
|
||||
|
||||
var dtoOptions = GetDtoOptions(AuthorizationContext, request);
|
||||
|
||||
if (!request.UserId.Equals(Guid.Empty))
|
||||
{
|
||||
var user = UserManager.GetUserById(request.UserId);
|
||||
|
||||
return DtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||
}
|
||||
|
||||
return DtoService.GetBaseItemDto(item, dtoOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetYears request)
|
||||
{
|
||||
var result = GetResult(request);
|
||||
|
||||
return ToOptimizedResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all items.
|
||||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
|
||||
protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IList<BaseItem> items)
|
||||
{
|
||||
return items
|
||||
.Select(i => i.ProductionYear ?? 0)
|
||||
.Where(i => i > 0)
|
||||
.Distinct()
|
||||
.Select(year => LibraryManager.GetYear(year));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Text;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MediaBrowser.Common.Json.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Long to String JSON converter.
|
||||
/// Javascript does not support 64-bit integers.
|
||||
/// </summary>
|
||||
public class JsonInt64Converter : JsonConverter<long>
|
||||
{
|
||||
/// <summary>
|
||||
/// Read JSON string as int64.
|
||||
/// </summary>
|
||||
/// <param name="reader"><see cref="Utf8JsonReader"/>.</param>
|
||||
/// <param name="type">Type.</param>
|
||||
/// <param name="options">Options.</param>
|
||||
/// <returns>Parsed value.</returns>
|
||||
public override long Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
// try to parse number directly from bytes
|
||||
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
|
||||
if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed)
|
||||
{
|
||||
return number;
|
||||
}
|
||||
|
||||
// try to parse from a string if the above failed, this covers cases with other escaped/UTF characters
|
||||
if (long.TryParse(reader.GetString(), out number))
|
||||
{
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to default handling
|
||||
return reader.GetInt64();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write long to JSON string.
|
||||
/// </summary>
|
||||
/// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
|
||||
/// <param name="value">Value to write.</param>
|
||||
/// <param name="options">Options.</param>
|
||||
public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString(NumberFormatInfo.InvariantInfo));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue