You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Lidarr/src/Lidarr.Api.V1/Commands/CommandController.cs

128 lines
4.4 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Lidarr.Http;
using Lidarr.Http.REST;
using Lidarr.Http.REST.Attributes;
using Lidarr.Http.Validation;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Composition;
using NzbDrone.Common.Serializer;
using NzbDrone.Common.TPL;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ProgressMessaging;
using NzbDrone.SignalR;
namespace Lidarr.Api.V1.Commands
{
[V1ApiController]
public class CommandController : RestControllerWithSignalR<CommandResource, CommandModel>, IHandle<CommandUpdatedEvent>
{
private readonly IManageCommandQueue _commandQueueManager;
private readonly KnownTypes _knownTypes;
private readonly Debouncer _debouncer;
private readonly Dictionary<int, CommandResource> _pendingUpdates;
private readonly CommandPriorityComparer _commandPriorityComparer = new CommandPriorityComparer();
public CommandController(IManageCommandQueue commandQueueManager,
IBroadcastSignalRMessage signalRBroadcaster,
KnownTypes knownTypes)
: base(signalRBroadcaster)
{
_commandQueueManager = commandQueueManager;
_knownTypes = knownTypes;
_debouncer = new Debouncer(SendUpdates, TimeSpan.FromSeconds(0.1));
_pendingUpdates = new Dictionary<int, CommandResource>();
PostValidator.RuleFor(c => c.Name).NotBlank();
}
public override CommandResource GetResourceById(int id)
{
return _commandQueueManager.Get(id).ToResource();
}
[RestPostById]
[Consumes("application/json")]
[Produces("application/json")]
public ActionResult<CommandResource> StartCommand(CommandResource commandResource)
{
var commandType =
_knownTypes.GetImplementations(typeof(Command))
.Single(c => c.Name.Replace("Command", "")
.Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
Request.Body.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(Request.Body))
{
var body = reader.ReadToEnd();
dynamic command = STJson.Deserialize(body, commandType);
command.Trigger = CommandTrigger.Manual;
command.SuppressMessages = !command.SendUpdatesToClient;
command.SendUpdatesToClient = true;
command.ClientUserAgent = Request.Headers["UserAgent"];
var trackedCommand = _commandQueueManager.Push(command, CommandPriority.Normal, CommandTrigger.Manual);
return Created(trackedCommand.Id);
}
}
[HttpGet]
[Produces("application/json")]
public List<CommandResource> GetStartedCommands()
{
return _commandQueueManager.All()
.OrderBy(c => c.Status, _commandPriorityComparer)
.ThenByDescending(c => c.Priority)
.ToResource();
}
[RestDeleteById]
public void CancelCommand(int id)
{
_commandQueueManager.Cancel(id);
}
[NonAction]
public void Handle(CommandUpdatedEvent message)
{
if (message.Command.Body.SendUpdatesToClient)
{
lock (_pendingUpdates)
{
_pendingUpdates[message.Command.Id] = message.Command.ToResource();
}
_debouncer.Execute();
}
}
private void SendUpdates()
{
lock (_pendingUpdates)
{
var pendingUpdates = _pendingUpdates.Values.ToArray();
_pendingUpdates.Clear();
foreach (var pendingUpdate in pendingUpdates)
{
BroadcastResourceChange(ModelAction.Updated, pendingUpdate);
if (pendingUpdate.Name == typeof(MessagingCleanupCommand).Name.Replace("Command", "") &&
pendingUpdate.Status == CommandStatus.Completed)
{
BroadcastResourceChange(ModelAction.Sync);
}
}
}
}
}
}