|
|
|
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.MediaFiles.TrackImport.Manual;
|
|
|
|
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();
|
|
|
|
var priority = commandType == typeof(ManualImportCommand)
|
|
|
|
? CommandPriority.High
|
|
|
|
: CommandPriority.Normal;
|
|
|
|
|
|
|
|
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, priority, 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|