Purge V2 API
This commit is contained in:
parent
fdecd1fea4
commit
b4ca4908fc
|
@ -22,7 +22,7 @@ function getUrls(state) {
|
||||||
tags
|
tags
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
let icalUrl = `${window.location.host}${window.Sonarr.urlBase}/feed/calendar/Sonarr.ics?`;
|
let icalUrl = `${window.location.host}${window.Sonarr.urlBase}/feed/v3/calendar/Sonarr.ics?`;
|
||||||
|
|
||||||
if (unmonitored) {
|
if (unmonitored) {
|
||||||
icalUrl += 'unmonitored=true&';
|
icalUrl += 'unmonitored=true&';
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
// Blacklist has been deprecated for blocklist.
|
|
||||||
using NzbDrone.Api.Blocklist;
|
|
||||||
using NzbDrone.Core.Blocklisting;
|
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Blacklist
|
|
||||||
{
|
|
||||||
public class BlacklistModule : SonarrRestModule<BlocklistResource>
|
|
||||||
{
|
|
||||||
private readonly BlocklistService _blocklistService;
|
|
||||||
|
|
||||||
public BlacklistModule(BlocklistService blocklistService)
|
|
||||||
{
|
|
||||||
_blocklistService = blocklistService;
|
|
||||||
GetResourcePaged = Blocklist;
|
|
||||||
DeleteResource = DeleteBlockList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PagingResource<BlocklistResource> Blocklist(PagingResource<BlocklistResource> pagingResource)
|
|
||||||
{
|
|
||||||
var pagingSpec = pagingResource.MapToPagingSpec<BlocklistResource, Core.Blocklisting.Blocklist>("id", SortDirection.Ascending);
|
|
||||||
|
|
||||||
return ApplyToPage(_blocklistService.Paged, pagingSpec, BlocklistResourceMapper.MapToResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteBlockList(int id)
|
|
||||||
{
|
|
||||||
_blocklistService.Delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
using NzbDrone.Core.Blocklisting;
|
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Blocklist
|
|
||||||
{
|
|
||||||
public class BlocklistModule : SonarrRestModule<BlocklistResource>
|
|
||||||
{
|
|
||||||
private readonly BlocklistService _blocklistService;
|
|
||||||
|
|
||||||
public BlocklistModule(BlocklistService blocklistService)
|
|
||||||
{
|
|
||||||
_blocklistService = blocklistService;
|
|
||||||
GetResourcePaged = Blocklist;
|
|
||||||
DeleteResource = DeleteBlockList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PagingResource<BlocklistResource> Blocklist(PagingResource<BlocklistResource> pagingResource)
|
|
||||||
{
|
|
||||||
var pagingSpec = pagingResource.MapToPagingSpec<BlocklistResource, Core.Blocklisting.Blocklist>("id", SortDirection.Ascending);
|
|
||||||
|
|
||||||
return ApplyToPage(_blocklistService.Paged, pagingSpec, BlocklistResourceMapper.MapToResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteBlockList(int id)
|
|
||||||
{
|
|
||||||
_blocklistService.Delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
using NzbDrone.Core.Languages;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Blocklist
|
|
||||||
{
|
|
||||||
public class BlocklistResource : RestResource
|
|
||||||
{
|
|
||||||
public int SeriesId { get; set; }
|
|
||||||
public List<int> EpisodeIds { get; set; }
|
|
||||||
public string SourceTitle { get; set; }
|
|
||||||
public QualityModel Quality { get; set; }
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
public DownloadProtocol Protocol { get; set; }
|
|
||||||
public string Indexer { get; set; }
|
|
||||||
public string Message { get; set; }
|
|
||||||
public Language Language { get; set; }
|
|
||||||
|
|
||||||
public SeriesResource Series { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class BlocklistResourceMapper
|
|
||||||
{
|
|
||||||
public static BlocklistResource MapToResource(this Core.Blocklisting.Blocklist model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new BlocklistResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
SeriesId = model.SeriesId,
|
|
||||||
EpisodeIds = model.EpisodeIds,
|
|
||||||
SourceTitle = model.SourceTitle,
|
|
||||||
Quality = model.Quality,
|
|
||||||
Date = model.Date,
|
|
||||||
Protocol = model.Protocol,
|
|
||||||
Indexer = model.Indexer,
|
|
||||||
Message = model.Message,
|
|
||||||
|
|
||||||
Series = model.Series.ToResource()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
using Nancy;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Ical.Net;
|
|
||||||
using Ical.Net.CalendarComponents;
|
|
||||||
using Ical.Net.DataTypes;
|
|
||||||
using Ical.Net.Serialization;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using Nancy.Responses;
|
|
||||||
using NzbDrone.Core.Tags;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Calendar
|
|
||||||
{
|
|
||||||
public class CalendarFeedModule : NzbDroneFeedModule
|
|
||||||
{
|
|
||||||
private readonly IEpisodeService _episodeService;
|
|
||||||
private readonly ITagService _tagService;
|
|
||||||
|
|
||||||
public CalendarFeedModule(IEpisodeService episodeService, ITagService tagService)
|
|
||||||
: base("calendar")
|
|
||||||
{
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_tagService = tagService;
|
|
||||||
|
|
||||||
Get("/NzbDrone.ics", options => GetCalendarFeed());
|
|
||||||
Get("/Sonarr.ics", options => GetCalendarFeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetCalendarFeed()
|
|
||||||
{
|
|
||||||
var pastDays = 7;
|
|
||||||
var futureDays = 28;
|
|
||||||
var start = DateTime.Today.AddDays(-pastDays);
|
|
||||||
var end = DateTime.Today.AddDays(futureDays);
|
|
||||||
var unmonitored = false;
|
|
||||||
var premieresOnly = false;
|
|
||||||
var asAllDay = false;
|
|
||||||
var tags = new List<int>();
|
|
||||||
|
|
||||||
// TODO: Remove start/end parameters in v3, they don't work well for iCal
|
|
||||||
var queryStart = Request.Query.Start;
|
|
||||||
var queryEnd = Request.Query.End;
|
|
||||||
var queryPastDays = Request.Query.PastDays;
|
|
||||||
var queryFutureDays = Request.Query.FutureDays;
|
|
||||||
var queryUnmonitored = Request.Query.Unmonitored;
|
|
||||||
var queryPremieresOnly = Request.Query.PremieresOnly;
|
|
||||||
var queryPremiersOnly = Request.Query.PremiersOnly;
|
|
||||||
var queryAsAllDay = Request.Query.AsAllDay;
|
|
||||||
var queryTags = Request.Query.Tags;
|
|
||||||
|
|
||||||
if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value);
|
|
||||||
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
|
||||||
|
|
||||||
if (queryPastDays.HasValue)
|
|
||||||
{
|
|
||||||
pastDays = int.Parse(queryPastDays.Value);
|
|
||||||
start = DateTime.Today.AddDays(-pastDays);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryFutureDays.HasValue)
|
|
||||||
{
|
|
||||||
futureDays = int.Parse(queryFutureDays.Value);
|
|
||||||
end = DateTime.Today.AddDays(futureDays);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryUnmonitored.HasValue)
|
|
||||||
{
|
|
||||||
unmonitored = bool.Parse(queryUnmonitored.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryPremieresOnly.HasValue)
|
|
||||||
{
|
|
||||||
premieresOnly = bool.Parse(queryPremieresOnly.Value);
|
|
||||||
}
|
|
||||||
else if (queryPremiersOnly.HasValue)
|
|
||||||
{
|
|
||||||
// There was a typo, recognize mistyped 'premiersOnly' boolean too for background compat.
|
|
||||||
premieresOnly = bool.Parse(queryPremiersOnly.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (queryAsAllDay.HasValue)
|
|
||||||
{
|
|
||||||
asAllDay = bool.Parse(queryAsAllDay.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryTags.HasValue)
|
|
||||||
{
|
|
||||||
var tagInput = (string)queryTags.Value.ToString();
|
|
||||||
tags.AddRange(tagInput.Split(',').Select(_tagService.GetTag).Select(t => t.Id));
|
|
||||||
}
|
|
||||||
|
|
||||||
var episodes = _episodeService.EpisodesBetweenDates(start, end, unmonitored);
|
|
||||||
var calendar = new Ical.Net.Calendar
|
|
||||||
{
|
|
||||||
ProductId = "-//sonarr.tv//Sonarr//EN"
|
|
||||||
};
|
|
||||||
|
|
||||||
var calendarName = "Sonarr TV Schedule";
|
|
||||||
calendar.AddProperty(new CalendarProperty("NAME", calendarName));
|
|
||||||
calendar.AddProperty(new CalendarProperty("X-WR-CALNAME", calendarName));
|
|
||||||
|
|
||||||
foreach (var episode in episodes.OrderBy(v => v.AirDateUtc.Value))
|
|
||||||
{
|
|
||||||
if (premieresOnly && (episode.SeasonNumber == 0 || episode.EpisodeNumber != 1))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tags.Any() && tags.None(episode.Series.Tags.Contains))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var occurrence = calendar.Create<CalendarEvent>();
|
|
||||||
occurrence.Uid = "NzbDrone_episode_" + episode.Id;
|
|
||||||
occurrence.Status = episode.HasFile ? EventStatus.Confirmed : EventStatus.Tentative;
|
|
||||||
occurrence.Description = episode.Overview;
|
|
||||||
occurrence.Categories = new List<string>() { episode.Series.Network };
|
|
||||||
|
|
||||||
if (asAllDay)
|
|
||||||
{
|
|
||||||
occurrence.Start = new CalDateTime(episode.AirDateUtc.Value.ToLocalTime()) { HasTime = false };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
occurrence.Start = new CalDateTime(episode.AirDateUtc.Value) { HasTime = true };
|
|
||||||
occurrence.End = new CalDateTime(episode.AirDateUtc.Value.AddMinutes(episode.Series.Runtime)) { HasTime = true };
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (episode.Series.SeriesType)
|
|
||||||
{
|
|
||||||
case SeriesTypes.Daily:
|
|
||||||
occurrence.Summary = $"{episode.Series.Title} - {episode.Title}";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
occurrence.Summary =$"{episode.Series.Title} - {episode.SeasonNumber}x{episode.EpisodeNumber:00} - {episode.Title}";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var serializer = (IStringSerializer) new SerializerFactory().Build(calendar.GetType(), new SerializationContext());
|
|
||||||
var icalendar = serializer.SerializeToString(calendar);
|
|
||||||
|
|
||||||
return new TextResponse(icalendar, "text/calendar");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Api.Episodes;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Calendar
|
|
||||||
{
|
|
||||||
public class CalendarModule : EpisodeModuleWithSignalR
|
|
||||||
{
|
|
||||||
public CalendarModule(IEpisodeService episodeService,
|
|
||||||
ISeriesService seriesService,
|
|
||||||
IUpgradableSpecification upgradableSpecification,
|
|
||||||
IBroadcastSignalRMessage signalRBroadcaster)
|
|
||||||
: base(episodeService, seriesService, upgradableSpecification, signalRBroadcaster, "calendar")
|
|
||||||
{
|
|
||||||
GetResourceAll = GetCalendar;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<EpisodeResource> GetCalendar()
|
|
||||||
{
|
|
||||||
var start = DateTime.Today;
|
|
||||||
var end = DateTime.Today.AddDays(2);
|
|
||||||
var includeUnmonitored = false;
|
|
||||||
|
|
||||||
var queryStart = Request.Query.Start;
|
|
||||||
var queryEnd = Request.Query.End;
|
|
||||||
var queryIncludeUnmonitored = Request.Query.Unmonitored;
|
|
||||||
|
|
||||||
if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value);
|
|
||||||
if (queryEnd.HasValue) end = DateTime.Parse(queryEnd.Value);
|
|
||||||
if (queryIncludeUnmonitored.HasValue) includeUnmonitored = Convert.ToBoolean(queryIncludeUnmonitored.Value);
|
|
||||||
|
|
||||||
var resources = MapToResource(_episodeService.EpisodesBetweenDates(start, end, includeUnmonitored), true, true);
|
|
||||||
|
|
||||||
return resources.OrderBy(e => e.AirDateUtc).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Sonarr.Http.Extensions;
|
|
||||||
using NzbDrone.Common;
|
|
||||||
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;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Validation;
|
|
||||||
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Commands
|
|
||||||
{
|
|
||||||
public class CommandModule : SonarrRestModuleWithSignalR<CommandResource, CommandModel>, IHandle<CommandUpdatedEvent>
|
|
||||||
{
|
|
||||||
private readonly IManageCommandQueue _commandQueueManager;
|
|
||||||
private readonly IServiceFactory _serviceFactory;
|
|
||||||
private readonly Debouncer _debouncer;
|
|
||||||
private readonly Dictionary<int, CommandResource> _pendingUpdates;
|
|
||||||
|
|
||||||
public CommandModule(IManageCommandQueue commandQueueManager,
|
|
||||||
IBroadcastSignalRMessage signalRBroadcaster,
|
|
||||||
IServiceFactory serviceFactory)
|
|
||||||
: base(signalRBroadcaster)
|
|
||||||
{
|
|
||||||
_commandQueueManager = commandQueueManager;
|
|
||||||
_serviceFactory = serviceFactory;
|
|
||||||
|
|
||||||
GetResourceById = GetCommand;
|
|
||||||
CreateResource = StartCommand;
|
|
||||||
GetResourceAll = GetStartedCommands;
|
|
||||||
|
|
||||||
PostValidator.RuleFor(c => c.Name).NotBlank();
|
|
||||||
|
|
||||||
_debouncer = new Debouncer(SendUpdates, TimeSpan.FromSeconds(0.1));
|
|
||||||
_pendingUpdates = new Dictionary<int, CommandResource>();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private CommandResource GetCommand(int id)
|
|
||||||
{
|
|
||||||
return _commandQueueManager.Get(id).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int StartCommand(CommandResource commandResource)
|
|
||||||
{
|
|
||||||
var commandType = _serviceFactory.GetImplementations(typeof(Command))
|
|
||||||
.Single(c => c.Name.Replace("Command", "").Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase));
|
|
||||||
|
|
||||||
dynamic command = Request.Body.FromJson(commandType);
|
|
||||||
command.Trigger = CommandTrigger.Manual;
|
|
||||||
|
|
||||||
var trackedCommand = _commandQueueManager.Push(command, CommandPriority.Normal, CommandTrigger.Manual);
|
|
||||||
return trackedCommand.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<CommandResource> GetStartedCommands()
|
|
||||||
{
|
|
||||||
return _commandQueueManager.GetStarted().ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Messaging.Commands;
|
|
||||||
using NzbDrone.Common.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Commands
|
|
||||||
{
|
|
||||||
public class CommandResource : RestResource
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string Message { get; set; }
|
|
||||||
public object Body { get; set; }
|
|
||||||
public CommandPriority Priority { get; set; }
|
|
||||||
public CommandStatus Status { get; set; }
|
|
||||||
public DateTime Queued { get; set; }
|
|
||||||
public DateTime? Started { get; set; }
|
|
||||||
public DateTime? Ended { get; set; }
|
|
||||||
public TimeSpan? Duration { get; set; }
|
|
||||||
public string Exception { get; set; }
|
|
||||||
public CommandTrigger Trigger { get; set; }
|
|
||||||
|
|
||||||
public string ClientUserAgent { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string CompletionMessage { get; set; }
|
|
||||||
|
|
||||||
//Legacy
|
|
||||||
public CommandStatus State
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Status;
|
|
||||||
}
|
|
||||||
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Manual
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Trigger == CommandTrigger.Manual;
|
|
||||||
}
|
|
||||||
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTime StartedOn
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Queued;
|
|
||||||
}
|
|
||||||
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTime? StateChangeTime
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
|
|
||||||
if (Started.HasValue) return Started.Value;
|
|
||||||
|
|
||||||
return Ended;
|
|
||||||
}
|
|
||||||
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SendUpdatesToClient
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Body != null) return (Body as Command).SendUpdatesToClient;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool UpdateScheduledTask
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Body != null) return (Body as Command).UpdateScheduledTask;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTime? LastExecutionTime { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class CommandResourceMapper
|
|
||||||
{
|
|
||||||
public static CommandResource ToResource(this CommandModel model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new CommandResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Name = model.Name,
|
|
||||||
Message = model.Message,
|
|
||||||
Body = model.Body,
|
|
||||||
Priority = model.Priority,
|
|
||||||
Status = model.Status,
|
|
||||||
Queued = model.QueuedAt,
|
|
||||||
Started = model.StartedAt,
|
|
||||||
Ended = model.EndedAt,
|
|
||||||
Duration = model.Duration,
|
|
||||||
Exception = model.Exception,
|
|
||||||
Trigger = model.Trigger,
|
|
||||||
|
|
||||||
ClientUserAgent = UserAgentParser.SimplifyUserAgent(model.Body.ClientUserAgent),
|
|
||||||
|
|
||||||
CompletionMessage = model.Body.CompletionMessage,
|
|
||||||
LastExecutionTime = model.Body.LastExecutionTime
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<CommandResource> ToResource(this IEnumerable<CommandModel> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.Validation.Paths;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class DownloadClientConfigModule : NzbDroneConfigModule<DownloadClientConfigResource>
|
|
||||||
{
|
|
||||||
public DownloadClientConfigModule(IConfigService configService)
|
|
||||||
: base(configService)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override DownloadClientConfigResource ToResource(IConfigService model)
|
|
||||||
{
|
|
||||||
return DownloadClientConfigResourceMapper.ToResource(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class DownloadClientConfigResource : RestResource
|
|
||||||
{
|
|
||||||
public string DownloadClientWorkingFolders { get; set; }
|
|
||||||
|
|
||||||
public bool EnableCompletedDownloadHandling { get; set; }
|
|
||||||
public bool AutoRedownloadFailed { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DownloadClientConfigResourceMapper
|
|
||||||
{
|
|
||||||
public static DownloadClientConfigResource ToResource(IConfigService model)
|
|
||||||
{
|
|
||||||
return new DownloadClientConfigResource
|
|
||||||
{
|
|
||||||
DownloadClientWorkingFolders = model.DownloadClientWorkingFolders,
|
|
||||||
|
|
||||||
EnableCompletedDownloadHandling = model.EnableCompletedDownloadHandling,
|
|
||||||
AutoRedownloadFailed = model.AutoRedownloadFailed
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Authentication;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.Update;
|
|
||||||
using NzbDrone.Core.Validation;
|
|
||||||
using NzbDrone.Core.Validation.Paths;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class HostConfigModule : SonarrRestModule<HostConfigResource>
|
|
||||||
{
|
|
||||||
private readonly IConfigFileProvider _configFileProvider;
|
|
||||||
private readonly IConfigService _configService;
|
|
||||||
private readonly IUserService _userService;
|
|
||||||
|
|
||||||
public HostConfigModule(IConfigFileProvider configFileProvider, IConfigService configService, IUserService userService)
|
|
||||||
: base("/config/host")
|
|
||||||
{
|
|
||||||
_configFileProvider = configFileProvider;
|
|
||||||
_configService = configService;
|
|
||||||
_userService = userService;
|
|
||||||
|
|
||||||
GetResourceSingle = GetHostConfig;
|
|
||||||
GetResourceById = GetHostConfig;
|
|
||||||
UpdateResource = SaveHostConfig;
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.BindAddress)
|
|
||||||
.ValidIp4Address()
|
|
||||||
.NotListenAllIp4Address()
|
|
||||||
.When(c => c.BindAddress != "*");
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.Port).ValidPort();
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.UrlBase).ValidUrlBase();
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.Username).NotEmpty().When(c => c.AuthenticationMethod != AuthenticationType.None);
|
|
||||||
SharedValidator.RuleFor(c => c.Password).NotEmpty().When(c => c.AuthenticationMethod != AuthenticationType.None);
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.SslPort).ValidPort().When(c => c.EnableSsl);
|
|
||||||
SharedValidator.RuleFor(c => c.SslCertHash).NotEmpty().When(c => c.EnableSsl && OsInfo.IsWindows);
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.Branch).NotEmpty().WithMessage("Branch name is required, 'master' is the default");
|
|
||||||
SharedValidator.RuleFor(c => c.UpdateScriptPath).IsValidPath().When(c => c.UpdateMechanism == UpdateMechanism.Script);
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.BackupFolder).IsValidPath().When(c => Path.IsPathRooted(c.BackupFolder));
|
|
||||||
SharedValidator.RuleFor(c => c.BackupInterval).InclusiveBetween(1, 7);
|
|
||||||
SharedValidator.RuleFor(c => c.BackupRetention).InclusiveBetween(1, 90);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HostConfigResource GetHostConfig()
|
|
||||||
{
|
|
||||||
var resource = _configFileProvider.ToResource(_configService);
|
|
||||||
resource.Id = 1;
|
|
||||||
|
|
||||||
var user = _userService.FindUser();
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
resource.Username = user.Username;
|
|
||||||
resource.Password = user.Password;
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HostConfigResource GetHostConfig(int id)
|
|
||||||
{
|
|
||||||
return GetHostConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveHostConfig(HostConfigResource resource)
|
|
||||||
{
|
|
||||||
var dictionary = resource.GetType()
|
|
||||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
|
||||||
.ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null));
|
|
||||||
|
|
||||||
_configFileProvider.SaveConfigDictionary(dictionary);
|
|
||||||
_configService.SaveConfigDictionary(dictionary);
|
|
||||||
|
|
||||||
if (resource.Username.IsNotNullOrWhiteSpace() && resource.Password.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
_userService.Upsert(resource.Username, resource.Password);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Authentication;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.Update;
|
|
||||||
using NzbDrone.Common.Http.Proxy;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class HostConfigResource : RestResource
|
|
||||||
{
|
|
||||||
public string BindAddress { get; set; }
|
|
||||||
public int Port { get; set; }
|
|
||||||
public int SslPort { get; set; }
|
|
||||||
public bool EnableSsl { get; set; }
|
|
||||||
public bool LaunchBrowser { get; set; }
|
|
||||||
public AuthenticationType AuthenticationMethod { get; set; }
|
|
||||||
public bool AnalyticsEnabled { get; set; }
|
|
||||||
public string Username { get; set; }
|
|
||||||
public string Password { get; set; }
|
|
||||||
public string LogLevel { get; set; }
|
|
||||||
public string ConsoleLogLevel { get; set; }
|
|
||||||
public string Branch { get; set; }
|
|
||||||
public string ApiKey { get; set; }
|
|
||||||
public string SslCertHash { get; set; }
|
|
||||||
public string UrlBase { get; set; }
|
|
||||||
public bool UpdateAutomatically { get; set; }
|
|
||||||
public UpdateMechanism UpdateMechanism { get; set; }
|
|
||||||
public string UpdateScriptPath { get; set; }
|
|
||||||
public bool ProxyEnabled { get; set; }
|
|
||||||
public ProxyType ProxyType { get; set; }
|
|
||||||
public string ProxyHostname { get; set; }
|
|
||||||
public int ProxyPort { get; set; }
|
|
||||||
public string ProxyUsername { get; set; }
|
|
||||||
public string ProxyPassword { get; set; }
|
|
||||||
public string ProxyBypassFilter { get; set; }
|
|
||||||
public bool ProxyBypassLocalAddresses { get; set; }
|
|
||||||
public string BackupFolder { get; set; }
|
|
||||||
public int BackupInterval { get; set; }
|
|
||||||
public int BackupRetention { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class HostConfigResourceMapper
|
|
||||||
{
|
|
||||||
public static HostConfigResource ToResource(this IConfigFileProvider model, IConfigService configService)
|
|
||||||
{
|
|
||||||
// TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead?
|
|
||||||
return new HostConfigResource
|
|
||||||
{
|
|
||||||
BindAddress = model.BindAddress,
|
|
||||||
Port = model.Port,
|
|
||||||
SslPort = model.SslPort,
|
|
||||||
EnableSsl = model.EnableSsl,
|
|
||||||
LaunchBrowser = model.LaunchBrowser,
|
|
||||||
AuthenticationMethod = model.AuthenticationMethod,
|
|
||||||
AnalyticsEnabled = model.AnalyticsEnabled,
|
|
||||||
//Username
|
|
||||||
//Password
|
|
||||||
LogLevel = model.LogLevel,
|
|
||||||
ConsoleLogLevel = model.ConsoleLogLevel,
|
|
||||||
Branch = model.Branch,
|
|
||||||
ApiKey = model.ApiKey,
|
|
||||||
SslCertHash = model.SslCertHash,
|
|
||||||
UrlBase = model.UrlBase,
|
|
||||||
UpdateAutomatically = model.UpdateAutomatically,
|
|
||||||
UpdateMechanism = model.UpdateMechanism,
|
|
||||||
UpdateScriptPath = model.UpdateScriptPath,
|
|
||||||
ProxyEnabled = configService.ProxyEnabled,
|
|
||||||
ProxyType = configService.ProxyType,
|
|
||||||
ProxyHostname = configService.ProxyHostname,
|
|
||||||
ProxyPort = configService.ProxyPort,
|
|
||||||
ProxyUsername = configService.ProxyUsername,
|
|
||||||
ProxyPassword = configService.ProxyPassword,
|
|
||||||
ProxyBypassFilter = configService.ProxyBypassFilter,
|
|
||||||
ProxyBypassLocalAddresses = configService.ProxyBypassLocalAddresses,
|
|
||||||
BackupFolder = configService.BackupFolder,
|
|
||||||
BackupInterval = configService.BackupInterval,
|
|
||||||
BackupRetention = configService.BackupRetention
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using Sonarr.Http.Validation;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class IndexerConfigModule : NzbDroneConfigModule<IndexerConfigResource>
|
|
||||||
{
|
|
||||||
|
|
||||||
public IndexerConfigModule(IConfigService configService)
|
|
||||||
: base(configService)
|
|
||||||
{
|
|
||||||
SharedValidator.RuleFor(c => c.MinimumAge)
|
|
||||||
.GreaterThanOrEqualTo(0);
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.MaximumSize)
|
|
||||||
.GreaterThanOrEqualTo(0);
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.Retention)
|
|
||||||
.GreaterThanOrEqualTo(0);
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.RssSyncInterval)
|
|
||||||
.IsValidRssSyncInterval();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IndexerConfigResource ToResource(IConfigService model)
|
|
||||||
{
|
|
||||||
return IndexerConfigResourceMapper.ToResource(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class IndexerConfigResource : RestResource
|
|
||||||
{
|
|
||||||
public int MinimumAge { get; set; }
|
|
||||||
public int MaximumSize { get; set; }
|
|
||||||
public int Retention { get; set; }
|
|
||||||
public int RssSyncInterval { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class IndexerConfigResourceMapper
|
|
||||||
{
|
|
||||||
public static IndexerConfigResource ToResource(IConfigService model)
|
|
||||||
{
|
|
||||||
return new IndexerConfigResource
|
|
||||||
{
|
|
||||||
MinimumAge = model.MinimumAge,
|
|
||||||
MaximumSize = model.MaximumSize,
|
|
||||||
Retention = model.Retention,
|
|
||||||
RssSyncInterval = model.RssSyncInterval,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.Validation;
|
|
||||||
using NzbDrone.Core.Validation.Paths;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class MediaManagementConfigModule : NzbDroneConfigModule<MediaManagementConfigResource>
|
|
||||||
{
|
|
||||||
public MediaManagementConfigModule(IConfigService configService, PathExistsValidator pathExistsValidator, FolderChmodValidator folderChmodValidator)
|
|
||||||
: base(configService)
|
|
||||||
{
|
|
||||||
SharedValidator.RuleFor(c => c.ChmodFolder).SetValidator(folderChmodValidator).When(c => !string.IsNullOrEmpty(c.ChmodFolder) && PlatformInfo.IsMono);
|
|
||||||
SharedValidator.RuleFor(c => c.RecycleBin).IsValidPath().SetValidator(pathExistsValidator).When(c => !string.IsNullOrWhiteSpace(c.RecycleBin));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override MediaManagementConfigResource ToResource(IConfigService model)
|
|
||||||
{
|
|
||||||
return MediaManagementConfigResourceMapper.ToResource(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class MediaManagementConfigResource : RestResource
|
|
||||||
{
|
|
||||||
public bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; }
|
|
||||||
public string RecycleBin { get; set; }
|
|
||||||
public ProperDownloadTypes DownloadPropersAndRepacks { get; set; }
|
|
||||||
public bool CreateEmptySeriesFolders { get; set; }
|
|
||||||
public bool DeleteEmptyFolders { get; set; }
|
|
||||||
public FileDateType FileDate { get; set; }
|
|
||||||
|
|
||||||
public bool SetPermissionsLinux { get; set; }
|
|
||||||
public string ChmodFolder { get; set; }
|
|
||||||
public string ChownGroup { get; set; }
|
|
||||||
|
|
||||||
public bool SkipFreeSpaceCheckWhenImporting { get; set; }
|
|
||||||
public bool CopyUsingHardlinks { get; set; }
|
|
||||||
public bool ImportExtraFiles { get; set; }
|
|
||||||
public string ExtraFileExtensions { get; set; }
|
|
||||||
public bool EnableMediaInfo { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MediaManagementConfigResourceMapper
|
|
||||||
{
|
|
||||||
public static MediaManagementConfigResource ToResource(IConfigService model)
|
|
||||||
{
|
|
||||||
return new MediaManagementConfigResource
|
|
||||||
{
|
|
||||||
AutoUnmonitorPreviouslyDownloadedEpisodes = model.AutoUnmonitorPreviouslyDownloadedEpisodes,
|
|
||||||
RecycleBin = model.RecycleBin,
|
|
||||||
DownloadPropersAndRepacks = model.DownloadPropersAndRepacks,
|
|
||||||
CreateEmptySeriesFolders = model.CreateEmptySeriesFolders,
|
|
||||||
DeleteEmptyFolders = model.DeleteEmptyFolders,
|
|
||||||
FileDate = model.FileDate,
|
|
||||||
|
|
||||||
SetPermissionsLinux = model.SetPermissionsLinux,
|
|
||||||
ChmodFolder = model.ChmodFolder,
|
|
||||||
ChownGroup = model.ChownGroup,
|
|
||||||
|
|
||||||
SkipFreeSpaceCheckWhenImporting = model.SkipFreeSpaceCheckWhenImporting,
|
|
||||||
CopyUsingHardlinks = model.CopyUsingHardlinks,
|
|
||||||
ImportExtraFiles = model.ImportExtraFiles,
|
|
||||||
ExtraFileExtensions = model.ExtraFileExtensions,
|
|
||||||
EnableMediaInfo = model.EnableMediaInfo
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using FluentValidation;
|
|
||||||
using FluentValidation.Results;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Organizer;
|
|
||||||
using Nancy.ModelBinding;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class NamingConfigModule : SonarrRestModule<NamingConfigResource>
|
|
||||||
{
|
|
||||||
private readonly INamingConfigService _namingConfigService;
|
|
||||||
private readonly IFilenameSampleService _filenameSampleService;
|
|
||||||
private readonly IFilenameValidationService _filenameValidationService;
|
|
||||||
private readonly IBuildFileNames _filenameBuilder;
|
|
||||||
|
|
||||||
public NamingConfigModule(INamingConfigService namingConfigService,
|
|
||||||
IFilenameSampleService filenameSampleService,
|
|
||||||
IFilenameValidationService filenameValidationService,
|
|
||||||
IBuildFileNames filenameBuilder)
|
|
||||||
: base("config/naming")
|
|
||||||
{
|
|
||||||
_namingConfigService = namingConfigService;
|
|
||||||
_filenameSampleService = filenameSampleService;
|
|
||||||
_filenameValidationService = filenameValidationService;
|
|
||||||
_filenameBuilder = filenameBuilder;
|
|
||||||
GetResourceSingle = GetNamingConfig;
|
|
||||||
GetResourceById = GetNamingConfig;
|
|
||||||
UpdateResource = UpdateNamingConfig;
|
|
||||||
|
|
||||||
Get("/samples", x => GetExamples(this.Bind<NamingConfigResource>()));
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.MultiEpisodeStyle).InclusiveBetween(0, 5);
|
|
||||||
SharedValidator.RuleFor(c => c.StandardEpisodeFormat).ValidEpisodeFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.DailyEpisodeFormat).ValidDailyEpisodeFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.AnimeEpisodeFormat).ValidAnimeEpisodeFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.SeriesFolderFormat).ValidSeriesFolderFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.SeasonFolderFormat).ValidSeasonFolderFormat();
|
|
||||||
SharedValidator.RuleFor(c => c.SpecialsFolderFormat).ValidSpecialsFolderFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateNamingConfig(NamingConfigResource resource)
|
|
||||||
{
|
|
||||||
var nameSpec = resource.ToModel();
|
|
||||||
ValidateFormatResult(nameSpec);
|
|
||||||
|
|
||||||
_namingConfigService.Save(nameSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private NamingConfigResource GetNamingConfig()
|
|
||||||
{
|
|
||||||
var nameSpec = _namingConfigService.GetConfig();
|
|
||||||
var resource = nameSpec.ToResource();
|
|
||||||
|
|
||||||
if (resource.StandardEpisodeFormat.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec);
|
|
||||||
basicConfig.AddToResource(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private NamingConfigResource GetNamingConfig(int id)
|
|
||||||
{
|
|
||||||
return GetNamingConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetExamples(NamingConfigResource config)
|
|
||||||
{
|
|
||||||
var nameSpec = config.ToModel();
|
|
||||||
var sampleResource = new NamingSampleResource();
|
|
||||||
|
|
||||||
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
|
||||||
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
|
||||||
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
|
||||||
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
|
||||||
var animeMultiEpisodeSampleResult = _filenameSampleService.GetAnimeMultiEpisodeSample(nameSpec);
|
|
||||||
|
|
||||||
sampleResource.SingleEpisodeExample = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: singleEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.MultiEpisodeExample = _filenameValidationService.ValidateStandardFilename(multiEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: multiEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.DailyEpisodeExample = _filenameValidationService.ValidateDailyFilename(dailyEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: dailyEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.AnimeEpisodeExample = _filenameValidationService.ValidateAnimeFilename(animeEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: animeEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.AnimeMultiEpisodeExample = _filenameValidationService.ValidateAnimeFilename(animeMultiEpisodeSampleResult) != null
|
|
||||||
? "Invalid format"
|
|
||||||
: animeMultiEpisodeSampleResult.FileName;
|
|
||||||
|
|
||||||
sampleResource.SeriesFolderExample = nameSpec.SeriesFolderFormat.IsNullOrWhiteSpace()
|
|
||||||
? "Invalid format"
|
|
||||||
: _filenameSampleService.GetSeriesFolderSample(nameSpec);
|
|
||||||
|
|
||||||
sampleResource.SeasonFolderExample = nameSpec.SeasonFolderFormat.IsNullOrWhiteSpace()
|
|
||||||
? "Invalid format"
|
|
||||||
: _filenameSampleService.GetSeasonFolderSample(nameSpec);
|
|
||||||
|
|
||||||
sampleResource.SpecialsFolderExample = nameSpec.SpecialsFolderFormat.IsNullOrWhiteSpace()
|
|
||||||
? "Invalid format"
|
|
||||||
: _filenameSampleService.GetSpecialsFolderSample(nameSpec);
|
|
||||||
|
|
||||||
return sampleResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ValidateFormatResult(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
|
||||||
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
|
||||||
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
|
||||||
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
|
||||||
var animeMultiEpisodeSampleResult = _filenameSampleService.GetAnimeMultiEpisodeSample(nameSpec);
|
|
||||||
|
|
||||||
var singleEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult);
|
|
||||||
var multiEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(multiEpisodeSampleResult);
|
|
||||||
var dailyEpisodeValidationResult = _filenameValidationService.ValidateDailyFilename(dailyEpisodeSampleResult);
|
|
||||||
var animeEpisodeValidationResult = _filenameValidationService.ValidateAnimeFilename(animeEpisodeSampleResult);
|
|
||||||
var animeMultiEpisodeValidationResult = _filenameValidationService.ValidateAnimeFilename(animeMultiEpisodeSampleResult);
|
|
||||||
|
|
||||||
var validationFailures = new List<ValidationFailure>();
|
|
||||||
|
|
||||||
validationFailures.AddIfNotNull(singleEpisodeValidationResult);
|
|
||||||
validationFailures.AddIfNotNull(multiEpisodeValidationResult);
|
|
||||||
validationFailures.AddIfNotNull(dailyEpisodeValidationResult);
|
|
||||||
validationFailures.AddIfNotNull(animeEpisodeValidationResult);
|
|
||||||
validationFailures.AddIfNotNull(animeMultiEpisodeValidationResult);
|
|
||||||
|
|
||||||
if (validationFailures.Any())
|
|
||||||
{
|
|
||||||
throw new ValidationException(validationFailures.DistinctBy(v => v.PropertyName).ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Organizer;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class NamingConfigResource : RestResource
|
|
||||||
{
|
|
||||||
public bool RenameEpisodes { get; set; }
|
|
||||||
public bool ReplaceIllegalCharacters { get; set; }
|
|
||||||
public int MultiEpisodeStyle { get; set; }
|
|
||||||
public string StandardEpisodeFormat { get; set; }
|
|
||||||
public string DailyEpisodeFormat { get; set; }
|
|
||||||
public string AnimeEpisodeFormat { get; set; }
|
|
||||||
public string SeriesFolderFormat { get; set; }
|
|
||||||
public string SeasonFolderFormat { get; set; }
|
|
||||||
public string SpecialsFolderFormat { get; set; }
|
|
||||||
public bool IncludeSeriesTitle { get; set; }
|
|
||||||
public bool IncludeEpisodeTitle { get; set; }
|
|
||||||
public bool IncludeQuality { get; set; }
|
|
||||||
public bool ReplaceSpaces { get; set; }
|
|
||||||
public string Separator { get; set; }
|
|
||||||
public string NumberStyle { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class NamingConfigResourceMapper
|
|
||||||
{
|
|
||||||
public static NamingConfigResource ToResource(this NamingConfig model)
|
|
||||||
{
|
|
||||||
return new NamingConfigResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
RenameEpisodes = model.RenameEpisodes,
|
|
||||||
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
|
||||||
MultiEpisodeStyle = model.MultiEpisodeStyle,
|
|
||||||
StandardEpisodeFormat = model.StandardEpisodeFormat,
|
|
||||||
DailyEpisodeFormat = model.DailyEpisodeFormat,
|
|
||||||
AnimeEpisodeFormat = model.AnimeEpisodeFormat,
|
|
||||||
SeriesFolderFormat = model.SeriesFolderFormat,
|
|
||||||
SeasonFolderFormat = model.SeasonFolderFormat,
|
|
||||||
SpecialsFolderFormat = model.SpecialsFolderFormat
|
|
||||||
//IncludeSeriesTitle
|
|
||||||
//IncludeEpisodeTitle
|
|
||||||
//IncludeQuality
|
|
||||||
//ReplaceSpaces
|
|
||||||
//Separator
|
|
||||||
//NumberStyle
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddToResource(this BasicNamingConfig basicNamingConfig, NamingConfigResource resource)
|
|
||||||
{
|
|
||||||
resource.IncludeSeriesTitle = basicNamingConfig.IncludeSeriesTitle;
|
|
||||||
resource.IncludeEpisodeTitle = basicNamingConfig.IncludeEpisodeTitle;
|
|
||||||
resource.IncludeQuality = basicNamingConfig.IncludeQuality;
|
|
||||||
resource.ReplaceSpaces = basicNamingConfig.ReplaceSpaces;
|
|
||||||
resource.Separator = basicNamingConfig.Separator;
|
|
||||||
resource.NumberStyle = basicNamingConfig.NumberStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static NamingConfig ToModel(this NamingConfigResource resource)
|
|
||||||
{
|
|
||||||
return new NamingConfig
|
|
||||||
{
|
|
||||||
Id = resource.Id,
|
|
||||||
|
|
||||||
RenameEpisodes = resource.RenameEpisodes,
|
|
||||||
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
|
|
||||||
MultiEpisodeStyle = resource.MultiEpisodeStyle,
|
|
||||||
StandardEpisodeFormat = resource.StandardEpisodeFormat,
|
|
||||||
DailyEpisodeFormat = resource.DailyEpisodeFormat,
|
|
||||||
AnimeEpisodeFormat = resource.AnimeEpisodeFormat,
|
|
||||||
SeriesFolderFormat = resource.SeriesFolderFormat,
|
|
||||||
SeasonFolderFormat = resource.SeasonFolderFormat,
|
|
||||||
SpecialsFolderFormat = resource.SpecialsFolderFormat
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class NamingSampleResource
|
|
||||||
{
|
|
||||||
public string SingleEpisodeExample { get; set; }
|
|
||||||
public string MultiEpisodeExample { get; set; }
|
|
||||||
public string DailyEpisodeExample { get; set; }
|
|
||||||
public string AnimeEpisodeExample { get; set; }
|
|
||||||
public string AnimeMultiEpisodeExample { get; set; }
|
|
||||||
public string SeriesFolderExample { get; set; }
|
|
||||||
public string SeasonFolderExample { get; set; }
|
|
||||||
public string SpecialsFolderExample { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public abstract class NzbDroneConfigModule<TResource> : SonarrRestModule<TResource> where TResource : RestResource, new()
|
|
||||||
{
|
|
||||||
private readonly IConfigService _configService;
|
|
||||||
|
|
||||||
protected NzbDroneConfigModule(IConfigService configService)
|
|
||||||
: this(new TResource().ResourceName.Replace("config", ""), configService)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected NzbDroneConfigModule(string resource, IConfigService configService) :
|
|
||||||
base("config/" + resource.Trim('/'))
|
|
||||||
{
|
|
||||||
_configService = configService;
|
|
||||||
|
|
||||||
GetResourceSingle = GetConfig;
|
|
||||||
GetResourceById = GetConfig;
|
|
||||||
UpdateResource = SaveConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TResource GetConfig()
|
|
||||||
{
|
|
||||||
var resource = ToResource(_configService);
|
|
||||||
resource.Id = 1;
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract TResource ToResource(IConfigService model);
|
|
||||||
|
|
||||||
private TResource GetConfig(int id)
|
|
||||||
{
|
|
||||||
return GetConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveConfig(TResource resource)
|
|
||||||
{
|
|
||||||
var dictionary = resource.GetType()
|
|
||||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
|
||||||
.ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null));
|
|
||||||
|
|
||||||
_configService.SaveConfigDictionary(dictionary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class UiConfigModule : NzbDroneConfigModule<UiConfigResource>
|
|
||||||
{
|
|
||||||
public UiConfigModule(IConfigService configService)
|
|
||||||
: base(configService)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override UiConfigResource ToResource(IConfigService model)
|
|
||||||
{
|
|
||||||
return UiConfigResourceMapper.ToResource(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Config
|
|
||||||
{
|
|
||||||
public class UiConfigResource : RestResource
|
|
||||||
{
|
|
||||||
//Calendar
|
|
||||||
public int FirstDayOfWeek { get; set; }
|
|
||||||
public string CalendarWeekColumnHeader { get; set; }
|
|
||||||
|
|
||||||
//Dates
|
|
||||||
public string ShortDateFormat { get; set; }
|
|
||||||
public string LongDateFormat { get; set; }
|
|
||||||
public string TimeFormat { get; set; }
|
|
||||||
public bool ShowRelativeDates { get; set; }
|
|
||||||
|
|
||||||
public bool EnableColorImpairedMode { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UiConfigResourceMapper
|
|
||||||
{
|
|
||||||
public static UiConfigResource ToResource(IConfigService model)
|
|
||||||
{
|
|
||||||
return new UiConfigResource
|
|
||||||
{
|
|
||||||
FirstDayOfWeek = model.FirstDayOfWeek,
|
|
||||||
CalendarWeekColumnHeader = model.CalendarWeekColumnHeader,
|
|
||||||
|
|
||||||
ShortDateFormat = model.ShortDateFormat,
|
|
||||||
LongDateFormat = model.LongDateFormat,
|
|
||||||
TimeFormat = model.TimeFormat,
|
|
||||||
ShowRelativeDates = model.ShowRelativeDates,
|
|
||||||
|
|
||||||
EnableColorImpairedMode = model.EnableColorImpairedMode,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Core.DiskSpace;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.DiskSpace
|
|
||||||
{
|
|
||||||
public class DiskSpaceModule :SonarrRestModule<DiskSpaceResource>
|
|
||||||
{
|
|
||||||
private readonly IDiskSpaceService _diskSpaceService;
|
|
||||||
|
|
||||||
public DiskSpaceModule(IDiskSpaceService diskSpaceService)
|
|
||||||
: base("diskspace")
|
|
||||||
{
|
|
||||||
_diskSpaceService = diskSpaceService;
|
|
||||||
GetResourceAll = GetFreeSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public List<DiskSpaceResource> GetFreeSpace()
|
|
||||||
{
|
|
||||||
return _diskSpaceService.GetFreeSpace().ConvertAll(DiskSpaceResourceMapper.MapToResource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.DiskSpace
|
|
||||||
{
|
|
||||||
public class DiskSpaceResource : RestResource
|
|
||||||
{
|
|
||||||
public string Path { get; set; }
|
|
||||||
public string Label { get; set; }
|
|
||||||
public long FreeSpace { get; set; }
|
|
||||||
public long TotalSpace { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DiskSpaceResourceMapper
|
|
||||||
{
|
|
||||||
public static DiskSpaceResource MapToResource(this Core.DiskSpace.DiskSpace model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new DiskSpaceResource
|
|
||||||
{
|
|
||||||
Path = model.Path,
|
|
||||||
Label = model.Label,
|
|
||||||
FreeSpace = model.FreeSpace,
|
|
||||||
TotalSpace = model.TotalSpace
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
using NzbDrone.Core.Download;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.DownloadClient
|
|
||||||
{
|
|
||||||
public class DownloadClientModule : ProviderModuleBase<DownloadClientResource, IDownloadClient, DownloadClientDefinition>
|
|
||||||
{
|
|
||||||
public DownloadClientModule(IDownloadClientFactory downloadClientFactory)
|
|
||||||
: base(downloadClientFactory, "downloadclient")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MapToResource(DownloadClientResource resource, DownloadClientDefinition definition)
|
|
||||||
{
|
|
||||||
base.MapToResource(resource, definition);
|
|
||||||
|
|
||||||
resource.Enable = definition.Enable;
|
|
||||||
resource.Protocol = definition.Protocol;
|
|
||||||
resource.Priority = definition.Priority;
|
|
||||||
resource.RemoveCompletedDownloads = definition.RemoveCompletedDownloads;
|
|
||||||
resource.RemoveFailedDownloads = definition.RemoveFailedDownloads;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MapToModel(DownloadClientDefinition definition, DownloadClientResource resource)
|
|
||||||
{
|
|
||||||
base.MapToModel(definition, resource);
|
|
||||||
|
|
||||||
definition.Enable = resource.Enable;
|
|
||||||
definition.Protocol = resource.Protocol;
|
|
||||||
definition.Priority = resource.Priority;
|
|
||||||
definition.RemoveCompletedDownloads = resource.RemoveCompletedDownloads;
|
|
||||||
definition.RemoveFailedDownloads = resource.RemoveFailedDownloads;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.DownloadClient
|
|
||||||
{
|
|
||||||
public class DownloadClientResource : ProviderResource
|
|
||||||
{
|
|
||||||
public bool Enable { get; set; }
|
|
||||||
public DownloadProtocol Protocol { get; set; }
|
|
||||||
public int Priority { get; set; }
|
|
||||||
public bool RemoveCompletedDownloads { get; set; }
|
|
||||||
public bool RemoveFailedDownloads { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Core.Datastore.Events;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
|
||||||
using NzbDrone.Core.Exceptions;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using HttpStatusCode = System.Net.HttpStatusCode;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.EpisodeFiles
|
|
||||||
{
|
|
||||||
public class EpisodeFileModule : SonarrRestModuleWithSignalR<EpisodeFileResource, EpisodeFile>,
|
|
||||||
IHandle<EpisodeFileAddedEvent>
|
|
||||||
{
|
|
||||||
private readonly IMediaFileService _mediaFileService;
|
|
||||||
private readonly IDeleteMediaFiles _mediaFileDeletionService;
|
|
||||||
private readonly ISeriesService _seriesService;
|
|
||||||
private readonly IUpgradableSpecification _upgradableSpecification;
|
|
||||||
|
|
||||||
public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster,
|
|
||||||
IMediaFileService mediaFileService,
|
|
||||||
IDeleteMediaFiles mediaFileDeletionService,
|
|
||||||
ISeriesService seriesService,
|
|
||||||
IUpgradableSpecification upgradableSpecification)
|
|
||||||
: base(signalRBroadcaster)
|
|
||||||
{
|
|
||||||
_mediaFileService = mediaFileService;
|
|
||||||
_mediaFileDeletionService = mediaFileDeletionService;
|
|
||||||
_seriesService = seriesService;
|
|
||||||
_upgradableSpecification = upgradableSpecification;
|
|
||||||
GetResourceById = GetEpisodeFile;
|
|
||||||
GetResourceAll = GetEpisodeFiles;
|
|
||||||
UpdateResource = SetQuality;
|
|
||||||
DeleteResource = DeleteEpisodeFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EpisodeFileResource GetEpisodeFile(int id)
|
|
||||||
{
|
|
||||||
var episodeFile = _mediaFileService.Get(id);
|
|
||||||
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
|
||||||
|
|
||||||
return episodeFile.ToResource(series, _upgradableSpecification);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<EpisodeFileResource> GetEpisodeFiles()
|
|
||||||
{
|
|
||||||
if (!Request.Query.SeriesId.HasValue)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("seriesId is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
var seriesId = (int)Request.Query.SeriesId;
|
|
||||||
|
|
||||||
var series = _seriesService.GetSeries(seriesId);
|
|
||||||
|
|
||||||
return _mediaFileService.GetFilesBySeries(seriesId).ConvertAll(f => f.ToResource(series, _upgradableSpecification));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetQuality(EpisodeFileResource episodeFileResource)
|
|
||||||
{
|
|
||||||
var episodeFile = _mediaFileService.Get(episodeFileResource.Id);
|
|
||||||
episodeFile.Quality = episodeFileResource.Quality;
|
|
||||||
episodeFile.Language = episodeFileResource.Language;
|
|
||||||
_mediaFileService.Update(episodeFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteEpisodeFile(int id)
|
|
||||||
{
|
|
||||||
var episodeFile = _mediaFileService.Get(id);
|
|
||||||
|
|
||||||
if (episodeFile == null)
|
|
||||||
{
|
|
||||||
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Episode file not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
|
||||||
|
|
||||||
_mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(EpisodeFileAddedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using NzbDrone.Core.Languages;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.EpisodeFiles
|
|
||||||
{
|
|
||||||
public class EpisodeFileResource : RestResource
|
|
||||||
{
|
|
||||||
public int SeriesId { get; set; }
|
|
||||||
public int SeasonNumber { get; set; }
|
|
||||||
public string RelativePath { get; set; }
|
|
||||||
public string Path { get; set; }
|
|
||||||
public long Size { get; set; }
|
|
||||||
public DateTime DateAdded { get; set; }
|
|
||||||
public string SceneName { get; set; }
|
|
||||||
public QualityModel Quality { get; set; }
|
|
||||||
public Language Language { get; set; }
|
|
||||||
public MediaInfoResource MediaInfo { get; set; }
|
|
||||||
public string OriginalFilePath { get; set; }
|
|
||||||
|
|
||||||
public bool QualityCutoffNotMet { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class EpisodeFileResourceMapper
|
|
||||||
{
|
|
||||||
private static EpisodeFileResource ToResource(this EpisodeFile model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new EpisodeFileResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
SeriesId = model.SeriesId,
|
|
||||||
SeasonNumber = model.SeasonNumber,
|
|
||||||
RelativePath = model.RelativePath,
|
|
||||||
//Path
|
|
||||||
Size = model.Size,
|
|
||||||
DateAdded = model.DateAdded,
|
|
||||||
SceneName = model.SceneName,
|
|
||||||
Quality = model.Quality,
|
|
||||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
|
|
||||||
OriginalFilePath = model.OriginalFilePath
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EpisodeFileResource ToResource(this EpisodeFile model, Core.Tv.Series series, IUpgradableSpecification upgradableSpecification)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new EpisodeFileResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
SeriesId = model.SeriesId,
|
|
||||||
SeasonNumber = model.SeasonNumber,
|
|
||||||
RelativePath = model.RelativePath,
|
|
||||||
Path = Path.Combine(series.Path, model.RelativePath),
|
|
||||||
Size = model.Size,
|
|
||||||
DateAdded = model.DateAdded,
|
|
||||||
SceneName = model.SceneName,
|
|
||||||
Quality = model.Quality,
|
|
||||||
Language = model.Language,
|
|
||||||
QualityCutoffNotMet = upgradableSpecification.QualityCutoffNotMet(series.QualityProfile.Value, model.Quality),
|
|
||||||
MediaInfo = model.MediaInfo.ToResource(model.SceneName),
|
|
||||||
OriginalFilePath = model.OriginalFilePath
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.EpisodeFiles
|
|
||||||
{
|
|
||||||
public class MediaInfoResource : RestResource
|
|
||||||
{
|
|
||||||
public decimal AudioChannels { get; set; }
|
|
||||||
public string AudioCodec { get; set; }
|
|
||||||
public string VideoCodec { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MediaInfoResourceMapper
|
|
||||||
{
|
|
||||||
public static MediaInfoResource ToResource(this MediaInfoModel model, string sceneName)
|
|
||||||
{
|
|
||||||
if (model == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MediaInfoResource
|
|
||||||
{
|
|
||||||
AudioChannels = MediaInfoFormatter.FormatAudioChannels(model),
|
|
||||||
AudioCodec = MediaInfoFormatter.FormatAudioCodec(model, sceneName),
|
|
||||||
VideoCodec = MediaInfoFormatter.FormatVideoCodec(model, sceneName)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
|
||||||
{
|
|
||||||
public class EpisodeModule : EpisodeModuleWithSignalR
|
|
||||||
{
|
|
||||||
public EpisodeModule(ISeriesService seriesService,
|
|
||||||
IEpisodeService episodeService,
|
|
||||||
IUpgradableSpecification upgradableSpecification,
|
|
||||||
IBroadcastSignalRMessage signalRBroadcaster)
|
|
||||||
: base(episodeService, seriesService, upgradableSpecification, signalRBroadcaster)
|
|
||||||
{
|
|
||||||
GetResourceAll = GetEpisodes;
|
|
||||||
UpdateResource = SetMonitored;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<EpisodeResource> GetEpisodes()
|
|
||||||
{
|
|
||||||
if (!Request.Query.SeriesId.HasValue)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("seriesId is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
var seriesId = (int)Request.Query.SeriesId;
|
|
||||||
|
|
||||||
var resources = MapToResource(_episodeService.GetEpisodeBySeries(seriesId), false, true);
|
|
||||||
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetMonitored(EpisodeResource episodeResource)
|
|
||||||
{
|
|
||||||
_episodeService.SetEpisodeMonitored(episodeResource.Id, episodeResource.Monitored);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Api.EpisodeFiles;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Datastore.Events;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
|
||||||
using NzbDrone.Core.Download;
|
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
|
||||||
{
|
|
||||||
public abstract class EpisodeModuleWithSignalR : SonarrRestModuleWithSignalR<EpisodeResource, Episode>,
|
|
||||||
IHandle<EpisodeGrabbedEvent>,
|
|
||||||
IHandle<EpisodeImportedEvent>
|
|
||||||
{
|
|
||||||
protected readonly IEpisodeService _episodeService;
|
|
||||||
protected readonly ISeriesService _seriesService;
|
|
||||||
protected readonly IUpgradableSpecification _upgradableSpecification;
|
|
||||||
|
|
||||||
protected EpisodeModuleWithSignalR(IEpisodeService episodeService,
|
|
||||||
ISeriesService seriesService,
|
|
||||||
IUpgradableSpecification upgradableSpecification,
|
|
||||||
IBroadcastSignalRMessage signalRBroadcaster)
|
|
||||||
: base(signalRBroadcaster)
|
|
||||||
{
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_seriesService = seriesService;
|
|
||||||
_upgradableSpecification = upgradableSpecification;
|
|
||||||
|
|
||||||
GetResourceById = GetEpisode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected EpisodeModuleWithSignalR(IEpisodeService episodeService,
|
|
||||||
ISeriesService seriesService,
|
|
||||||
IUpgradableSpecification upgradableSpecification,
|
|
||||||
IBroadcastSignalRMessage signalRBroadcaster,
|
|
||||||
string resource)
|
|
||||||
: base(signalRBroadcaster, resource)
|
|
||||||
{
|
|
||||||
_episodeService = episodeService;
|
|
||||||
_seriesService = seriesService;
|
|
||||||
_upgradableSpecification = upgradableSpecification;
|
|
||||||
|
|
||||||
GetResourceById = GetEpisode;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected EpisodeResource GetEpisode(int id)
|
|
||||||
{
|
|
||||||
var episode = _episodeService.GetEpisode(id);
|
|
||||||
var resource = MapToResource(episode, true, true);
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected EpisodeResource MapToResource(Episode episode, bool includeSeries, bool includeEpisodeFile)
|
|
||||||
{
|
|
||||||
var resource = episode.ToResource();
|
|
||||||
|
|
||||||
if (includeSeries || includeEpisodeFile)
|
|
||||||
{
|
|
||||||
var series = episode.Series ?? _seriesService.GetSeries(episode.SeriesId);
|
|
||||||
|
|
||||||
if (includeSeries)
|
|
||||||
{
|
|
||||||
resource.Series = series.ToResource();
|
|
||||||
}
|
|
||||||
if (includeEpisodeFile && episode.EpisodeFileId != 0)
|
|
||||||
{
|
|
||||||
resource.EpisodeFile = episode.EpisodeFile.Value.ToResource(series, _upgradableSpecification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected List<EpisodeResource> MapToResource(List<Episode> episodes, bool includeSeries, bool includeEpisodeFile)
|
|
||||||
{
|
|
||||||
var result = episodes.ToResource();
|
|
||||||
|
|
||||||
if (includeSeries || includeEpisodeFile)
|
|
||||||
{
|
|
||||||
var seriesDict = new Dictionary<int, Core.Tv.Series>();
|
|
||||||
for (var i = 0; i < episodes.Count; i++)
|
|
||||||
{
|
|
||||||
var episode = episodes[i];
|
|
||||||
var resource = result[i];
|
|
||||||
|
|
||||||
var series = episode.Series ?? seriesDict.GetValueOrDefault(episodes[i].SeriesId) ?? _seriesService.GetSeries(episodes[i].SeriesId);
|
|
||||||
seriesDict[series.Id] = series;
|
|
||||||
|
|
||||||
if (includeSeries)
|
|
||||||
{
|
|
||||||
resource.Series = series.ToResource();
|
|
||||||
}
|
|
||||||
if (includeEpisodeFile && episodes[i].EpisodeFileId != 0)
|
|
||||||
{
|
|
||||||
resource.EpisodeFile = episodes[i].EpisodeFile.Value.ToResource(series, _upgradableSpecification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(EpisodeGrabbedEvent message)
|
|
||||||
{
|
|
||||||
foreach (var episode in message.Episode.Episodes)
|
|
||||||
{
|
|
||||||
var resource = episode.ToResource();
|
|
||||||
resource.Grabbed = true;
|
|
||||||
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(EpisodeImportedEvent message)
|
|
||||||
{
|
|
||||||
if (!message.NewDownload)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var episode in message.EpisodeInfo.Episodes)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, episode.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NzbDrone.Api.EpisodeFiles;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
|
||||||
{
|
|
||||||
public class EpisodeResource : RestResource
|
|
||||||
{
|
|
||||||
public int SeriesId { get; set; }
|
|
||||||
public int EpisodeFileId { get; set; }
|
|
||||||
public int SeasonNumber { get; set; }
|
|
||||||
public int EpisodeNumber { get; set; }
|
|
||||||
public string Title { get; set; }
|
|
||||||
public string AirDate { get; set; }
|
|
||||||
public DateTime? AirDateUtc { get; set; }
|
|
||||||
public string Overview { get; set; }
|
|
||||||
public EpisodeFileResource EpisodeFile { get; set; }
|
|
||||||
|
|
||||||
public bool HasFile { get; set; }
|
|
||||||
public bool Monitored { get; set; }
|
|
||||||
public int? AbsoluteEpisodeNumber { get; set; }
|
|
||||||
public int? SceneAbsoluteEpisodeNumber { get; set; }
|
|
||||||
public int? SceneEpisodeNumber { get; set; }
|
|
||||||
public int? SceneSeasonNumber { get; set; }
|
|
||||||
public bool UnverifiedSceneNumbering { get; set; }
|
|
||||||
public string SeriesTitle { get; set; }
|
|
||||||
public SeriesResource Series { get; set; }
|
|
||||||
public DateTime? LastSearchTime { get; set; }
|
|
||||||
|
|
||||||
//Hiding this so people don't think its usable (only used to set the initial state)
|
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
|
||||||
public bool Grabbed { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class EpisodeResourceMapper
|
|
||||||
{
|
|
||||||
public static EpisodeResource ToResource(this Episode model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new EpisodeResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
SeriesId = model.SeriesId,
|
|
||||||
EpisodeFileId = model.EpisodeFileId,
|
|
||||||
SeasonNumber = model.SeasonNumber,
|
|
||||||
EpisodeNumber = model.EpisodeNumber,
|
|
||||||
Title = model.Title,
|
|
||||||
AirDate = model.AirDate,
|
|
||||||
AirDateUtc = model.AirDateUtc,
|
|
||||||
Overview = model.Overview,
|
|
||||||
//EpisodeFile
|
|
||||||
|
|
||||||
HasFile = model.HasFile,
|
|
||||||
Monitored = model.Monitored,
|
|
||||||
AbsoluteEpisodeNumber = model.AbsoluteEpisodeNumber,
|
|
||||||
SceneAbsoluteEpisodeNumber = model.SceneAbsoluteEpisodeNumber,
|
|
||||||
SceneEpisodeNumber = model.SceneEpisodeNumber,
|
|
||||||
SceneSeasonNumber = model.SceneSeasonNumber,
|
|
||||||
UnverifiedSceneNumbering = model.UnverifiedSceneNumbering,
|
|
||||||
SeriesTitle = model.SeriesTitle,
|
|
||||||
//Series = model.Series.MapToResource(),
|
|
||||||
LastSearchTime = model.LastSearchTime
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<EpisodeResource> ToResource(this IEnumerable<Episode> models)
|
|
||||||
{
|
|
||||||
if (models == null) return null;
|
|
||||||
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
|
||||||
{
|
|
||||||
public class RenameEpisodeModule : SonarrRestModule<RenameEpisodeResource>
|
|
||||||
{
|
|
||||||
private readonly IRenameEpisodeFileService _renameEpisodeFileService;
|
|
||||||
|
|
||||||
public RenameEpisodeModule(IRenameEpisodeFileService renameEpisodeFileService)
|
|
||||||
: base("rename")
|
|
||||||
{
|
|
||||||
_renameEpisodeFileService = renameEpisodeFileService;
|
|
||||||
|
|
||||||
GetResourceAll = GetEpisodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<RenameEpisodeResource> GetEpisodes()
|
|
||||||
{
|
|
||||||
if (!Request.Query.SeriesId.HasValue)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("seriesId is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
var seriesId = (int)Request.Query.SeriesId;
|
|
||||||
|
|
||||||
if (Request.Query.SeasonNumber.HasValue)
|
|
||||||
{
|
|
||||||
var seasonNumber = (int)Request.Query.SeasonNumber;
|
|
||||||
return _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _renameEpisodeFileService.GetRenamePreviews(seriesId).ToResource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Episodes
|
|
||||||
{
|
|
||||||
public class RenameEpisodeResource : RestResource
|
|
||||||
{
|
|
||||||
public int SeriesId { get; set; }
|
|
||||||
public int SeasonNumber { get; set; }
|
|
||||||
public List<int> EpisodeNumbers { get; set; }
|
|
||||||
public int EpisodeFileId { get; set; }
|
|
||||||
public string ExistingPath { get; set; }
|
|
||||||
public string NewPath { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RenameEpisodeResourceMapper
|
|
||||||
{
|
|
||||||
public static RenameEpisodeResource ToResource(this Core.MediaFiles.RenameEpisodeFilePreview model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new RenameEpisodeResource
|
|
||||||
{
|
|
||||||
SeriesId = model.SeriesId,
|
|
||||||
SeasonNumber = model.SeasonNumber,
|
|
||||||
EpisodeNumbers = model.EpisodeNumbers.ToList(),
|
|
||||||
EpisodeFileId = model.EpisodeFileId,
|
|
||||||
ExistingPath = model.ExistingPath,
|
|
||||||
NewPath = model.NewPath
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<RenameEpisodeResource> ToResource(this IEnumerable<Core.MediaFiles.RenameEpisodeFilePreview> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Nancy;
|
|
||||||
using Sonarr.Http.Extensions;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.FileSystem
|
|
||||||
{
|
|
||||||
public class FileSystemModule : NzbDroneApiModule
|
|
||||||
{
|
|
||||||
private readonly IFileSystemLookupService _fileSystemLookupService;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
private readonly IDiskScanService _diskScanService;
|
|
||||||
|
|
||||||
public FileSystemModule(IFileSystemLookupService fileSystemLookupService,
|
|
||||||
IDiskProvider diskProvider,
|
|
||||||
IDiskScanService diskScanService)
|
|
||||||
: base("/filesystem")
|
|
||||||
{
|
|
||||||
_fileSystemLookupService = fileSystemLookupService;
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
_diskScanService = diskScanService;
|
|
||||||
Get("/", x => GetContents());
|
|
||||||
Get("/type", x => GetEntityType());
|
|
||||||
Get("/mediafiles", x => GetMediaFiles());
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetContents()
|
|
||||||
{
|
|
||||||
var pathQuery = Request.Query.path;
|
|
||||||
var includeFiles = Request.GetBooleanQueryParameter("includeFiles");
|
|
||||||
var allowFoldersWithoutTrailingSlashes = Request.GetBooleanQueryParameter("allowFoldersWithoutTrailingSlashes");
|
|
||||||
|
|
||||||
return _fileSystemLookupService.LookupContents((string)pathQuery.Value, includeFiles, allowFoldersWithoutTrailingSlashes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetEntityType()
|
|
||||||
{
|
|
||||||
var pathQuery = Request.Query.path;
|
|
||||||
var path = (string)pathQuery.Value;
|
|
||||||
|
|
||||||
if (_diskProvider.FileExists(path))
|
|
||||||
{
|
|
||||||
return new { type = "file" };
|
|
||||||
}
|
|
||||||
|
|
||||||
//Return folder even if it doesn't exist on disk to avoid leaking anything from the UI about the underlying system
|
|
||||||
return new { type = "folder" };
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetMediaFiles()
|
|
||||||
{
|
|
||||||
var pathQuery = Request.Query.path;
|
|
||||||
var path = (string)pathQuery.Value;
|
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(path))
|
|
||||||
{
|
|
||||||
return new string[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return _diskScanService.GetVideoFiles(path).Select(f => new {
|
|
||||||
Path = f,
|
|
||||||
RelativePath = path.GetRelativePath(f),
|
|
||||||
Name = Path.GetFileName(f)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Core.Datastore.Events;
|
|
||||||
using NzbDrone.Core.HealthCheck;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Health
|
|
||||||
{
|
|
||||||
public class HealthModule : SonarrRestModuleWithSignalR<HealthResource, HealthCheck>,
|
|
||||||
IHandle<HealthCheckCompleteEvent>
|
|
||||||
{
|
|
||||||
private readonly IHealthCheckService _healthCheckService;
|
|
||||||
|
|
||||||
public HealthModule(IBroadcastSignalRMessage signalRBroadcaster, IHealthCheckService healthCheckService)
|
|
||||||
: base(signalRBroadcaster)
|
|
||||||
{
|
|
||||||
_healthCheckService = healthCheckService;
|
|
||||||
GetResourceAll = GetHealth;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<HealthResource> GetHealth()
|
|
||||||
{
|
|
||||||
return _healthCheckService.Results().ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(HealthCheckCompleteEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Sync);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Common.Http;
|
|
||||||
using NzbDrone.Core.HealthCheck;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Health
|
|
||||||
{
|
|
||||||
public class HealthResource : RestResource
|
|
||||||
{
|
|
||||||
public HealthCheckResult Type { get; set; }
|
|
||||||
public string Message { get; set; }
|
|
||||||
public HttpUri WikiUrl { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class HealthResourceMapper
|
|
||||||
{
|
|
||||||
public static HealthResource ToResource(this HealthCheck model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new HealthResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Type = model.Type,
|
|
||||||
Message = model.Message,
|
|
||||||
WikiUrl = model.WikiUrl
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<HealthResource> ToResource(this IEnumerable<HealthCheck> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Nancy;
|
|
||||||
using NzbDrone.Api.Episodes;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
|
||||||
using NzbDrone.Core.Download;
|
|
||||||
using NzbDrone.Core.History;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.History
|
|
||||||
{
|
|
||||||
public class HistoryModule : SonarrRestModule<HistoryResource>
|
|
||||||
{
|
|
||||||
private readonly IHistoryService _historyService;
|
|
||||||
private readonly IUpgradableSpecification _upgradableSpecification;
|
|
||||||
private readonly IFailedDownloadService _failedDownloadService;
|
|
||||||
|
|
||||||
public HistoryModule(IHistoryService historyService,
|
|
||||||
IUpgradableSpecification upgradableSpecification,
|
|
||||||
IFailedDownloadService failedDownloadService)
|
|
||||||
{
|
|
||||||
_historyService = historyService;
|
|
||||||
_upgradableSpecification = upgradableSpecification;
|
|
||||||
_failedDownloadService = failedDownloadService;
|
|
||||||
GetResourcePaged = GetHistory;
|
|
||||||
|
|
||||||
Get("/since", x => GetHistorySince());
|
|
||||||
Post("/failed", x => MarkAsFailed());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HistoryResource MapToResource(EpisodeHistory model)
|
|
||||||
{
|
|
||||||
var resource = model.ToResource();
|
|
||||||
|
|
||||||
resource.Series = model.Series.ToResource();
|
|
||||||
resource.Episode = model.Episode.ToResource();
|
|
||||||
|
|
||||||
if (model.Series != null)
|
|
||||||
{
|
|
||||||
resource.QualityCutoffNotMet = _upgradableSpecification.QualityCutoffNotMet(model.Series.QualityProfile.Value, model.Quality);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PagingResource<HistoryResource> GetHistory(PagingResource<HistoryResource> pagingResource)
|
|
||||||
{
|
|
||||||
var episodeId = Request.Query.EpisodeId;
|
|
||||||
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, EpisodeHistory>("date", SortDirection.Descending);
|
|
||||||
var filter = pagingResource.Filters.FirstOrDefault();
|
|
||||||
|
|
||||||
if (filter != null && filter.Key == "eventType")
|
|
||||||
{
|
|
||||||
var filterValue = (EpisodeHistoryEventType)Convert.ToInt32(filter.Value);
|
|
||||||
pagingSpec.FilterExpressions.Add(v => v.EventType == filterValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (episodeId.HasValue)
|
|
||||||
{
|
|
||||||
int i = (int)episodeId;
|
|
||||||
pagingSpec.FilterExpressions.Add(h => h.EpisodeId == i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApplyToPage(_historyService.Paged, pagingSpec, MapToResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<HistoryResource> GetHistorySince()
|
|
||||||
{
|
|
||||||
var queryDate = Request.Query.Date;
|
|
||||||
var queryEventType = Request.Query.EventType;
|
|
||||||
|
|
||||||
if (!queryDate.HasValue)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("date is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTime date = DateTime.Parse(queryDate.Value);
|
|
||||||
EpisodeHistoryEventType? eventType = null;
|
|
||||||
|
|
||||||
if (queryEventType.HasValue)
|
|
||||||
{
|
|
||||||
eventType = (EpisodeHistoryEventType)Convert.ToInt32(queryEventType.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _historyService.Since(date, eventType).Select(MapToResource).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private object MarkAsFailed()
|
|
||||||
{
|
|
||||||
var id = (int)Request.Form.Id;
|
|
||||||
_failedDownloadService.MarkAsFailed(id);
|
|
||||||
return new object();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Api.Episodes;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Core.History;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using NzbDrone.Core.Languages;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.History
|
|
||||||
{
|
|
||||||
public class HistoryResource : RestResource
|
|
||||||
{
|
|
||||||
public int EpisodeId { get; set; }
|
|
||||||
public int SeriesId { get; set; }
|
|
||||||
public string SourceTitle { get; set; }
|
|
||||||
public QualityModel Quality { get; set; }
|
|
||||||
public bool QualityCutoffNotMet { get; set; }
|
|
||||||
public DateTime Date { get; set; }
|
|
||||||
public string DownloadId { get; set; }
|
|
||||||
public Language Language { get; set; }
|
|
||||||
|
|
||||||
public EpisodeHistoryEventType EventType { get; set; }
|
|
||||||
|
|
||||||
public Dictionary<string, string> Data { get; set; }
|
|
||||||
|
|
||||||
public EpisodeResource Episode { get; set; }
|
|
||||||
public SeriesResource Series { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class HistoryResourceMapper
|
|
||||||
{
|
|
||||||
public static HistoryResource ToResource(this EpisodeHistory model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new HistoryResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
EpisodeId = model.EpisodeId,
|
|
||||||
SeriesId = model.SeriesId,
|
|
||||||
SourceTitle = model.SourceTitle,
|
|
||||||
Quality = model.Quality,
|
|
||||||
//QualityCutoffNotMet
|
|
||||||
Date = model.Date,
|
|
||||||
DownloadId = model.DownloadId,
|
|
||||||
|
|
||||||
EventType = model.EventType,
|
|
||||||
|
|
||||||
Data = model.Data
|
|
||||||
//Episode
|
|
||||||
//Series
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
|
||||||
{
|
|
||||||
public class IndexerModule : ProviderModuleBase<IndexerResource, IIndexer, IndexerDefinition>
|
|
||||||
{
|
|
||||||
public IndexerModule(IndexerFactory indexerFactory)
|
|
||||||
: base(indexerFactory, "indexer")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MapToResource(IndexerResource resource, IndexerDefinition definition)
|
|
||||||
{
|
|
||||||
base.MapToResource(resource, definition);
|
|
||||||
|
|
||||||
resource.EnableRss = definition.EnableRss;
|
|
||||||
resource.EnableSearch = definition.EnableAutomaticSearch || definition.EnableInteractiveSearch;
|
|
||||||
resource.SupportsRss = definition.SupportsRss;
|
|
||||||
resource.SupportsSearch = definition.SupportsSearch;
|
|
||||||
resource.Protocol = definition.Protocol;
|
|
||||||
resource.Priority = definition.Priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MapToModel(IndexerDefinition definition, IndexerResource resource)
|
|
||||||
{
|
|
||||||
base.MapToModel(definition, resource);
|
|
||||||
|
|
||||||
definition.EnableRss = resource.EnableRss;
|
|
||||||
definition.EnableAutomaticSearch = resource.EnableSearch;
|
|
||||||
definition.EnableInteractiveSearch = resource.EnableSearch;
|
|
||||||
definition.Priority = resource.Priority;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
|
||||||
{
|
|
||||||
public class IndexerResource : ProviderResource
|
|
||||||
{
|
|
||||||
public bool EnableRss { get; set; }
|
|
||||||
public bool EnableSearch { get; set; }
|
|
||||||
public bool SupportsRss { get; set; }
|
|
||||||
public bool SupportsSearch { get; set; }
|
|
||||||
public DownloadProtocol Protocol { get; set; }
|
|
||||||
public int Priority { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
|
||||||
using Nancy;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.Download;
|
|
||||||
using NzbDrone.Core.Exceptions;
|
|
||||||
using NzbDrone.Core.IndexerSearch;
|
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
using Nancy.ModelBinding;
|
|
||||||
using NzbDrone.Common.Cache;
|
|
||||||
using HttpStatusCode = System.Net.HttpStatusCode;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
|
||||||
{
|
|
||||||
public class ReleaseModule : ReleaseModuleBase
|
|
||||||
{
|
|
||||||
private readonly IFetchAndParseRss _rssFetcherAndParser;
|
|
||||||
private readonly ISearchForReleases _releaseSearchService;
|
|
||||||
private readonly IMakeDownloadDecision _downloadDecisionMaker;
|
|
||||||
private readonly IPrioritizeDownloadDecision _prioritizeDownloadDecision;
|
|
||||||
private readonly IDownloadService _downloadService;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
private readonly ICached<RemoteEpisode> _remoteEpisodeCache;
|
|
||||||
|
|
||||||
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
|
|
||||||
ISearchForReleases releaseSearchService,
|
|
||||||
IMakeDownloadDecision downloadDecisionMaker,
|
|
||||||
IPrioritizeDownloadDecision prioritizeDownloadDecision,
|
|
||||||
IDownloadService downloadService,
|
|
||||||
ICacheManager cacheManager,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_rssFetcherAndParser = rssFetcherAndParser;
|
|
||||||
_releaseSearchService = releaseSearchService;
|
|
||||||
_downloadDecisionMaker = downloadDecisionMaker;
|
|
||||||
_prioritizeDownloadDecision = prioritizeDownloadDecision;
|
|
||||||
_downloadService = downloadService;
|
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
GetResourceAll = GetReleases;
|
|
||||||
Post("/", x => DownloadRelease(this.Bind<ReleaseResource>()));
|
|
||||||
|
|
||||||
PostValidator.RuleFor(s => s.DownloadAllowed).Equal(true);
|
|
||||||
PostValidator.RuleFor(s => s.Guid).NotEmpty();
|
|
||||||
|
|
||||||
_remoteEpisodeCache = cacheManager.GetCache<RemoteEpisode>(GetType(), "remoteEpisodes");
|
|
||||||
}
|
|
||||||
|
|
||||||
private object DownloadRelease(ReleaseResource release)
|
|
||||||
{
|
|
||||||
var remoteEpisode = _remoteEpisodeCache.Find(GetCacheKey(release));
|
|
||||||
|
|
||||||
if (remoteEpisode == null)
|
|
||||||
{
|
|
||||||
_logger.Debug("Couldn't find requested release in cache, cache timeout probably expired.");
|
|
||||||
|
|
||||||
return new NotFoundResponse();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_downloadService.DownloadReport(remoteEpisode);
|
|
||||||
}
|
|
||||||
catch (ReleaseDownloadException ex)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, ex.Message);
|
|
||||||
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
return release;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ReleaseResource> GetReleases()
|
|
||||||
{
|
|
||||||
if (Request.Query.episodeId != null)
|
|
||||||
{
|
|
||||||
return GetEpisodeReleases(Request.Query.episodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetRss();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ReleaseResource> GetEpisodeReleases(int episodeId)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var decisions = _releaseSearchService.EpisodeSearch(episodeId, true, true);
|
|
||||||
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
|
|
||||||
|
|
||||||
return MapDecisions(prioritizedDecisions);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Episode search failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new List<ReleaseResource>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ReleaseResource> GetRss()
|
|
||||||
{
|
|
||||||
var reports = _rssFetcherAndParser.Fetch();
|
|
||||||
var decisions = _downloadDecisionMaker.GetRssDecision(reports);
|
|
||||||
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
|
|
||||||
|
|
||||||
return MapDecisions(prioritizedDecisions);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
|
|
||||||
{
|
|
||||||
var resource = base.MapDecision(decision, initialWeight);
|
|
||||||
_remoteEpisodeCache.Set(GetCacheKey(resource), decision.RemoteEpisode, TimeSpan.FromMinutes(30));
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetCacheKey(ReleaseResource resource)
|
|
||||||
{
|
|
||||||
return string.Concat(resource.IndexerId, "_", resource.Guid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
|
||||||
{
|
|
||||||
public abstract class ReleaseModuleBase : SonarrRestModule<ReleaseResource>
|
|
||||||
{
|
|
||||||
protected virtual List<ReleaseResource> MapDecisions(IEnumerable<DownloadDecision> decisions)
|
|
||||||
{
|
|
||||||
var result = new List<ReleaseResource>();
|
|
||||||
|
|
||||||
foreach (var downloadDecision in decisions)
|
|
||||||
{
|
|
||||||
var release = MapDecision(downloadDecision, result.Count);
|
|
||||||
|
|
||||||
result.Add(release);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
|
|
||||||
{
|
|
||||||
var release = decision.ToResource();
|
|
||||||
|
|
||||||
release.ReleaseWeight = initialWeight;
|
|
||||||
|
|
||||||
if (decision.RemoteEpisode.Series != null)
|
|
||||||
{
|
|
||||||
release.QualityWeight = decision.RemoteEpisode.Series
|
|
||||||
.QualityProfile.Value
|
|
||||||
.Items.FindIndex(v => v.Quality == release.Quality.Quality) * 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
release.QualityWeight += release.Quality.Revision.Real * 10;
|
|
||||||
release.QualityWeight += release.Quality.Revision.Version;
|
|
||||||
|
|
||||||
return release;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using FluentValidation;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.Download;
|
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
|
||||||
{
|
|
||||||
public class ReleasePushModule : ReleaseModuleBase
|
|
||||||
{
|
|
||||||
private readonly IMakeDownloadDecision _downloadDecisionMaker;
|
|
||||||
private readonly IProcessDownloadDecisions _downloadDecisionProcessor;
|
|
||||||
private readonly IIndexerFactory _indexerFactory;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public ReleasePushModule(IMakeDownloadDecision downloadDecisionMaker,
|
|
||||||
IProcessDownloadDecisions downloadDecisionProcessor,
|
|
||||||
IIndexerFactory indexerFactory,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_downloadDecisionMaker = downloadDecisionMaker;
|
|
||||||
_downloadDecisionProcessor = downloadDecisionProcessor;
|
|
||||||
_indexerFactory = indexerFactory;
|
|
||||||
_logger = logger;
|
|
||||||
|
|
||||||
Post("/push", x => ProcessRelease(ReadResourceFromRequest()));
|
|
||||||
|
|
||||||
PostValidator.RuleFor(s => s.Title).NotEmpty();
|
|
||||||
PostValidator.RuleFor(s => s.DownloadUrl).NotEmpty();
|
|
||||||
PostValidator.RuleFor(s => s.Protocol).NotEmpty();
|
|
||||||
PostValidator.RuleFor(s => s.PublishDate).NotEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private object ProcessRelease(ReleaseResource release)
|
|
||||||
{
|
|
||||||
_logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl);
|
|
||||||
|
|
||||||
var info = release.ToModel();
|
|
||||||
|
|
||||||
info.Guid = "PUSH-" + info.DownloadUrl;
|
|
||||||
|
|
||||||
ResolveIndexer(info);
|
|
||||||
|
|
||||||
var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
|
|
||||||
_downloadDecisionProcessor.ProcessDecisions(decisions);
|
|
||||||
|
|
||||||
return MapDecisions(decisions).First();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResolveIndexer(ReleaseInfo release)
|
|
||||||
{
|
|
||||||
if (release.IndexerId == 0 && release.Indexer.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
var indexer = _indexerFactory.All().FirstOrDefault(v => v.Name == release.Indexer);
|
|
||||||
if (indexer != null)
|
|
||||||
{
|
|
||||||
release.IndexerId = indexer.Id;
|
|
||||||
_logger.Debug("Push Release {0} associated with indexer {1} - {2}.", release.Title, release.IndexerId, release.Indexer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Debug("Push Release {0} not associated with known indexer {1}.", release.Title, release.Indexer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (release.IndexerId != 0 && release.Indexer.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var indexer = _indexerFactory.Get(release.IndexerId);
|
|
||||||
release.Indexer = indexer.Name;
|
|
||||||
_logger.Debug("Push Release {0} associated with indexer {1} - {2}.", release.Title, release.IndexerId, release.Indexer);
|
|
||||||
}
|
|
||||||
catch (ModelNotFoundException)
|
|
||||||
{
|
|
||||||
_logger.Debug("Push Release {0} not associated with known indexer {1}.", release.Title, release.IndexerId);
|
|
||||||
release.IndexerId = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Debug("Push Release {0} not associated with an indexer.", release.Title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Parser;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
using NzbDrone.Core.Languages;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Indexers
|
|
||||||
{
|
|
||||||
public class ReleaseResource : RestResource
|
|
||||||
{
|
|
||||||
public string Guid { get; set; }
|
|
||||||
public QualityModel Quality { get; set; }
|
|
||||||
public int QualityWeight { get; set; }
|
|
||||||
public int Age { get; set; }
|
|
||||||
public double AgeHours { get; set; }
|
|
||||||
public double AgeMinutes { get; set; }
|
|
||||||
public long Size { get; set; }
|
|
||||||
public int IndexerId { get; set; }
|
|
||||||
public string Indexer { get; set; }
|
|
||||||
public string ReleaseGroup { get; set; }
|
|
||||||
public string ReleaseHash { get; set; }
|
|
||||||
public string Title { get; set; }
|
|
||||||
public bool FullSeason { get; set; }
|
|
||||||
public int SeasonNumber { get; set; }
|
|
||||||
public Language Language { get; set; }
|
|
||||||
public string AirDate { get; set; }
|
|
||||||
public string SeriesTitle { get; set; }
|
|
||||||
public int[] EpisodeNumbers { get; set; }
|
|
||||||
public int[] AbsoluteEpisodeNumbers { get; set; }
|
|
||||||
public bool Approved { get; set; }
|
|
||||||
public bool TemporarilyRejected { get; set; }
|
|
||||||
public bool Rejected { get; set; }
|
|
||||||
public int TvdbId { get; set; }
|
|
||||||
public int TvRageId { get; set; }
|
|
||||||
public IEnumerable<string> Rejections { get; set; }
|
|
||||||
public DateTime PublishDate { get; set; }
|
|
||||||
public string CommentUrl { get; set; }
|
|
||||||
public string DownloadUrl { get; set; }
|
|
||||||
public string InfoUrl { get; set; }
|
|
||||||
public bool DownloadAllowed { get; set; }
|
|
||||||
public int ReleaseWeight { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public string MagnetUrl { get; set; }
|
|
||||||
public string InfoHash { get; set; }
|
|
||||||
public int? Seeders { get; set; }
|
|
||||||
public int? Leechers { get; set; }
|
|
||||||
public DownloadProtocol Protocol { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
// Used to support the original Release Push implementation, removed in v3
|
|
||||||
// JsonIgnore so we don't serialize it, but can still parse it
|
|
||||||
[JsonIgnore]
|
|
||||||
public DownloadProtocol DownloadProtocol
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Protocol;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value > 0 && Protocol == 0)
|
|
||||||
{
|
|
||||||
Protocol = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsDaily { get; set; }
|
|
||||||
public bool IsAbsoluteNumbering { get; set; }
|
|
||||||
public bool IsPossibleSpecialEpisode { get; set; }
|
|
||||||
public bool Special { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ReleaseResourceMapper
|
|
||||||
{
|
|
||||||
public static ReleaseResource ToResource(this DownloadDecision model)
|
|
||||||
{
|
|
||||||
var releaseInfo = model.RemoteEpisode.Release;
|
|
||||||
var parsedEpisodeInfo = model.RemoteEpisode.ParsedEpisodeInfo;
|
|
||||||
var remoteEpisode = model.RemoteEpisode;
|
|
||||||
var torrentInfo = (model.RemoteEpisode.Release as TorrentInfo) ?? new TorrentInfo();
|
|
||||||
|
|
||||||
// TODO: Clean this mess up. don't mix data from multiple classes, use sub-resources instead? (Got a huge Deja Vu, didn't we talk about this already once?)
|
|
||||||
return new ReleaseResource
|
|
||||||
{
|
|
||||||
Guid = releaseInfo.Guid,
|
|
||||||
Quality = parsedEpisodeInfo?.Quality,
|
|
||||||
//QualityWeight
|
|
||||||
Age = releaseInfo.Age,
|
|
||||||
AgeHours = releaseInfo.AgeHours,
|
|
||||||
AgeMinutes = releaseInfo.AgeMinutes,
|
|
||||||
Size = releaseInfo.Size,
|
|
||||||
IndexerId = releaseInfo.IndexerId,
|
|
||||||
Indexer = releaseInfo.Indexer,
|
|
||||||
ReleaseGroup = parsedEpisodeInfo?.ReleaseGroup,
|
|
||||||
ReleaseHash = parsedEpisodeInfo?.ReleaseHash,
|
|
||||||
Title = releaseInfo.Title,
|
|
||||||
FullSeason = parsedEpisodeInfo?.FullSeason ?? false,
|
|
||||||
SeasonNumber = parsedEpisodeInfo?.SeasonNumber ?? 0,
|
|
||||||
Language = parsedEpisodeInfo?.Language,
|
|
||||||
AirDate = parsedEpisodeInfo?.AirDate,
|
|
||||||
SeriesTitle = parsedEpisodeInfo?.SeriesTitle,
|
|
||||||
EpisodeNumbers = parsedEpisodeInfo?.EpisodeNumbers,
|
|
||||||
AbsoluteEpisodeNumbers = parsedEpisodeInfo?.AbsoluteEpisodeNumbers,
|
|
||||||
Approved = model.Approved,
|
|
||||||
TemporarilyRejected = model.TemporarilyRejected,
|
|
||||||
Rejected = model.Rejected,
|
|
||||||
TvdbId = releaseInfo.TvdbId,
|
|
||||||
TvRageId = releaseInfo.TvRageId,
|
|
||||||
Rejections = model.Rejections.Select(r => r.Reason).ToList(),
|
|
||||||
PublishDate = releaseInfo.PublishDate,
|
|
||||||
CommentUrl = releaseInfo.CommentUrl,
|
|
||||||
DownloadUrl = releaseInfo.DownloadUrl,
|
|
||||||
InfoUrl = releaseInfo.InfoUrl,
|
|
||||||
DownloadAllowed = remoteEpisode.DownloadAllowed,
|
|
||||||
//ReleaseWeight
|
|
||||||
|
|
||||||
MagnetUrl = torrentInfo.MagnetUrl,
|
|
||||||
InfoHash = torrentInfo.InfoHash,
|
|
||||||
Seeders = torrentInfo.Seeders,
|
|
||||||
Leechers = (torrentInfo.Peers.HasValue && torrentInfo.Seeders.HasValue) ? (torrentInfo.Peers.Value - torrentInfo.Seeders.Value) : (int?)null,
|
|
||||||
Protocol = releaseInfo.DownloadProtocol,
|
|
||||||
|
|
||||||
IsDaily = parsedEpisodeInfo?.IsDaily ?? false,
|
|
||||||
IsAbsoluteNumbering = parsedEpisodeInfo?.IsAbsoluteNumbering ?? false,
|
|
||||||
IsPossibleSpecialEpisode = parsedEpisodeInfo?.IsPossibleSpecialEpisode ?? false,
|
|
||||||
Special = parsedEpisodeInfo?.Special ?? false,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ReleaseInfo ToModel(this ReleaseResource resource)
|
|
||||||
{
|
|
||||||
ReleaseInfo model;
|
|
||||||
|
|
||||||
if (resource.Protocol == DownloadProtocol.Torrent)
|
|
||||||
{
|
|
||||||
model = new TorrentInfo
|
|
||||||
{
|
|
||||||
MagnetUrl = resource.MagnetUrl,
|
|
||||||
InfoHash = resource.InfoHash,
|
|
||||||
Seeders = resource.Seeders,
|
|
||||||
Peers = (resource.Seeders.HasValue && resource.Leechers.HasValue) ? (resource.Seeders + resource.Leechers) : null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
model = new ReleaseInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
model.Guid = resource.Guid;
|
|
||||||
model.Title = resource.Title;
|
|
||||||
model.Size = resource.Size;
|
|
||||||
model.DownloadUrl = resource.DownloadUrl;
|
|
||||||
model.InfoUrl = resource.InfoUrl;
|
|
||||||
model.CommentUrl = resource.CommentUrl;
|
|
||||||
model.IndexerId = resource.IndexerId;
|
|
||||||
model.Indexer = resource.Indexer;
|
|
||||||
model.DownloadProtocol = resource.DownloadProtocol;
|
|
||||||
model.TvdbId = resource.TvdbId;
|
|
||||||
model.TvRageId = resource.TvRageId;
|
|
||||||
model.PublishDate = resource.PublishDate.ToUniversalTime();
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Logs
|
|
||||||
{
|
|
||||||
public class LogFileModule : LogFileModuleBase
|
|
||||||
{
|
|
||||||
private readonly IAppFolderInfo _appFolderInfo;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
|
|
||||||
public LogFileModule(IAppFolderInfo appFolderInfo,
|
|
||||||
IDiskProvider diskProvider,
|
|
||||||
IConfigFileProvider configFileProvider)
|
|
||||||
: base(diskProvider, configFileProvider, "")
|
|
||||||
{
|
|
||||||
_appFolderInfo = appFolderInfo;
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IEnumerable<string> GetLogFiles()
|
|
||||||
{
|
|
||||||
return _diskProvider.GetFiles(_appFolderInfo.GetLogFolder(), SearchOption.TopDirectoryOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string GetLogFilePath(string filename)
|
|
||||||
{
|
|
||||||
return Path.Combine(_appFolderInfo.GetLogFolder(), filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string DownloadUrlRoot => "logfile";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Nancy;
|
|
||||||
using Nancy.Responses;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Logs
|
|
||||||
{
|
|
||||||
public abstract class LogFileModuleBase : SonarrRestModule<LogFileResource>
|
|
||||||
{
|
|
||||||
protected const string LOGFILE_ROUTE = @"/(?<filename>[-.a-zA-Z0-9]+?\.txt)";
|
|
||||||
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
private readonly IConfigFileProvider _configFileProvider;
|
|
||||||
|
|
||||||
public LogFileModuleBase(IDiskProvider diskProvider,
|
|
||||||
IConfigFileProvider configFileProvider,
|
|
||||||
string route)
|
|
||||||
: base("log/file" + route)
|
|
||||||
{
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
_configFileProvider = configFileProvider;
|
|
||||||
GetResourceAll = GetLogFilesResponse;
|
|
||||||
|
|
||||||
Get(LOGFILE_ROUTE, options => GetLogFileResponse(options.filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<LogFileResource> GetLogFilesResponse()
|
|
||||||
{
|
|
||||||
var result = new List<LogFileResource>();
|
|
||||||
|
|
||||||
var files = GetLogFiles().ToList();
|
|
||||||
|
|
||||||
for (int i = 0; i < files.Count; i++)
|
|
||||||
{
|
|
||||||
var file = files[i];
|
|
||||||
var filename = Path.GetFileName(file);
|
|
||||||
|
|
||||||
result.Add(new LogFileResource
|
|
||||||
{
|
|
||||||
Id = i + 1,
|
|
||||||
Filename = filename,
|
|
||||||
LastWriteTime = _diskProvider.FileGetLastWrite(file),
|
|
||||||
ContentsUrl = string.Format("{0}/api/{1}/{2}", _configFileProvider.UrlBase, Resource, filename),
|
|
||||||
DownloadUrl = string.Format("{0}/{1}/{2}", _configFileProvider.UrlBase, DownloadUrlRoot, filename)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.OrderByDescending(l => l.LastWriteTime).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetLogFileResponse(string filename)
|
|
||||||
{
|
|
||||||
LogManager.Flush();
|
|
||||||
|
|
||||||
var filePath = GetLogFilePath(filename);
|
|
||||||
|
|
||||||
if (!_diskProvider.FileExists(filePath))
|
|
||||||
return new NotFoundResponse();
|
|
||||||
|
|
||||||
var data = _diskProvider.ReadAllText(filePath);
|
|
||||||
|
|
||||||
return new TextResponse(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract IEnumerable<string> GetLogFiles();
|
|
||||||
protected abstract string GetLogFilePath(string filename);
|
|
||||||
|
|
||||||
protected abstract string DownloadUrlRoot { get; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Logs
|
|
||||||
{
|
|
||||||
public class LogFileResource : RestResource
|
|
||||||
{
|
|
||||||
public string Filename { get; set; }
|
|
||||||
public DateTime LastWriteTime { get; set; }
|
|
||||||
public string ContentsUrl { get; set; }
|
|
||||||
public string DownloadUrl { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.Instrumentation;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Logs
|
|
||||||
{
|
|
||||||
public class LogModule : SonarrRestModule<LogResource>
|
|
||||||
{
|
|
||||||
private readonly ILogService _logService;
|
|
||||||
|
|
||||||
public LogModule(ILogService logService)
|
|
||||||
{
|
|
||||||
_logService = logService;
|
|
||||||
GetResourcePaged = GetLogs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PagingResource<LogResource> GetLogs(PagingResource<LogResource> pagingResource)
|
|
||||||
{
|
|
||||||
var pageSpec = pagingResource.MapToPagingSpec<LogResource, Log>();
|
|
||||||
|
|
||||||
if (pageSpec.SortKey == "time")
|
|
||||||
{
|
|
||||||
pageSpec.SortKey = "id";
|
|
||||||
}
|
|
||||||
|
|
||||||
var filter = pagingResource.Filters.FirstOrDefault();
|
|
||||||
|
|
||||||
if (filter != null && filter.Key == "level")
|
|
||||||
{
|
|
||||||
switch (filter.Value)
|
|
||||||
{
|
|
||||||
case "Fatal":
|
|
||||||
pageSpec.FilterExpressions.Add(h => h.Level == "Fatal");
|
|
||||||
break;
|
|
||||||
case "Error":
|
|
||||||
pageSpec.FilterExpressions.Add(h => h.Level == "Fatal" || h.Level == "Error");
|
|
||||||
break;
|
|
||||||
case "Warn":
|
|
||||||
pageSpec.FilterExpressions.Add(h => h.Level == "Fatal" || h.Level == "Error" || h.Level == "Warn");
|
|
||||||
break;
|
|
||||||
case "Info":
|
|
||||||
pageSpec.FilterExpressions.Add(h => h.Level == "Fatal" || h.Level == "Error" || h.Level == "Warn" || h.Level == "Info");
|
|
||||||
break;
|
|
||||||
case "Debug":
|
|
||||||
pageSpec.FilterExpressions.Add(h => h.Level == "Fatal" || h.Level == "Error" || h.Level == "Warn" || h.Level == "Info" || h.Level == "Debug");
|
|
||||||
break;
|
|
||||||
case "Trace":
|
|
||||||
pageSpec.FilterExpressions.Add(h => h.Level == "Fatal" || h.Level == "Error" || h.Level == "Warn" || h.Level == "Info" || h.Level == "Debug" || h.Level == "Trace");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ApplyToPage(_logService.Paged, pageSpec, LogResourceMapper.ToResource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
using System;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Logs
|
|
||||||
{
|
|
||||||
public class LogResource : RestResource
|
|
||||||
{
|
|
||||||
public DateTime Time { get; set; }
|
|
||||||
public string Exception { get; set; }
|
|
||||||
public string ExceptionType { get; set; }
|
|
||||||
public string Level { get; set; }
|
|
||||||
public string Logger { get; set; }
|
|
||||||
public string Message { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LogResourceMapper
|
|
||||||
{
|
|
||||||
public static LogResource ToResource(this Core.Instrumentation.Log model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new LogResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Time = model.Time,
|
|
||||||
Exception = model.Exception,
|
|
||||||
ExceptionType = model.ExceptionType,
|
|
||||||
Level = model.Level,
|
|
||||||
Logger = model.Logger,
|
|
||||||
Message = model.Message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Logs
|
|
||||||
{
|
|
||||||
public class UpdateLogFileModule : LogFileModuleBase
|
|
||||||
{
|
|
||||||
private readonly IAppFolderInfo _appFolderInfo;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
|
|
||||||
public UpdateLogFileModule(IAppFolderInfo appFolderInfo,
|
|
||||||
IDiskProvider diskProvider,
|
|
||||||
IConfigFileProvider configFileProvider)
|
|
||||||
: base(diskProvider, configFileProvider, "/update")
|
|
||||||
{
|
|
||||||
_appFolderInfo = appFolderInfo;
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IEnumerable<string> GetLogFiles()
|
|
||||||
{
|
|
||||||
if (!_diskProvider.FolderExists(_appFolderInfo.GetUpdateLogFolder())) return Enumerable.Empty<string>();
|
|
||||||
|
|
||||||
return _diskProvider.GetFiles(_appFolderInfo.GetUpdateLogFolder(), SearchOption.TopDirectoryOnly)
|
|
||||||
.Where(f => Regex.IsMatch(Path.GetFileName(f), LOGFILE_ROUTE.TrimStart('/'), RegexOptions.IgnoreCase))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string GetLogFilePath(string filename)
|
|
||||||
{
|
|
||||||
return Path.Combine(_appFolderInfo.GetUpdateLogFolder(), filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string DownloadUrlRoot => "updatelogfile";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.MediaFiles.EpisodeImport.Manual;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Extensions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.ManualImport
|
|
||||||
{
|
|
||||||
public class ManualImportModule : SonarrRestModule<ManualImportResource>
|
|
||||||
{
|
|
||||||
private readonly IManualImportService _manualImportService;
|
|
||||||
|
|
||||||
public ManualImportModule(IManualImportService manualImportService)
|
|
||||||
: base("/manualimport")
|
|
||||||
{
|
|
||||||
_manualImportService = manualImportService;
|
|
||||||
|
|
||||||
GetResourceAll = GetMediaFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ManualImportResource> GetMediaFiles()
|
|
||||||
{
|
|
||||||
var folderQuery = Request.Query.folder;
|
|
||||||
var folder = (string)folderQuery.Value;
|
|
||||||
|
|
||||||
var downloadIdQuery = Request.Query.downloadId;
|
|
||||||
var downloadId = (string)downloadIdQuery.Value;
|
|
||||||
var filterExistingFiles = Request.GetBooleanQueryParameter("filterExistingFiles", true);
|
|
||||||
|
|
||||||
return _manualImportService.GetMediaFiles(folder, downloadId, null, filterExistingFiles)
|
|
||||||
.ToResource()
|
|
||||||
.Select(AddQualityWeight).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ManualImportResource AddQualityWeight(ManualImportResource item)
|
|
||||||
{
|
|
||||||
if (item.Quality != null)
|
|
||||||
{
|
|
||||||
item.QualityWeight = Quality.DefaultQualityDefinitions.Single(q => q.Quality == item.Quality.Quality).Weight;
|
|
||||||
item.QualityWeight += item.Quality.Revision.Real * 10;
|
|
||||||
item.QualityWeight += item.Quality.Revision.Version;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Api.Episodes;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Common.Crypto;
|
|
||||||
using NzbDrone.Core.DecisionEngine;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.ManualImport
|
|
||||||
{
|
|
||||||
public class ManualImportResource : RestResource
|
|
||||||
{
|
|
||||||
public string Path { get; set; }
|
|
||||||
public string RelativePath { get; set; }
|
|
||||||
public string FolderName { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public long Size { get; set; }
|
|
||||||
public SeriesResource Series { get; set; }
|
|
||||||
public int? SeasonNumber { get; set; }
|
|
||||||
public List<EpisodeResource> Episodes { get; set; }
|
|
||||||
public QualityModel Quality { get; set; }
|
|
||||||
public int QualityWeight { get; set; }
|
|
||||||
public string DownloadId { get; set; }
|
|
||||||
public IEnumerable<Rejection> Rejections { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ManualImportResourceMapper
|
|
||||||
{
|
|
||||||
public static ManualImportResource ToResource(this Core.MediaFiles.EpisodeImport.Manual.ManualImportItem model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new ManualImportResource
|
|
||||||
{
|
|
||||||
Id = HashConverter.GetHashInt31(model.Path),
|
|
||||||
|
|
||||||
Path = model.Path,
|
|
||||||
RelativePath = model.RelativePath,
|
|
||||||
FolderName = model.FolderName,
|
|
||||||
Name = model.Name,
|
|
||||||
Size = model.Size,
|
|
||||||
Series = model.Series.ToResource(),
|
|
||||||
SeasonNumber = model.SeasonNumber,
|
|
||||||
Episodes = model.Episodes.ToResource(),
|
|
||||||
Quality = model.Quality,
|
|
||||||
//QualityWeight
|
|
||||||
DownloadId = model.DownloadId,
|
|
||||||
Rejections = model.Rejections
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<ManualImportResource> ToResource(this IEnumerable<Core.MediaFiles.EpisodeImport.Manual.ManualImportItem> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
using System.IO;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Nancy;
|
|
||||||
using Nancy.Responses;
|
|
||||||
using NzbDrone.Common.Disk;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.MediaCovers
|
|
||||||
{
|
|
||||||
public class MediaCoverModule : NzbDroneApiModule
|
|
||||||
{
|
|
||||||
private static readonly Regex RegexResizedImage = new Regex(@"-\d+\.jpg$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
||||||
|
|
||||||
private const string MEDIA_COVER_ROUTE = @"/(?<seriesId>\d+)/(?<filename>(.+)\.(jpg|png|gif))";
|
|
||||||
|
|
||||||
private readonly IAppFolderInfo _appFolderInfo;
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
|
||||||
|
|
||||||
public MediaCoverModule(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider) : base("MediaCover")
|
|
||||||
{
|
|
||||||
_appFolderInfo = appFolderInfo;
|
|
||||||
_diskProvider = diskProvider;
|
|
||||||
|
|
||||||
Get(MEDIA_COVER_ROUTE, options => GetMediaCover(options.seriesId, options.filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetMediaCover(int seriesId, string filename)
|
|
||||||
{
|
|
||||||
var filePath = Path.Combine(_appFolderInfo.GetAppDataPath(), "MediaCover", seriesId.ToString(), filename);
|
|
||||||
|
|
||||||
if (!_diskProvider.FileExists(filePath) || _diskProvider.GetFileSize(filePath) == 0)
|
|
||||||
{
|
|
||||||
// Return the full sized image if someone requests a non-existing resized one.
|
|
||||||
// TODO: This code can be removed later once everyone had the update for a while.
|
|
||||||
var basefilePath = RegexResizedImage.Replace(filePath, ".jpg");
|
|
||||||
if (basefilePath == filePath || !_diskProvider.FileExists(basefilePath))
|
|
||||||
{
|
|
||||||
return new NotFoundResponse();
|
|
||||||
}
|
|
||||||
filePath = basefilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new StreamResponse(() => File.OpenRead(filePath), MimeTypes.GetMimeType(filePath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using NzbDrone.Core.Extras.Metadata;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Metadata
|
|
||||||
{
|
|
||||||
public class MetadataModule : ProviderModuleBase<MetadataResource, IMetadata, MetadataDefinition>
|
|
||||||
{
|
|
||||||
public MetadataModule(IMetadataFactory metadataFactory)
|
|
||||||
: base(metadataFactory, "metadata")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MapToResource(MetadataResource resource, MetadataDefinition definition)
|
|
||||||
{
|
|
||||||
base.MapToResource(resource, definition);
|
|
||||||
|
|
||||||
resource.Enable = definition.Enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MapToModel(MetadataDefinition definition, MetadataResource resource)
|
|
||||||
{
|
|
||||||
base.MapToModel(definition, resource);
|
|
||||||
|
|
||||||
definition.Enable = resource.Enable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
namespace NzbDrone.Api.Metadata
|
|
||||||
{
|
|
||||||
public class MetadataResource : ProviderResource
|
|
||||||
{
|
|
||||||
public bool Enable { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
using NzbDrone.Core.Notifications;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Notifications
|
|
||||||
{
|
|
||||||
public class NotificationModule : ProviderModuleBase<NotificationResource, INotification, NotificationDefinition>
|
|
||||||
{
|
|
||||||
public NotificationModule(NotificationFactory notificationFactory)
|
|
||||||
: base(notificationFactory, "notification")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MapToResource(NotificationResource resource, NotificationDefinition definition)
|
|
||||||
{
|
|
||||||
base.MapToResource(resource, definition);
|
|
||||||
|
|
||||||
resource.OnGrab = definition.OnGrab;
|
|
||||||
resource.OnDownload = definition.OnDownload;
|
|
||||||
resource.OnUpgrade = definition.OnUpgrade;
|
|
||||||
resource.OnRename = definition.OnRename;
|
|
||||||
resource.SupportsOnGrab = definition.SupportsOnGrab;
|
|
||||||
resource.SupportsOnDownload = definition.SupportsOnDownload;
|
|
||||||
resource.SupportsOnUpgrade = definition.SupportsOnUpgrade;
|
|
||||||
resource.SupportsOnRename = definition.SupportsOnRename;
|
|
||||||
resource.Tags = definition.Tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MapToModel(NotificationDefinition definition, NotificationResource resource)
|
|
||||||
{
|
|
||||||
base.MapToModel(definition, resource);
|
|
||||||
|
|
||||||
definition.OnGrab = resource.OnGrab;
|
|
||||||
definition.OnDownload = resource.OnDownload;
|
|
||||||
definition.OnUpgrade = resource.OnUpgrade;
|
|
||||||
definition.OnRename = resource.OnRename;
|
|
||||||
definition.SupportsOnGrab = resource.SupportsOnGrab;
|
|
||||||
definition.SupportsOnDownload = resource.SupportsOnDownload;
|
|
||||||
definition.SupportsOnUpgrade = resource.SupportsOnUpgrade;
|
|
||||||
definition.SupportsOnRename = resource.SupportsOnRename;
|
|
||||||
definition.Tags = resource.Tags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Notifications
|
|
||||||
{
|
|
||||||
public class NotificationResource : ProviderResource
|
|
||||||
{
|
|
||||||
public bool OnGrab { get; set; }
|
|
||||||
public bool OnDownload { get; set; }
|
|
||||||
public bool OnUpgrade { get; set; }
|
|
||||||
public bool OnRename { get; set; }
|
|
||||||
public bool SupportsOnGrab { get; set; }
|
|
||||||
public bool SupportsOnDownload { get; set; }
|
|
||||||
public bool SupportsOnUpgrade { get; set; }
|
|
||||||
public bool SupportsOnRename { get; set; }
|
|
||||||
public HashSet<int> Tags { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api
|
|
||||||
{
|
|
||||||
public abstract class NzbDroneApiModule : SonarrModule
|
|
||||||
{
|
|
||||||
protected NzbDroneApiModule(string resource)
|
|
||||||
: base("/api/" + resource.Trim('/'))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api
|
|
||||||
{
|
|
||||||
public abstract class NzbDroneFeedModule : SonarrModule
|
|
||||||
{
|
|
||||||
protected NzbDroneFeedModule(string resource)
|
|
||||||
: base("/feed/" + resource.Trim('/'))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
using NzbDrone.Api.Episodes;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Parser;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Parse
|
|
||||||
{
|
|
||||||
public class ParseModule : SonarrRestModule<ParseResource>
|
|
||||||
{
|
|
||||||
private readonly IParsingService _parsingService;
|
|
||||||
|
|
||||||
public ParseModule(IParsingService parsingService)
|
|
||||||
{
|
|
||||||
_parsingService = parsingService;
|
|
||||||
|
|
||||||
GetResourceSingle = Parse;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ParseResource Parse()
|
|
||||||
{
|
|
||||||
var title = Request.Query.Title.Value as string;
|
|
||||||
var path = Request.Query.Path.Value as string;
|
|
||||||
|
|
||||||
if (path.IsNullOrWhiteSpace() && title.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
throw new BadRequestException("title or path is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
var parsedEpisodeInfo = path.IsNotNullOrWhiteSpace() ? Parser.ParsePath(path) : Parser.ParseTitle(title);
|
|
||||||
|
|
||||||
if (parsedEpisodeInfo == null)
|
|
||||||
{
|
|
||||||
return new ParseResource
|
|
||||||
{
|
|
||||||
Title = title
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0);
|
|
||||||
|
|
||||||
if (remoteEpisode != null)
|
|
||||||
{
|
|
||||||
return new ParseResource
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
ParsedEpisodeInfo = remoteEpisode.ParsedEpisodeInfo,
|
|
||||||
Series = remoteEpisode.Series.ToResource(),
|
|
||||||
Episodes = remoteEpisode.Episodes.ToResource()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new ParseResource
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
ParsedEpisodeInfo = parsedEpisodeInfo
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Api.Episodes;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Core.Parser.Model;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Parse
|
|
||||||
{
|
|
||||||
public class ParseResource : RestResource
|
|
||||||
{
|
|
||||||
public string Title { get; set; }
|
|
||||||
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; }
|
|
||||||
public SeriesResource Series { get; set; }
|
|
||||||
public List<EpisodeResource> Episodes { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
|
||||||
using FluentValidation.Results;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Profiles.Delay;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Validation;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles.Delay
|
|
||||||
{
|
|
||||||
public class DelayProfileModule : SonarrRestModule<DelayProfileResource>
|
|
||||||
{
|
|
||||||
private readonly IDelayProfileService _delayProfileService;
|
|
||||||
|
|
||||||
public DelayProfileModule(IDelayProfileService delayProfileService, DelayProfileTagInUseValidator tagInUseValidator)
|
|
||||||
{
|
|
||||||
_delayProfileService = delayProfileService;
|
|
||||||
|
|
||||||
GetResourceAll = GetAll;
|
|
||||||
GetResourceById = GetById;
|
|
||||||
UpdateResource = Update;
|
|
||||||
CreateResource = Create;
|
|
||||||
DeleteResource = DeleteProfile;
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(d => d.Tags).NotEmpty().When(d => d.Id != 1);
|
|
||||||
SharedValidator.RuleFor(d => d.Tags).EmptyCollection<DelayProfileResource, int>().When(d => d.Id == 1);
|
|
||||||
SharedValidator.RuleFor(d => d.Tags).SetValidator(tagInUseValidator);
|
|
||||||
SharedValidator.RuleFor(d => d.UsenetDelay).GreaterThanOrEqualTo(0);
|
|
||||||
SharedValidator.RuleFor(d => d.TorrentDelay).GreaterThanOrEqualTo(0);
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(d => d).Custom((delayProfile, context) =>
|
|
||||||
{
|
|
||||||
if (!delayProfile.EnableUsenet && !delayProfile.EnableTorrent)
|
|
||||||
{
|
|
||||||
context.AddFailure("Either Usenet or Torrent should be enabled");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private int Create(DelayProfileResource resource)
|
|
||||||
{
|
|
||||||
var model = resource.ToModel();
|
|
||||||
model = _delayProfileService.Add(model);
|
|
||||||
|
|
||||||
return model.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteProfile(int id)
|
|
||||||
{
|
|
||||||
if (id == 1)
|
|
||||||
{
|
|
||||||
throw new MethodNotAllowedException("Cannot delete global delay profile");
|
|
||||||
}
|
|
||||||
|
|
||||||
_delayProfileService.Delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update(DelayProfileResource resource)
|
|
||||||
{
|
|
||||||
var model = resource.ToModel();
|
|
||||||
_delayProfileService.Update(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DelayProfileResource GetById(int id)
|
|
||||||
{
|
|
||||||
return _delayProfileService.Get(id).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<DelayProfileResource> GetAll()
|
|
||||||
{
|
|
||||||
return _delayProfileService.All().ToResource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
using NzbDrone.Core.Profiles.Delay;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles.Delay
|
|
||||||
{
|
|
||||||
public class DelayProfileResource : RestResource
|
|
||||||
{
|
|
||||||
public bool EnableUsenet { get; set; }
|
|
||||||
public bool EnableTorrent { get; set; }
|
|
||||||
public DownloadProtocol PreferredProtocol { get; set; }
|
|
||||||
public int UsenetDelay { get; set; }
|
|
||||||
public int TorrentDelay { get; set; }
|
|
||||||
public bool BypassIfHighestQuality { get; set; }
|
|
||||||
public int Order { get; set; }
|
|
||||||
public HashSet<int> Tags { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DelayProfileResourceMapper
|
|
||||||
{
|
|
||||||
public static DelayProfileResource ToResource(this DelayProfile model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new DelayProfileResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
EnableUsenet = model.EnableUsenet,
|
|
||||||
EnableTorrent = model.EnableTorrent,
|
|
||||||
PreferredProtocol = model.PreferredProtocol,
|
|
||||||
UsenetDelay = model.UsenetDelay,
|
|
||||||
TorrentDelay = model.TorrentDelay,
|
|
||||||
BypassIfHighestQuality = model.BypassIfHighestQuality,
|
|
||||||
Order = model.Order,
|
|
||||||
Tags = new HashSet<int>(model.Tags)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DelayProfile ToModel(this DelayProfileResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new DelayProfile
|
|
||||||
{
|
|
||||||
Id = resource.Id,
|
|
||||||
|
|
||||||
EnableUsenet = resource.EnableUsenet,
|
|
||||||
EnableTorrent = resource.EnableTorrent,
|
|
||||||
PreferredProtocol = resource.PreferredProtocol,
|
|
||||||
UsenetDelay = resource.UsenetDelay,
|
|
||||||
TorrentDelay = resource.TorrentDelay,
|
|
||||||
BypassIfHighestQuality = resource.BypassIfHighestQuality,
|
|
||||||
Order = resource.Order,
|
|
||||||
Tags = new HashSet<int>(resource.Tags)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<DelayProfileResource> ToResource(this IEnumerable<DelayProfile> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.Languages;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles.Languages
|
|
||||||
{
|
|
||||||
public class LanguageModule : SonarrRestModule<LanguageResource>
|
|
||||||
{
|
|
||||||
public LanguageModule()
|
|
||||||
{
|
|
||||||
GetResourceAll = GetAll;
|
|
||||||
GetResourceById = GetById;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LanguageResource GetById(int id)
|
|
||||||
{
|
|
||||||
var language = (Language)id;
|
|
||||||
|
|
||||||
return new LanguageResource
|
|
||||||
{
|
|
||||||
Id = (int)language,
|
|
||||||
Name = language.ToString()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<LanguageResource> GetAll()
|
|
||||||
{
|
|
||||||
return ((Language[])Enum.GetValues(typeof (Language)))
|
|
||||||
.Select(l => new LanguageResource
|
|
||||||
{
|
|
||||||
Id = (int) l,
|
|
||||||
Name = l.ToString()
|
|
||||||
})
|
|
||||||
.OrderBy(l => l.Name)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles.Languages
|
|
||||||
{
|
|
||||||
public class LanguageResource : RestResource
|
|
||||||
{
|
|
||||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
|
|
||||||
public new int Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string NameLower => Name.ToLowerInvariant();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
using System.Text;
|
|
||||||
using Nancy;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles
|
|
||||||
{
|
|
||||||
public class LegacyProfileModule : NzbDroneApiModule
|
|
||||||
{
|
|
||||||
public LegacyProfileModule()
|
|
||||||
: base("qualityprofile")
|
|
||||||
{
|
|
||||||
Get("/", x =>
|
|
||||||
{
|
|
||||||
string queryString = ConvertQueryParams(Request.Query);
|
|
||||||
var url = string.Format("/api/profile?{0}", queryString);
|
|
||||||
|
|
||||||
return Response.AsRedirect(url);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private string ConvertQueryParams(DynamicDictionary query)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
foreach (var key in query)
|
|
||||||
{
|
|
||||||
var value = query[key];
|
|
||||||
|
|
||||||
sb.AppendFormat("&{0}={1}", key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.ToString().Trim('&');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.Profiles.Qualities;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles
|
|
||||||
{
|
|
||||||
public class ProfileModule : SonarrRestModule<ProfileResource>
|
|
||||||
{
|
|
||||||
private readonly IQualityProfileService _qualityProfileService;
|
|
||||||
|
|
||||||
public ProfileModule(IQualityProfileService qualityProfileService)
|
|
||||||
{
|
|
||||||
_qualityProfileService = qualityProfileService;
|
|
||||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
|
||||||
SharedValidator.RuleFor(c => c.Cutoff).NotNull();
|
|
||||||
SharedValidator.RuleFor(c => c.Items).MustHaveAllowedQuality();
|
|
||||||
|
|
||||||
GetResourceAll = GetAll;
|
|
||||||
GetResourceById = GetById;
|
|
||||||
UpdateResource = Update;
|
|
||||||
CreateResource = Create;
|
|
||||||
DeleteResource = DeleteProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int Create(ProfileResource resource)
|
|
||||||
{
|
|
||||||
var model = resource.ToModel();
|
|
||||||
|
|
||||||
return _qualityProfileService.Add(model).Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteProfile(int id)
|
|
||||||
{
|
|
||||||
_qualityProfileService.Delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update(ProfileResource resource)
|
|
||||||
{
|
|
||||||
var model = resource.ToModel();
|
|
||||||
|
|
||||||
_qualityProfileService.Update(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProfileResource GetById(int id)
|
|
||||||
{
|
|
||||||
return _qualityProfileService.Get(id).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ProfileResource> GetAll()
|
|
||||||
{
|
|
||||||
return _qualityProfileService.All().ToResource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Profiles.Qualities;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles
|
|
||||||
{
|
|
||||||
public class ProfileResource : RestResource
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public bool UpgradeAllowed { get; set; }
|
|
||||||
public Quality Cutoff { get; set; }
|
|
||||||
public List<ProfileQualityItemResource> Items { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ProfileQualityItemResource : RestResource
|
|
||||||
{
|
|
||||||
public Quality Quality { get; set; }
|
|
||||||
public bool Allowed { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ProfileResourceMapper
|
|
||||||
{
|
|
||||||
public static ProfileResource ToResource(this QualityProfile model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
var cutoffItem = model.Items.First(q =>
|
|
||||||
{
|
|
||||||
if (q.Id == model.Cutoff) return true;
|
|
||||||
|
|
||||||
if (q.Quality == null) return false;
|
|
||||||
|
|
||||||
return q.Quality.Id == model.Cutoff;
|
|
||||||
});
|
|
||||||
|
|
||||||
var cutoff = cutoffItem.Items == null || cutoffItem.Items.Empty()
|
|
||||||
? cutoffItem.Quality
|
|
||||||
: cutoffItem.Items.First().Quality;
|
|
||||||
|
|
||||||
return new ProfileResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Name = model.Name,
|
|
||||||
UpgradeAllowed = model.UpgradeAllowed,
|
|
||||||
Cutoff = cutoff,
|
|
||||||
|
|
||||||
// Flatten groups so things don't explode
|
|
||||||
Items = model.Items.SelectMany(i =>
|
|
||||||
{
|
|
||||||
if (i == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i.Items.Any())
|
|
||||||
{
|
|
||||||
return i.Items.ConvertAll(ToResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new List<ProfileQualityItemResource> {ToResource(i)};
|
|
||||||
}).ToList()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ProfileQualityItemResource ToResource(this QualityProfileQualityItem model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new ProfileQualityItemResource
|
|
||||||
{
|
|
||||||
Quality = model.Quality,
|
|
||||||
Allowed = model.Allowed
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static QualityProfile ToModel(this ProfileResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new QualityProfile
|
|
||||||
{
|
|
||||||
Id = resource.Id,
|
|
||||||
|
|
||||||
Name = resource.Name,
|
|
||||||
UpgradeAllowed = resource.UpgradeAllowed,
|
|
||||||
Cutoff = resource.Cutoff.Id,
|
|
||||||
Items = resource.Items.ConvertAll(ToModel)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static QualityProfileQualityItem ToModel(this ProfileQualityItemResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new QualityProfileQualityItem
|
|
||||||
{
|
|
||||||
Quality = (Quality)resource.Quality.Id,
|
|
||||||
Allowed = resource.Allowed
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<ProfileResource> ToResource(this IEnumerable<QualityProfile> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.Profiles.Qualities;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Mapping;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles
|
|
||||||
{
|
|
||||||
public class ProfileSchemaModule : SonarrRestModule<ProfileResource>
|
|
||||||
{
|
|
||||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
|
||||||
|
|
||||||
public ProfileSchemaModule(IQualityDefinitionService qualityDefinitionService)
|
|
||||||
: base("/profile/schema")
|
|
||||||
{
|
|
||||||
_qualityDefinitionService = qualityDefinitionService;
|
|
||||||
|
|
||||||
GetResourceAll = GetAll;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ProfileResource> GetAll()
|
|
||||||
{
|
|
||||||
var items = _qualityDefinitionService.All()
|
|
||||||
.OrderBy(v => v.Weight)
|
|
||||||
.Select(v => new QualityProfileQualityItem { Quality = v.Quality, Allowed = false })
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
var profile = new QualityProfile();
|
|
||||||
profile.Cutoff = Quality.Unknown.Id;
|
|
||||||
profile.Items = items;
|
|
||||||
|
|
||||||
return new List<ProfileResource> { profile.ToResource() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using FluentValidation;
|
|
||||||
using FluentValidation.Validators;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Profiles
|
|
||||||
{
|
|
||||||
public static class ProfileValidation
|
|
||||||
{
|
|
||||||
public static IRuleBuilderOptions<T, IList<ProfileQualityItemResource>> MustHaveAllowedQuality<T>(this IRuleBuilder<T, IList<ProfileQualityItemResource>> ruleBuilder)
|
|
||||||
{
|
|
||||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
|
||||||
|
|
||||||
return ruleBuilder.SetValidator(new AllowedValidator<T>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AllowedValidator<T> : PropertyValidator
|
|
||||||
{
|
|
||||||
public AllowedValidator()
|
|
||||||
: base("Must contain at least one allowed quality")
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool IsValid(PropertyValidatorContext context)
|
|
||||||
{
|
|
||||||
var list = context.PropertyValue as IList<ProfileQualityItemResource>;
|
|
||||||
|
|
||||||
if (list == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!list.Any(c => c.Allowed))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,226 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using FluentValidation;
|
|
||||||
using FluentValidation.Results;
|
|
||||||
using Nancy;
|
|
||||||
using NzbDrone.Common.Reflection;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
|
||||||
using NzbDrone.Common.Serializer;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.ClientSchema;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api
|
|
||||||
{
|
|
||||||
public abstract class ProviderModuleBase<TProviderResource, TProvider, TProviderDefinition> : SonarrRestModule<TProviderResource>
|
|
||||||
where TProviderDefinition : ProviderDefinition, new()
|
|
||||||
where TProvider : IProvider
|
|
||||||
where TProviderResource : ProviderResource, new()
|
|
||||||
{
|
|
||||||
private readonly IProviderFactory<TProvider, TProviderDefinition> _providerFactory;
|
|
||||||
|
|
||||||
protected ProviderModuleBase(IProviderFactory<TProvider, TProviderDefinition> providerFactory, string resource)
|
|
||||||
: base(resource)
|
|
||||||
{
|
|
||||||
_providerFactory = providerFactory;
|
|
||||||
|
|
||||||
Get("schema", x => GetTemplates());
|
|
||||||
Post("test", x => Test(ReadResourceFromRequest(true)));
|
|
||||||
Post("action/{action}", x => RequestAction(x.action, ReadResourceFromRequest(true)));
|
|
||||||
|
|
||||||
GetResourceAll = GetAll;
|
|
||||||
GetResourceById = GetProviderById;
|
|
||||||
CreateResource = CreateProvider;
|
|
||||||
UpdateResource = UpdateProvider;
|
|
||||||
DeleteResource = DeleteProvider;
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
|
||||||
SharedValidator.RuleFor(c => c.Name).Must((v,c) => !_providerFactory.All().Any(p => p.Name == c && p.Id != v.Id)).WithMessage("Should be unique");
|
|
||||||
SharedValidator.RuleFor(c => c.Implementation).NotEmpty();
|
|
||||||
SharedValidator.RuleFor(c => c.ConfigContract).NotEmpty();
|
|
||||||
|
|
||||||
PostValidator.RuleFor(c => c.Fields).NotNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TProviderResource GetProviderById(int id)
|
|
||||||
{
|
|
||||||
var definition = _providerFactory.Get(id);
|
|
||||||
_providerFactory.SetProviderCharacteristics(definition);
|
|
||||||
|
|
||||||
var resource = new TProviderResource();
|
|
||||||
MapToResource(resource, definition);
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TProviderResource> GetAll()
|
|
||||||
{
|
|
||||||
var providerDefinitions = _providerFactory.All().OrderBy(p => p.ImplementationName);
|
|
||||||
|
|
||||||
var result = new List<TProviderResource>(providerDefinitions.Count());
|
|
||||||
|
|
||||||
foreach (var definition in providerDefinitions)
|
|
||||||
{
|
|
||||||
_providerFactory.SetProviderCharacteristics(definition);
|
|
||||||
|
|
||||||
var providerResource = new TProviderResource();
|
|
||||||
MapToResource(providerResource, definition);
|
|
||||||
|
|
||||||
result.Add(providerResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.OrderBy(p => p.Name).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CreateProvider(TProviderResource providerResource)
|
|
||||||
{
|
|
||||||
var providerDefinition = GetDefinition(providerResource, true, false, false);
|
|
||||||
|
|
||||||
if (providerDefinition.Enable)
|
|
||||||
{
|
|
||||||
Test(providerDefinition, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
providerDefinition = _providerFactory.Create(providerDefinition);
|
|
||||||
|
|
||||||
return providerDefinition.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateProvider(TProviderResource providerResource)
|
|
||||||
{
|
|
||||||
var providerDefinition = GetDefinition(providerResource, true, false, false);
|
|
||||||
|
|
||||||
_providerFactory.Update(providerDefinition);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TProviderDefinition GetDefinition(TProviderResource providerResource, bool validate, bool includeWarnings, bool forceValidate)
|
|
||||||
{
|
|
||||||
var definition = new TProviderDefinition();
|
|
||||||
|
|
||||||
MapToModel(definition, providerResource);
|
|
||||||
|
|
||||||
if (validate && (definition.Enable || forceValidate))
|
|
||||||
{
|
|
||||||
Validate(definition, includeWarnings);
|
|
||||||
}
|
|
||||||
|
|
||||||
return definition;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void MapToResource(TProviderResource resource, TProviderDefinition definition)
|
|
||||||
{
|
|
||||||
resource.Id = definition.Id;
|
|
||||||
|
|
||||||
resource.Name = definition.Name;
|
|
||||||
resource.ImplementationName = definition.ImplementationName;
|
|
||||||
resource.Implementation = definition.Implementation;
|
|
||||||
resource.ConfigContract = definition.ConfigContract;
|
|
||||||
resource.Message = definition.Message;
|
|
||||||
|
|
||||||
resource.Fields = SchemaBuilder.ToSchema(definition.Settings);
|
|
||||||
|
|
||||||
resource.InfoLink = $"https://wiki.servarr.com/sonarr/supported#{definition.Implementation.ToLower()}";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void MapToModel(TProviderDefinition definition, TProviderResource resource)
|
|
||||||
{
|
|
||||||
definition.Id = resource.Id;
|
|
||||||
|
|
||||||
definition.Name = resource.Name;
|
|
||||||
definition.ImplementationName = resource.ImplementationName;
|
|
||||||
definition.Implementation = resource.Implementation;
|
|
||||||
definition.ConfigContract = resource.ConfigContract;
|
|
||||||
definition.Message = resource.Message;
|
|
||||||
|
|
||||||
var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract);
|
|
||||||
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFromSchema(resource.Fields, configContract);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteProvider(int id)
|
|
||||||
{
|
|
||||||
_providerFactory.Delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetTemplates()
|
|
||||||
{
|
|
||||||
var defaultDefinitions = _providerFactory.GetDefaultDefinitions().OrderBy(p => p.ImplementationName).ToList();
|
|
||||||
|
|
||||||
var result = new List<TProviderResource>(defaultDefinitions.Count());
|
|
||||||
|
|
||||||
foreach (var providerDefinition in defaultDefinitions)
|
|
||||||
{
|
|
||||||
var providerResource = new TProviderResource();
|
|
||||||
MapToResource(providerResource, providerDefinition);
|
|
||||||
|
|
||||||
var presetDefinitions = _providerFactory.GetPresetDefinitions(providerDefinition);
|
|
||||||
|
|
||||||
providerResource.Presets = presetDefinitions.Select(v =>
|
|
||||||
{
|
|
||||||
var presetResource = new TProviderResource();
|
|
||||||
MapToResource(presetResource, v);
|
|
||||||
|
|
||||||
return presetResource as ProviderResource;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
result.Add(providerResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object Test(TProviderResource providerResource)
|
|
||||||
{
|
|
||||||
var providerDefinition = GetDefinition(providerResource, true, true, true);
|
|
||||||
|
|
||||||
Test(providerDefinition, true);
|
|
||||||
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private object RequestAction(string action, TProviderResource providerResource)
|
|
||||||
{
|
|
||||||
var providerDefinition = GetDefinition(providerResource, false, false, false);
|
|
||||||
|
|
||||||
var query = ((IDictionary<string, object>)Request.Query.ToDictionary()).ToDictionary(k => k.Key, k => k.Value.ToString());
|
|
||||||
|
|
||||||
var data = _providerFactory.RequestAction(providerDefinition, action, query);
|
|
||||||
Response resp = data.ToJson();
|
|
||||||
resp.ContentType = "application/json";
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Validate(TProviderDefinition definition, bool includeWarnings)
|
|
||||||
{
|
|
||||||
var validationResult = definition.Settings.Validate();
|
|
||||||
|
|
||||||
VerifyValidationResult(validationResult, includeWarnings);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Test(TProviderDefinition definition, bool includeWarnings)
|
|
||||||
{
|
|
||||||
var validationResult = _providerFactory.Test(definition);
|
|
||||||
|
|
||||||
VerifyValidationResult(validationResult, includeWarnings);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void VerifyValidationResult(ValidationResult validationResult, bool includeWarnings)
|
|
||||||
{
|
|
||||||
var result = validationResult as NzbDroneValidationResult;
|
|
||||||
|
|
||||||
if (result == null)
|
|
||||||
{
|
|
||||||
result = new NzbDroneValidationResult(validationResult.Errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeWarnings && (!result.IsValid || result.HasWarnings))
|
|
||||||
{
|
|
||||||
throw new ValidationException(result.Failures);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.IsValid)
|
|
||||||
{
|
|
||||||
throw new ValidationException(result.Errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using Sonarr.Http.ClientSchema;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api
|
|
||||||
{
|
|
||||||
public class ProviderResource : RestResource
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public List<Field> Fields { get; set; }
|
|
||||||
public string ImplementationName { get; set; }
|
|
||||||
public string Implementation { get; set; }
|
|
||||||
public string ConfigContract { get; set; }
|
|
||||||
public string InfoLink { get; set; }
|
|
||||||
public ProviderMessage Message { get; set; }
|
|
||||||
|
|
||||||
public List<ProviderResource> Presets { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Mapping;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Qualities
|
|
||||||
{
|
|
||||||
public class QualityDefinitionModule : SonarrRestModule<QualityDefinitionResource>
|
|
||||||
{
|
|
||||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
|
||||||
|
|
||||||
public QualityDefinitionModule(IQualityDefinitionService qualityDefinitionService)
|
|
||||||
{
|
|
||||||
_qualityDefinitionService = qualityDefinitionService;
|
|
||||||
|
|
||||||
GetResourceAll = GetAll;
|
|
||||||
|
|
||||||
GetResourceById = GetById;
|
|
||||||
|
|
||||||
UpdateResource = Update;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update(QualityDefinitionResource resource)
|
|
||||||
{
|
|
||||||
var model = resource.ToModel();
|
|
||||||
_qualityDefinitionService.Update(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private QualityDefinitionResource GetById(int id)
|
|
||||||
{
|
|
||||||
return _qualityDefinitionService.GetById(id).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<QualityDefinitionResource> GetAll()
|
|
||||||
{
|
|
||||||
return _qualityDefinitionService.All().ToResource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Qualities
|
|
||||||
{
|
|
||||||
public class QualityDefinitionResource : RestResource
|
|
||||||
{
|
|
||||||
public Quality Quality { get; set; }
|
|
||||||
|
|
||||||
public string Title { get; set; }
|
|
||||||
|
|
||||||
public int Weight { get; set; }
|
|
||||||
|
|
||||||
public double? MinSize { get; set; }
|
|
||||||
public double? MaxSize { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class QualityDefinitionResourceMapper
|
|
||||||
{
|
|
||||||
public static QualityDefinitionResource ToResource(this QualityDefinition model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new QualityDefinitionResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Quality = model.Quality,
|
|
||||||
|
|
||||||
Title = model.Title,
|
|
||||||
|
|
||||||
Weight = model.Weight,
|
|
||||||
|
|
||||||
MinSize = model.MinSize,
|
|
||||||
MaxSize = model.MaxSize
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static QualityDefinition ToModel(this QualityDefinitionResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new QualityDefinition
|
|
||||||
{
|
|
||||||
Id = resource.Id,
|
|
||||||
|
|
||||||
Quality = resource.Quality,
|
|
||||||
|
|
||||||
Title = resource.Title,
|
|
||||||
|
|
||||||
Weight = resource.Weight,
|
|
||||||
|
|
||||||
MinSize = resource.MinSize,
|
|
||||||
MaxSize = resource.MaxSize
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<QualityDefinitionResource> ToResource(this IEnumerable<QualityDefinition> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,134 +0,0 @@
|
||||||
using System;
|
|
||||||
using Nancy;
|
|
||||||
using Sonarr.Http.Extensions;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Download;
|
|
||||||
using NzbDrone.Core.Download.Pending;
|
|
||||||
using NzbDrone.Core.Download.TrackedDownloads;
|
|
||||||
using NzbDrone.Core.Queue;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Queue
|
|
||||||
{
|
|
||||||
public class QueueActionModule : SonarrRestModule<QueueResource>
|
|
||||||
{
|
|
||||||
private readonly IQueueService _queueService;
|
|
||||||
private readonly ITrackedDownloadService _trackedDownloadService;
|
|
||||||
private readonly ICompletedDownloadService _completedDownloadService;
|
|
||||||
private readonly IFailedDownloadService _failedDownloadService;
|
|
||||||
private readonly IProvideDownloadClient _downloadClientProvider;
|
|
||||||
private readonly IPendingReleaseService _pendingReleaseService;
|
|
||||||
private readonly IDownloadService _downloadService;
|
|
||||||
|
|
||||||
public QueueActionModule(IQueueService queueService,
|
|
||||||
ITrackedDownloadService trackedDownloadService,
|
|
||||||
ICompletedDownloadService completedDownloadService,
|
|
||||||
IFailedDownloadService failedDownloadService,
|
|
||||||
IProvideDownloadClient downloadClientProvider,
|
|
||||||
IPendingReleaseService pendingReleaseService,
|
|
||||||
IDownloadService downloadService)
|
|
||||||
{
|
|
||||||
_queueService = queueService;
|
|
||||||
_trackedDownloadService = trackedDownloadService;
|
|
||||||
_completedDownloadService = completedDownloadService;
|
|
||||||
_failedDownloadService = failedDownloadService;
|
|
||||||
_downloadClientProvider = downloadClientProvider;
|
|
||||||
_pendingReleaseService = pendingReleaseService;
|
|
||||||
_downloadService = downloadService;
|
|
||||||
|
|
||||||
Delete(@"/(?<id>[\d]{1,10})", x => Remove((int)x.Id));
|
|
||||||
Post("/import", x => Import());
|
|
||||||
Post("/grab", x => Grab());
|
|
||||||
}
|
|
||||||
|
|
||||||
private object Remove(int id)
|
|
||||||
{
|
|
||||||
var blocklist = false;
|
|
||||||
var blocklistQuery = Request.Query.blocklist;
|
|
||||||
|
|
||||||
// blacklist maintained for backwards compatability, UI uses blocklist.
|
|
||||||
var blacklistQuery = Request.Query.blacklist;
|
|
||||||
|
|
||||||
if (blocklistQuery.HasValue)
|
|
||||||
{
|
|
||||||
blocklist = Convert.ToBoolean(blocklistQuery.Value);
|
|
||||||
}
|
|
||||||
else if (blacklistQuery.HasValue)
|
|
||||||
{
|
|
||||||
blocklist = Convert.ToBoolean(blacklistQuery.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
|
||||||
|
|
||||||
if (pendingRelease != null)
|
|
||||||
{
|
|
||||||
_pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id);
|
|
||||||
|
|
||||||
return new object();
|
|
||||||
}
|
|
||||||
|
|
||||||
var trackedDownload = GetTrackedDownload(id);
|
|
||||||
|
|
||||||
if (trackedDownload == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var downloadClient = _downloadClientProvider.Get(trackedDownload.DownloadClient);
|
|
||||||
|
|
||||||
if (downloadClient == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException();
|
|
||||||
}
|
|
||||||
|
|
||||||
downloadClient.RemoveItem(trackedDownload.DownloadItem, true);
|
|
||||||
|
|
||||||
if (blocklist)
|
|
||||||
{
|
|
||||||
_failedDownloadService.MarkAsFailed(trackedDownload.DownloadItem.DownloadId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new object();
|
|
||||||
}
|
|
||||||
|
|
||||||
private object Import()
|
|
||||||
{
|
|
||||||
throw new BadRequestException("No longer available");
|
|
||||||
}
|
|
||||||
|
|
||||||
private object Grab()
|
|
||||||
{
|
|
||||||
var resource = Request.Body.FromJson<QueueResource>();
|
|
||||||
|
|
||||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(resource.Id);
|
|
||||||
|
|
||||||
if (pendingRelease == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
_downloadService.DownloadReport(pendingRelease.RemoteEpisode);
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TrackedDownload GetTrackedDownload(int queueId)
|
|
||||||
{
|
|
||||||
var queueItem = _queueService.Find(queueId);
|
|
||||||
|
|
||||||
if (queueItem == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var trackedDownload = _trackedDownloadService.Find(queueItem.DownloadId);
|
|
||||||
|
|
||||||
if (trackedDownload == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return trackedDownload;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.Datastore.Events;
|
|
||||||
using NzbDrone.Core.Download.Pending;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Core.Queue;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Queue
|
|
||||||
{
|
|
||||||
public class QueueModule : SonarrRestModuleWithSignalR<QueueResource, Core.Queue.Queue>,
|
|
||||||
IHandle<QueueUpdatedEvent>, IHandle<PendingReleasesUpdatedEvent>
|
|
||||||
{
|
|
||||||
private readonly IQueueService _queueService;
|
|
||||||
private readonly IPendingReleaseService _pendingReleaseService;
|
|
||||||
|
|
||||||
public QueueModule(IBroadcastSignalRMessage broadcastSignalRMessage, IQueueService queueService, IPendingReleaseService pendingReleaseService)
|
|
||||||
: base(broadcastSignalRMessage)
|
|
||||||
{
|
|
||||||
_queueService = queueService;
|
|
||||||
_pendingReleaseService = pendingReleaseService;
|
|
||||||
GetResourceAll = GetQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<QueueResource> GetQueue()
|
|
||||||
{
|
|
||||||
return GetQueueItems().ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<Core.Queue.Queue> GetQueueItems()
|
|
||||||
{
|
|
||||||
var queue = _queueService.GetQueue().Where(q => q.Series != null);
|
|
||||||
var pending = _pendingReleaseService.GetPendingQueue();
|
|
||||||
|
|
||||||
return queue.Concat(pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(QueueUpdatedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Sync);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(PendingReleasesUpdatedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Sync);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
using NzbDrone.Api.Series;
|
|
||||||
using NzbDrone.Api.Episodes;
|
|
||||||
using NzbDrone.Core.Download.TrackedDownloads;
|
|
||||||
using NzbDrone.Core.Indexers;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Queue
|
|
||||||
{
|
|
||||||
public class QueueResource : RestResource
|
|
||||||
{
|
|
||||||
public SeriesResource Series { get; set; }
|
|
||||||
public EpisodeResource Episode { get; set; }
|
|
||||||
public QualityModel Quality { get; set; }
|
|
||||||
public decimal Size { get; set; }
|
|
||||||
public string Title { get; set; }
|
|
||||||
public decimal Sizeleft { get; set; }
|
|
||||||
public TimeSpan? Timeleft { get; set; }
|
|
||||||
public DateTime? EstimatedCompletionTime { get; set; }
|
|
||||||
public string Status { get; set; }
|
|
||||||
public string TrackedDownloadStatus { get; set; }
|
|
||||||
public List<TrackedDownloadStatusMessage> StatusMessages { get; set; }
|
|
||||||
public string DownloadId { get; set; }
|
|
||||||
public DownloadProtocol Protocol { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class QueueResourceMapper
|
|
||||||
{
|
|
||||||
public static QueueResource ToResource(this Core.Queue.Queue model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new QueueResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Series = model.Series.ToResource(),
|
|
||||||
Episode = model.Episode.ToResource(),
|
|
||||||
Quality = model.Quality,
|
|
||||||
Size = model.Size,
|
|
||||||
Title = model.Title,
|
|
||||||
Sizeleft = model.Sizeleft,
|
|
||||||
Timeleft = model.Timeleft,
|
|
||||||
EstimatedCompletionTime = model.EstimatedCompletionTime,
|
|
||||||
Status = model.Status,
|
|
||||||
TrackedDownloadStatus = model.TrackedDownloadStatus.ToString(),
|
|
||||||
StatusMessages = model.StatusMessages,
|
|
||||||
DownloadId = model.DownloadId,
|
|
||||||
Protocol = model.Protocol
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<QueueResource> ToResource(this IEnumerable<Core.Queue.Queue> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
using NzbDrone.Core.Validation.Paths;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.RemotePathMappings
|
|
||||||
{
|
|
||||||
public class RemotePathMappingModule : SonarrRestModule<RemotePathMappingResource>
|
|
||||||
{
|
|
||||||
private readonly IRemotePathMappingService _remotePathMappingService;
|
|
||||||
|
|
||||||
public RemotePathMappingModule(IRemotePathMappingService remotePathMappingService,
|
|
||||||
PathExistsValidator pathExistsValidator,
|
|
||||||
MappedNetworkDriveValidator mappedNetworkDriveValidator)
|
|
||||||
{
|
|
||||||
_remotePathMappingService = remotePathMappingService;
|
|
||||||
|
|
||||||
GetResourceAll = GetMappings;
|
|
||||||
GetResourceById = GetMappingById;
|
|
||||||
CreateResource = CreateMapping;
|
|
||||||
DeleteResource = DeleteMapping;
|
|
||||||
UpdateResource = UpdateMapping;
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.Host)
|
|
||||||
.NotEmpty();
|
|
||||||
|
|
||||||
// We cannot use IsValidPath here, because it's a remote path, possibly other OS.
|
|
||||||
SharedValidator.RuleFor(c => c.RemotePath)
|
|
||||||
.NotEmpty();
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.LocalPath)
|
|
||||||
.Cascade(CascadeMode.StopOnFirstFailure)
|
|
||||||
.IsValidPath()
|
|
||||||
.SetValidator(mappedNetworkDriveValidator)
|
|
||||||
.SetValidator(pathExistsValidator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RemotePathMappingResource GetMappingById(int id)
|
|
||||||
{
|
|
||||||
return _remotePathMappingService.Get(id).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CreateMapping(RemotePathMappingResource resource)
|
|
||||||
{
|
|
||||||
var model = resource.ToModel();
|
|
||||||
|
|
||||||
return _remotePathMappingService.Add(model).Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<RemotePathMappingResource> GetMappings()
|
|
||||||
{
|
|
||||||
return _remotePathMappingService.All().ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteMapping(int id)
|
|
||||||
{
|
|
||||||
_remotePathMappingService.Remove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateMapping(RemotePathMappingResource resource)
|
|
||||||
{
|
|
||||||
var mapping = resource.ToModel();
|
|
||||||
|
|
||||||
_remotePathMappingService.Update(mapping);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.RemotePathMappings;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.RemotePathMappings
|
|
||||||
{
|
|
||||||
public class RemotePathMappingResource : RestResource
|
|
||||||
{
|
|
||||||
public string Host { get; set; }
|
|
||||||
public string RemotePath { get; set; }
|
|
||||||
public string LocalPath { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RemotePathMappingResourceMapper
|
|
||||||
{
|
|
||||||
public static RemotePathMappingResource ToResource(this RemotePathMapping model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new RemotePathMappingResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Host = model.Host,
|
|
||||||
RemotePath = model.RemotePath,
|
|
||||||
LocalPath = model.LocalPath
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RemotePathMapping ToModel(this RemotePathMappingResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new RemotePathMapping
|
|
||||||
{
|
|
||||||
Id = resource.Id,
|
|
||||||
|
|
||||||
Host = resource.Host,
|
|
||||||
RemotePath = resource.RemotePath,
|
|
||||||
LocalPath = resource.LocalPath
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<RemotePathMappingResource> ToResource(this IEnumerable<RemotePathMapping> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
|
||||||
using FluentValidation.Results;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Profiles.Releases;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Mapping;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Restrictions
|
|
||||||
{
|
|
||||||
public class RestrictionModule : SonarrRestModule<RestrictionResource>
|
|
||||||
{
|
|
||||||
private readonly IReleaseProfileService _releaseProfileService;
|
|
||||||
|
|
||||||
|
|
||||||
public RestrictionModule(IReleaseProfileService releaseProfileService)
|
|
||||||
{
|
|
||||||
_releaseProfileService = releaseProfileService;
|
|
||||||
|
|
||||||
GetResourceById = GetRestriction;
|
|
||||||
GetResourceAll = GetAllRestrictions;
|
|
||||||
CreateResource = CreateRestriction;
|
|
||||||
UpdateResource = UpdateRestriction;
|
|
||||||
DeleteResource = DeleteRestriction;
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(r => r).Custom((restriction, context) =>
|
|
||||||
{
|
|
||||||
if (restriction.Ignored.IsNullOrWhiteSpace() && restriction.Required.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
context.AddFailure("Either 'Must contain' or 'Must not contain' is required");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private RestrictionResource GetRestriction(int id)
|
|
||||||
{
|
|
||||||
return _releaseProfileService.Get(id).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<RestrictionResource> GetAllRestrictions()
|
|
||||||
{
|
|
||||||
return _releaseProfileService.All().ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CreateRestriction(RestrictionResource resource)
|
|
||||||
{
|
|
||||||
return _releaseProfileService.Add(resource.ToModel()).Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateRestriction(RestrictionResource resource)
|
|
||||||
{
|
|
||||||
_releaseProfileService.Update(resource.ToModel());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteRestriction(int id)
|
|
||||||
{
|
|
||||||
_releaseProfileService.Delete(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.Profiles.Releases;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Restrictions
|
|
||||||
{
|
|
||||||
public class RestrictionResource : RestResource
|
|
||||||
{
|
|
||||||
public string Required { get; set; }
|
|
||||||
public string Ignored { get; set; }
|
|
||||||
public HashSet<int> Tags { get; set; }
|
|
||||||
|
|
||||||
public RestrictionResource()
|
|
||||||
{
|
|
||||||
Tags = new HashSet<int>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RestrictionResourceMapper
|
|
||||||
{
|
|
||||||
public static RestrictionResource ToResource(this ReleaseProfile model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new RestrictionResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Required = string.Join(",", model.Required),
|
|
||||||
Ignored = string.Join(",", model.Ignored),
|
|
||||||
Tags = new HashSet<int>(model.Tags)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ReleaseProfile ToModel(this RestrictionResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new ReleaseProfile
|
|
||||||
{
|
|
||||||
Id = resource.Id,
|
|
||||||
|
|
||||||
Required = resource.Required.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
|
|
||||||
Ignored = resource.Ignored.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
|
|
||||||
Tags = new HashSet<int>(resource.Tags)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<RestrictionResource> ToResource(this IEnumerable<ReleaseProfile> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.RootFolders;
|
|
||||||
using NzbDrone.Core.Validation.Paths;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Mapping;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.RootFolders
|
|
||||||
{
|
|
||||||
public class RootFolderModule : SonarrRestModuleWithSignalR<RootFolderResource, RootFolder>
|
|
||||||
{
|
|
||||||
private readonly IRootFolderService _rootFolderService;
|
|
||||||
|
|
||||||
public RootFolderModule(IRootFolderService rootFolderService,
|
|
||||||
IBroadcastSignalRMessage signalRBroadcaster,
|
|
||||||
RootFolderValidator rootFolderValidator,
|
|
||||||
PathExistsValidator pathExistsValidator,
|
|
||||||
MappedNetworkDriveValidator mappedNetworkDriveValidator,
|
|
||||||
StartupFolderValidator startupFolderValidator,
|
|
||||||
SystemFolderValidator systemFolderValidator,
|
|
||||||
FolderWritableValidator folderWritableValidator
|
|
||||||
)
|
|
||||||
: base(signalRBroadcaster)
|
|
||||||
{
|
|
||||||
_rootFolderService = rootFolderService;
|
|
||||||
|
|
||||||
GetResourceAll = GetRootFolders;
|
|
||||||
GetResourceById = GetRootFolder;
|
|
||||||
CreateResource = CreateRootFolder;
|
|
||||||
DeleteResource = DeleteFolder;
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.Path)
|
|
||||||
.Cascade(CascadeMode.StopOnFirstFailure)
|
|
||||||
.IsValidPath()
|
|
||||||
.SetValidator(rootFolderValidator)
|
|
||||||
.SetValidator(mappedNetworkDriveValidator)
|
|
||||||
.SetValidator(startupFolderValidator)
|
|
||||||
.SetValidator(pathExistsValidator)
|
|
||||||
.SetValidator(systemFolderValidator)
|
|
||||||
.SetValidator(folderWritableValidator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private RootFolderResource GetRootFolder(int id)
|
|
||||||
{
|
|
||||||
return _rootFolderService.Get(id, true).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CreateRootFolder(RootFolderResource rootFolderResource)
|
|
||||||
{
|
|
||||||
var model = rootFolderResource.ToModel();
|
|
||||||
|
|
||||||
return _rootFolderService.Add(model).Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<RootFolderResource> GetRootFolders()
|
|
||||||
{
|
|
||||||
return _rootFolderService.AllWithUnmappedFolders().ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteFolder(int id)
|
|
||||||
{
|
|
||||||
_rootFolderService.Remove(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.RootFolders;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.RootFolders
|
|
||||||
{
|
|
||||||
public class RootFolderResource : RestResource
|
|
||||||
{
|
|
||||||
public string Path { get; set; }
|
|
||||||
public long? FreeSpace { get; set; }
|
|
||||||
public long? TotalSpace { get; set; }
|
|
||||||
|
|
||||||
public List<UnmappedFolder> UnmappedFolders { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class RootFolderResourceMapper
|
|
||||||
{
|
|
||||||
public static RootFolderResource ToResource(this RootFolder model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new RootFolderResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Path = model.Path,
|
|
||||||
FreeSpace = model.FreeSpace,
|
|
||||||
TotalSpace = model.TotalSpace,
|
|
||||||
UnmappedFolders = model.UnmappedFolders
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RootFolder ToModel(this RootFolderResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new RootFolder
|
|
||||||
{
|
|
||||||
Id = resource.Id,
|
|
||||||
|
|
||||||
Path = resource.Path,
|
|
||||||
//FreeSpace
|
|
||||||
//UnmappedFolders
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<RootFolderResource> ToResource(this IEnumerable<RootFolder> models)
|
|
||||||
{
|
|
||||||
return models.Select(ToResource).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using Nancy;
|
|
||||||
using Sonarr.Http.Extensions;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.SeasonPass
|
|
||||||
{
|
|
||||||
public class SeasonPassModule : NzbDroneApiModule
|
|
||||||
{
|
|
||||||
private readonly IEpisodeMonitoredService _episodeMonitoredService;
|
|
||||||
|
|
||||||
public SeasonPassModule(IEpisodeMonitoredService episodeMonitoredService)
|
|
||||||
: base("/seasonpass")
|
|
||||||
{
|
|
||||||
_episodeMonitoredService = episodeMonitoredService;
|
|
||||||
Post("/", series => UpdateAll());
|
|
||||||
}
|
|
||||||
|
|
||||||
private object UpdateAll()
|
|
||||||
{
|
|
||||||
//Read from request
|
|
||||||
var request = Request.Body.FromJson<SeasonPassResource>();
|
|
||||||
|
|
||||||
foreach (var s in request.Series)
|
|
||||||
{
|
|
||||||
_episodeMonitoredService.SetEpisodeMonitoredStatus(s, request.MonitoringOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResponseWithCode("ok", HttpStatusCode.Accepted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.SeasonPass
|
|
||||||
{
|
|
||||||
public class SeasonPassResource
|
|
||||||
{
|
|
||||||
public List<Core.Tv.Series> Series { get; set; }
|
|
||||||
public MonitoringOptions MonitoringOptions { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
using NzbDrone.Core.DataAugmentation.Scene;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Series
|
|
||||||
{
|
|
||||||
public class AlternateTitleResource
|
|
||||||
{
|
|
||||||
public string Title { get; set; }
|
|
||||||
public int? SeasonNumber { get; set; }
|
|
||||||
public int? SceneSeasonNumber { get; set; }
|
|
||||||
public string SceneOrigin { get; set; }
|
|
||||||
public string Comment { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AlternateTitleResourceMapper
|
|
||||||
{
|
|
||||||
public static AlternateTitleResource ToResource(this SceneMapping sceneMapping)
|
|
||||||
{
|
|
||||||
if (sceneMapping == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AlternateTitleResource
|
|
||||||
{
|
|
||||||
Title = sceneMapping.Title,
|
|
||||||
SeasonNumber = sceneMapping.SeasonNumber,
|
|
||||||
SceneSeasonNumber = sceneMapping.SceneSeasonNumber,
|
|
||||||
SceneOrigin = sceneMapping.SceneOrigin,
|
|
||||||
Comment = sceneMapping.Comment
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.MediaCover;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
namespace NzbDrone.Api.Series
|
|
||||||
{
|
|
||||||
public class SeasonResource
|
|
||||||
{
|
|
||||||
public int SeasonNumber { get; set; }
|
|
||||||
public bool Monitored { get; set; }
|
|
||||||
public SeasonStatisticsResource Statistics { get; set; }
|
|
||||||
public List<MediaCover> Images { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SeasonResourceMapper
|
|
||||||
{
|
|
||||||
public static SeasonResource ToResource(this Season model, bool includeImages = false)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new SeasonResource
|
|
||||||
{
|
|
||||||
SeasonNumber = model.SeasonNumber,
|
|
||||||
Monitored = model.Monitored,
|
|
||||||
Images = includeImages ? model.Images : null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Season ToModel(this SeasonResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new Season
|
|
||||||
{
|
|
||||||
SeasonNumber = resource.SeasonNumber,
|
|
||||||
Monitored = resource.Monitored,
|
|
||||||
Images = resource.Images
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<SeasonResource> ToResource(this IEnumerable<Season> models, bool includeImages = false)
|
|
||||||
{
|
|
||||||
return models.Select(s => ToResource(s, includeImages)).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Season> ToModel(this IEnumerable<SeasonResource> resources)
|
|
||||||
{
|
|
||||||
return resources?.Select(ToModel).ToList() ?? new List<Season>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
using System;
|
|
||||||
using NzbDrone.Core.SeriesStats;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Series
|
|
||||||
{
|
|
||||||
public class SeasonStatisticsResource
|
|
||||||
{
|
|
||||||
public DateTime? NextAiring { get; set; }
|
|
||||||
public DateTime? PreviousAiring { get; set; }
|
|
||||||
public int EpisodeFileCount { get; set; }
|
|
||||||
public int EpisodeCount { get; set; }
|
|
||||||
public int TotalEpisodeCount { get; set; }
|
|
||||||
public long SizeOnDisk { get; set; }
|
|
||||||
|
|
||||||
public decimal PercentOfEpisodes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (EpisodeCount == 0) return 0;
|
|
||||||
|
|
||||||
return (decimal)EpisodeFileCount / (decimal)EpisodeCount * 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SeasonStatisticsResourceMapper
|
|
||||||
{
|
|
||||||
public static SeasonStatisticsResource ToResource(this SeasonStatistics model)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new SeasonStatisticsResource
|
|
||||||
{
|
|
||||||
NextAiring = model.NextAiring,
|
|
||||||
PreviousAiring = model.PreviousAiring,
|
|
||||||
EpisodeFileCount = model.EpisodeFileCount,
|
|
||||||
EpisodeCount = model.EpisodeFileCount,
|
|
||||||
TotalEpisodeCount = model.TotalEpisodeCount,
|
|
||||||
SizeOnDisk = model.SizeOnDisk
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Nancy;
|
|
||||||
using NzbDrone.Core.Profiles.Languages;
|
|
||||||
using Sonarr.Http.Extensions;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Series
|
|
||||||
{
|
|
||||||
public class SeriesEditorModule : NzbDroneApiModule
|
|
||||||
{
|
|
||||||
private readonly ISeriesService _seriesService;
|
|
||||||
private readonly ILanguageProfileService _languageProfileService;
|
|
||||||
|
|
||||||
public SeriesEditorModule(ISeriesService seriesService, ILanguageProfileService languageProfileService)
|
|
||||||
: base("/series/editor")
|
|
||||||
{
|
|
||||||
_seriesService = seriesService;
|
|
||||||
_languageProfileService = languageProfileService;
|
|
||||||
Put("/", series => SaveAll());
|
|
||||||
}
|
|
||||||
|
|
||||||
private object SaveAll()
|
|
||||||
{
|
|
||||||
var resources = Request.Body.FromJson<List<SeriesResource>>();
|
|
||||||
|
|
||||||
var seriesToUpdate = resources.Select(seriesResource =>
|
|
||||||
{
|
|
||||||
var series = _seriesService.GetSeries(seriesResource.Id);
|
|
||||||
var updatedSeries = seriesResource.ToModel(series);
|
|
||||||
|
|
||||||
// If the new language profile doens't exist, keep it the same.
|
|
||||||
// This could happen if a 3rd-party app uses this endpoint to update a
|
|
||||||
// series and doesn't pass the languageProfileI as well.
|
|
||||||
|
|
||||||
if (!_languageProfileService.Exists(updatedSeries.LanguageProfileId))
|
|
||||||
{
|
|
||||||
updatedSeries.LanguageProfileId = series.LanguageProfileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedSeries;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
return ResponseWithCode(_seriesService.UpdateSeries(seriesToUpdate, true)
|
|
||||||
.ToResource(false)
|
|
||||||
, HttpStatusCode.Accepted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using Nancy;
|
|
||||||
using NzbDrone.Core.MediaCover;
|
|
||||||
using NzbDrone.Core.MetadataSource;
|
|
||||||
using System.Linq;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Series
|
|
||||||
{
|
|
||||||
public class SeriesLookupModule : SonarrRestModule<SeriesResource>
|
|
||||||
{
|
|
||||||
private readonly ISearchForNewSeries _searchProxy;
|
|
||||||
|
|
||||||
public SeriesLookupModule(ISearchForNewSeries searchProxy)
|
|
||||||
: base("/series/lookup")
|
|
||||||
{
|
|
||||||
_searchProxy = searchProxy;
|
|
||||||
Get("/", x => Search());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private object Search()
|
|
||||||
{
|
|
||||||
var tvDbResults = _searchProxy.SearchForNewSeries((string)Request.Query.term);
|
|
||||||
return MapToResource(tvDbResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static IEnumerable<SeriesResource> MapToResource(IEnumerable<Core.Tv.Series> series)
|
|
||||||
{
|
|
||||||
foreach (var currentSeries in series)
|
|
||||||
{
|
|
||||||
var resource = currentSeries.ToResource();
|
|
||||||
var poster = currentSeries.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
|
|
||||||
if (poster != null)
|
|
||||||
{
|
|
||||||
resource.RemotePoster = poster.Url;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return resource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,268 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Datastore.Events;
|
|
||||||
using NzbDrone.Core.MediaCover;
|
|
||||||
using NzbDrone.Core.MediaFiles;
|
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Core.SeriesStats;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
using NzbDrone.Core.Tv.Events;
|
|
||||||
using NzbDrone.Core.Validation.Paths;
|
|
||||||
using NzbDrone.Core.DataAugmentation.Scene;
|
|
||||||
using NzbDrone.Core.Profiles.Languages;
|
|
||||||
using NzbDrone.Core.Validation;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Extensions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Series
|
|
||||||
{
|
|
||||||
public class SeriesModule : SonarrRestModuleWithSignalR<SeriesResource, Core.Tv.Series>,
|
|
||||||
IHandle<EpisodeImportedEvent>,
|
|
||||||
IHandle<EpisodeFileDeletedEvent>,
|
|
||||||
IHandle<SeriesUpdatedEvent>,
|
|
||||||
IHandle<SeriesEditedEvent>,
|
|
||||||
IHandle<SeriesDeletedEvent>,
|
|
||||||
IHandle<SeriesRenamedEvent>,
|
|
||||||
IHandle<MediaCoversUpdatedEvent>
|
|
||||||
|
|
||||||
{
|
|
||||||
private readonly ISeriesService _seriesService;
|
|
||||||
private readonly IAddSeriesService _addSeriesService;
|
|
||||||
private readonly ISeriesStatisticsService _seriesStatisticsService;
|
|
||||||
private readonly ISceneMappingService _sceneMappingService;
|
|
||||||
private readonly IMapCoversToLocal _coverMapper;
|
|
||||||
private readonly ILanguageProfileService _languageProfileService;
|
|
||||||
|
|
||||||
public SeriesModule(IBroadcastSignalRMessage signalRBroadcaster,
|
|
||||||
ISeriesService seriesService,
|
|
||||||
IAddSeriesService addSeriesService,
|
|
||||||
ISeriesStatisticsService seriesStatisticsService,
|
|
||||||
ISceneMappingService sceneMappingService,
|
|
||||||
IMapCoversToLocal coverMapper,
|
|
||||||
ILanguageProfileService languageProfileService,
|
|
||||||
RootFolderValidator rootFolderValidator,
|
|
||||||
SeriesPathValidator seriesPathValidator,
|
|
||||||
SeriesExistsValidator seriesExistsValidator,
|
|
||||||
SeriesAncestorValidator seriesAncestorValidator,
|
|
||||||
SystemFolderValidator systemFolderValidator,
|
|
||||||
ProfileExistsValidator profileExistsValidator,
|
|
||||||
LanguageProfileExistsValidator languageProfileExistsValidator
|
|
||||||
)
|
|
||||||
: base(signalRBroadcaster)
|
|
||||||
{
|
|
||||||
_seriesService = seriesService;
|
|
||||||
_addSeriesService = addSeriesService;
|
|
||||||
_seriesStatisticsService = seriesStatisticsService;
|
|
||||||
_sceneMappingService = sceneMappingService;
|
|
||||||
|
|
||||||
_coverMapper = coverMapper;
|
|
||||||
_languageProfileService = languageProfileService;
|
|
||||||
|
|
||||||
GetResourceAll = AllSeries;
|
|
||||||
GetResourceById = GetSeries;
|
|
||||||
CreateResource = AddSeries;
|
|
||||||
UpdateResource = UpdateSeries;
|
|
||||||
DeleteResource = DeleteSeries;
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(s => s.ProfileId).ValidId();
|
|
||||||
SharedValidator.RuleFor(s => s.LanguageProfileId);
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(s => s.Path)
|
|
||||||
.Cascade(CascadeMode.StopOnFirstFailure)
|
|
||||||
.IsValidPath()
|
|
||||||
.SetValidator(rootFolderValidator)
|
|
||||||
.SetValidator(seriesPathValidator)
|
|
||||||
.SetValidator(seriesAncestorValidator)
|
|
||||||
.SetValidator(systemFolderValidator)
|
|
||||||
.When(s => !s.Path.IsNullOrWhiteSpace());
|
|
||||||
|
|
||||||
SharedValidator.RuleFor(s => s.ProfileId).SetValidator(profileExistsValidator);
|
|
||||||
|
|
||||||
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
|
|
||||||
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
|
|
||||||
PostValidator.RuleFor(s => s.TvdbId).GreaterThan(0).SetValidator(seriesExistsValidator);
|
|
||||||
PostValidator.RuleFor(s => s.LanguageProfileId).SetValidator(languageProfileExistsValidator).When(s => s.LanguageProfileId != 0);
|
|
||||||
|
|
||||||
PutValidator.RuleFor(s => s.Path).IsValidPath();
|
|
||||||
|
|
||||||
// Ensure any editing has a valid LanguageProfile
|
|
||||||
PutValidator.RuleFor(s => s.LanguageProfileId).SetValidator(languageProfileExistsValidator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SeriesResource GetSeries(int id)
|
|
||||||
{
|
|
||||||
var includeSeasonImages = Context != null && Request.GetBooleanQueryParameter("includeSeasonImages");
|
|
||||||
|
|
||||||
var series = _seriesService.GetSeries(id);
|
|
||||||
return MapToResource(series, includeSeasonImages);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SeriesResource> AllSeries()
|
|
||||||
{
|
|
||||||
var includeSeasonImages = Request.GetBooleanQueryParameter("includeSeasonImages");
|
|
||||||
var seriesStats = _seriesStatisticsService.SeriesStatistics();
|
|
||||||
var seriesResources = _seriesService.GetAllSeries().Select(s => s.ToResource(includeSeasonImages)).ToList();
|
|
||||||
|
|
||||||
MapCoversToLocal(seriesResources.ToArray());
|
|
||||||
LinkSeriesStatistics(seriesResources, seriesStats);
|
|
||||||
PopulateAlternateTitles(seriesResources);
|
|
||||||
|
|
||||||
return seriesResources;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int AddSeries(SeriesResource seriesResource)
|
|
||||||
{
|
|
||||||
var model = seriesResource.ToModel();
|
|
||||||
|
|
||||||
// Set a default LanguageProfileId to maintain backwards compatibility with apps using the v2 API
|
|
||||||
if (model.LanguageProfileId == 0 || !_languageProfileService.Exists(model.LanguageProfileId))
|
|
||||||
{
|
|
||||||
model.LanguageProfileId = _languageProfileService.All().First().Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _addSeriesService.AddSeries(model).Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateSeries(SeriesResource seriesResource)
|
|
||||||
{
|
|
||||||
var model = seriesResource.ToModel(_seriesService.GetSeries(seriesResource.Id));
|
|
||||||
|
|
||||||
_seriesService.UpdateSeries(model);
|
|
||||||
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, seriesResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteSeries(int id)
|
|
||||||
{
|
|
||||||
var deleteFiles = false;
|
|
||||||
var deleteFilesQuery = Request.Query.deleteFiles;
|
|
||||||
|
|
||||||
if (deleteFilesQuery.HasValue)
|
|
||||||
{
|
|
||||||
deleteFiles = Convert.ToBoolean(deleteFilesQuery.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
_seriesService.DeleteSeries(id, deleteFiles, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SeriesResource MapToResource(Core.Tv.Series series, bool includeSeasonImages)
|
|
||||||
{
|
|
||||||
if (series == null) return null;
|
|
||||||
|
|
||||||
var resource = series.ToResource(includeSeasonImages);
|
|
||||||
MapCoversToLocal(resource);
|
|
||||||
FetchAndLinkSeriesStatistics(resource);
|
|
||||||
PopulateAlternateTitles(resource);
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MapCoversToLocal(params SeriesResource[] series)
|
|
||||||
{
|
|
||||||
foreach (var seriesResource in series)
|
|
||||||
{
|
|
||||||
_coverMapper.ConvertToLocalUrls(seriesResource.Id, seriesResource.Images);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FetchAndLinkSeriesStatistics(SeriesResource resource)
|
|
||||||
{
|
|
||||||
LinkSeriesStatistics(resource, _seriesStatisticsService.SeriesStatistics(resource.Id));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LinkSeriesStatistics(List<SeriesResource> resources, List<SeriesStatistics> seriesStatistics)
|
|
||||||
{
|
|
||||||
var dictSeriesStats = seriesStatistics.ToDictionary(v => v.SeriesId);
|
|
||||||
|
|
||||||
foreach (var series in resources)
|
|
||||||
{
|
|
||||||
var stats = dictSeriesStats.GetValueOrDefault(series.Id);
|
|
||||||
if (stats == null) continue;
|
|
||||||
|
|
||||||
LinkSeriesStatistics(series, stats);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LinkSeriesStatistics(SeriesResource resource, SeriesStatistics seriesStatistics)
|
|
||||||
{
|
|
||||||
resource.TotalEpisodeCount = seriesStatistics.TotalEpisodeCount;
|
|
||||||
resource.EpisodeCount = seriesStatistics.EpisodeCount;
|
|
||||||
resource.EpisodeFileCount = seriesStatistics.EpisodeFileCount;
|
|
||||||
resource.NextAiring = seriesStatistics.NextAiring;
|
|
||||||
resource.PreviousAiring = seriesStatistics.PreviousAiring;
|
|
||||||
resource.SizeOnDisk = seriesStatistics.SizeOnDisk;
|
|
||||||
|
|
||||||
if (seriesStatistics.SeasonStatistics != null)
|
|
||||||
{
|
|
||||||
var dictSeasonStats = seriesStatistics.SeasonStatistics.ToDictionary(v => v.SeasonNumber);
|
|
||||||
|
|
||||||
foreach (var season in resource.Seasons)
|
|
||||||
{
|
|
||||||
season.Statistics = dictSeasonStats.GetValueOrDefault(season.SeasonNumber).ToResource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PopulateAlternateTitles(List<SeriesResource> resources)
|
|
||||||
{
|
|
||||||
foreach (var resource in resources)
|
|
||||||
{
|
|
||||||
PopulateAlternateTitles(resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PopulateAlternateTitles(SeriesResource resource)
|
|
||||||
{
|
|
||||||
var mappings = _sceneMappingService.FindByTvdbId(resource.TvdbId);
|
|
||||||
|
|
||||||
if (mappings == null) return;
|
|
||||||
|
|
||||||
resource.AlternateTitles = mappings.ConvertAll(AlternateTitleResourceMapper.ToResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(EpisodeImportedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.ImportedEpisode.SeriesId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(EpisodeFileDeletedEvent message)
|
|
||||||
{
|
|
||||||
if (message.Reason == DeleteMediaFileReason.Upgrade) return;
|
|
||||||
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.SeriesId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(SeriesUpdatedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.Series.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(SeriesEditedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.Series.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(SeriesDeletedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Deleted, message.Series.ToResource());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(SeriesRenamedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.Series.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(MediaCoversUpdatedEvent message)
|
|
||||||
{
|
|
||||||
if (message.Updated)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Updated, message.Series.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.MediaCover;
|
|
||||||
using NzbDrone.Core.Tv;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Series
|
|
||||||
{
|
|
||||||
public class SeriesResource : RestResource
|
|
||||||
{
|
|
||||||
public SeriesResource()
|
|
||||||
{
|
|
||||||
Monitored = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Todo: Sorters should be done completely on the client
|
|
||||||
//Todo: Is there an easy way to keep IgnoreArticlesWhenSorting in sync between, Series, History, Missing?
|
|
||||||
//Todo: We should get the entire QualityProfile instead of ID and Name separately
|
|
||||||
|
|
||||||
//View Only
|
|
||||||
public string Title { get; set; }
|
|
||||||
public List<AlternateTitleResource> AlternateTitles { get; set; }
|
|
||||||
public string SortTitle { get; set; }
|
|
||||||
|
|
||||||
public int SeasonCount
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Seasons == null) return 0;
|
|
||||||
|
|
||||||
return Seasons.Where(s => s.SeasonNumber > 0).Count();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? TotalEpisodeCount { get; set; }
|
|
||||||
public int? EpisodeCount { get; set; }
|
|
||||||
public int? EpisodeFileCount { get; set; }
|
|
||||||
public long? SizeOnDisk { get; set; }
|
|
||||||
public SeriesStatusType Status { get; set; }
|
|
||||||
public string Overview { get; set; }
|
|
||||||
public DateTime? NextAiring { get; set; }
|
|
||||||
public DateTime? PreviousAiring { get; set; }
|
|
||||||
public string Network { get; set; }
|
|
||||||
public string AirTime { get; set; }
|
|
||||||
public List<MediaCover> Images { get; set; }
|
|
||||||
|
|
||||||
public string RemotePoster { get; set; }
|
|
||||||
public List<SeasonResource> Seasons { get; set; }
|
|
||||||
public int Year { get; set; }
|
|
||||||
|
|
||||||
//View & Edit
|
|
||||||
public string Path { get; set; }
|
|
||||||
public int ProfileId { get; set; }
|
|
||||||
public int LanguageProfileId { get; set; }
|
|
||||||
|
|
||||||
//Editing Only
|
|
||||||
public bool SeasonFolder { get; set; }
|
|
||||||
public bool Monitored { get; set; }
|
|
||||||
|
|
||||||
public bool UseSceneNumbering { get; set; }
|
|
||||||
public int Runtime { get; set; }
|
|
||||||
public int TvdbId { get; set; }
|
|
||||||
public int TvRageId { get; set; }
|
|
||||||
public int TvMazeId { get; set; }
|
|
||||||
public DateTime? FirstAired { get; set; }
|
|
||||||
public DateTime? LastInfoSync { get; set; }
|
|
||||||
public SeriesTypes SeriesType { get; set; }
|
|
||||||
public string CleanTitle { get; set; }
|
|
||||||
public string ImdbId { get; set; }
|
|
||||||
public string TitleSlug { get; set; }
|
|
||||||
public string RootFolderPath { get; set; }
|
|
||||||
public string Certification { get; set; }
|
|
||||||
public List<string> Genres { get; set; }
|
|
||||||
public HashSet<int> Tags { get; set; }
|
|
||||||
public DateTime Added { get; set; }
|
|
||||||
public AddSeriesOptions AddOptions { get; set; }
|
|
||||||
public Ratings Ratings { get; set; }
|
|
||||||
|
|
||||||
//Used to support legacy consumers
|
|
||||||
public int QualityProfileId
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return ProfileId;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value > 0 && ProfileId == 0)
|
|
||||||
{
|
|
||||||
ProfileId = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SeriesResourceMapper
|
|
||||||
{
|
|
||||||
public static SeriesResource ToResource(this Core.Tv.Series model, bool includeSeasonImages = false)
|
|
||||||
{
|
|
||||||
if (model == null) return null;
|
|
||||||
|
|
||||||
return new SeriesResource
|
|
||||||
{
|
|
||||||
Id = model.Id,
|
|
||||||
|
|
||||||
Title = model.Title,
|
|
||||||
//AlternateTitles
|
|
||||||
SortTitle = model.SortTitle,
|
|
||||||
|
|
||||||
//TotalEpisodeCount
|
|
||||||
//EpisodeCount
|
|
||||||
//EpisodeFileCount
|
|
||||||
//SizeOnDisk
|
|
||||||
Status = model.Status,
|
|
||||||
Overview = model.Overview,
|
|
||||||
//NextAiring
|
|
||||||
//PreviousAiring
|
|
||||||
Network = model.Network,
|
|
||||||
AirTime = model.AirTime,
|
|
||||||
Images = model.Images,
|
|
||||||
|
|
||||||
Seasons = model.Seasons.ToResource(includeSeasonImages),
|
|
||||||
Year = model.Year,
|
|
||||||
|
|
||||||
Path = model.Path,
|
|
||||||
ProfileId = model.QualityProfileId,
|
|
||||||
LanguageProfileId = model.LanguageProfileId,
|
|
||||||
|
|
||||||
SeasonFolder = model.SeasonFolder,
|
|
||||||
Monitored = model.Monitored,
|
|
||||||
|
|
||||||
UseSceneNumbering = model.UseSceneNumbering,
|
|
||||||
Runtime = model.Runtime,
|
|
||||||
TvdbId = model.TvdbId,
|
|
||||||
TvRageId = model.TvRageId,
|
|
||||||
TvMazeId = model.TvMazeId,
|
|
||||||
FirstAired = model.FirstAired,
|
|
||||||
LastInfoSync = model.LastInfoSync,
|
|
||||||
SeriesType = model.SeriesType,
|
|
||||||
CleanTitle = model.CleanTitle,
|
|
||||||
ImdbId = model.ImdbId,
|
|
||||||
TitleSlug = model.TitleSlug,
|
|
||||||
RootFolderPath = model.RootFolderPath,
|
|
||||||
Certification = model.Certification,
|
|
||||||
Genres = model.Genres,
|
|
||||||
Tags = model.Tags,
|
|
||||||
Added = model.Added,
|
|
||||||
AddOptions = model.AddOptions,
|
|
||||||
Ratings = model.Ratings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Core.Tv.Series ToModel(this SeriesResource resource)
|
|
||||||
{
|
|
||||||
if (resource == null) return null;
|
|
||||||
|
|
||||||
return new Core.Tv.Series
|
|
||||||
{
|
|
||||||
Id = resource.Id,
|
|
||||||
|
|
||||||
Title = resource.Title,
|
|
||||||
//AlternateTitles
|
|
||||||
SortTitle = resource.SortTitle,
|
|
||||||
|
|
||||||
//TotalEpisodeCount
|
|
||||||
//EpisodeCount
|
|
||||||
//EpisodeFileCount
|
|
||||||
//SizeOnDisk
|
|
||||||
Status = resource.Status,
|
|
||||||
Overview = resource.Overview,
|
|
||||||
//NextAiring
|
|
||||||
//PreviousAiring
|
|
||||||
Network = resource.Network,
|
|
||||||
AirTime = resource.AirTime,
|
|
||||||
Images = resource.Images,
|
|
||||||
|
|
||||||
Seasons = resource.Seasons.ToModel(),
|
|
||||||
Year = resource.Year,
|
|
||||||
|
|
||||||
Path = resource.Path,
|
|
||||||
QualityProfileId = resource.ProfileId,
|
|
||||||
LanguageProfileId = resource.LanguageProfileId,
|
|
||||||
|
|
||||||
SeasonFolder = resource.SeasonFolder,
|
|
||||||
Monitored = resource.Monitored,
|
|
||||||
|
|
||||||
UseSceneNumbering = resource.UseSceneNumbering,
|
|
||||||
Runtime = resource.Runtime,
|
|
||||||
TvdbId = resource.TvdbId,
|
|
||||||
TvRageId = resource.TvRageId,
|
|
||||||
TvMazeId = resource.TvMazeId,
|
|
||||||
FirstAired = resource.FirstAired,
|
|
||||||
LastInfoSync = resource.LastInfoSync,
|
|
||||||
SeriesType = resource.SeriesType,
|
|
||||||
CleanTitle = resource.CleanTitle,
|
|
||||||
ImdbId = resource.ImdbId,
|
|
||||||
TitleSlug = resource.TitleSlug,
|
|
||||||
RootFolderPath = resource.RootFolderPath,
|
|
||||||
Certification = resource.Certification,
|
|
||||||
Genres = resource.Genres,
|
|
||||||
Tags = resource.Tags,
|
|
||||||
Added = resource.Added,
|
|
||||||
AddOptions = resource.AddOptions,
|
|
||||||
Ratings = resource.Ratings
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Core.Tv.Series ToModel(this SeriesResource resource, Core.Tv.Series series)
|
|
||||||
{
|
|
||||||
var updatedSeries = resource.ToModel();
|
|
||||||
|
|
||||||
series.ApplyChanges(updatedSeries);
|
|
||||||
|
|
||||||
return series;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<SeriesResource> ToResource(this IEnumerable<Core.Tv.Series> series, bool includeSeasonImages)
|
|
||||||
{
|
|
||||||
return series.Select(s => ToResource(s, includeSeasonImages)).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net462</TargetFramework>
|
|
||||||
<Platforms>x86</Platforms>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="FluentValidation" Version="8.4.0" />
|
|
||||||
<PackageReference Include="Ical.Net" Version="4.1.11" />
|
|
||||||
<PackageReference Include="Nancy" Version="2.0.0" />
|
|
||||||
<PackageReference Include="Nancy.Authentication.Basic" Version="2.0.0" />
|
|
||||||
<PackageReference Include="Nancy.Authentication.Forms" Version="2.0.0" />
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\NzbDrone.Core\Sonarr.Core.csproj" />
|
|
||||||
<ProjectReference Include="..\Sonarr.Http\Sonarr.Http.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
|
@ -1,33 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using NzbDrone.Core.Backup;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.System.Backup
|
|
||||||
{
|
|
||||||
public class BackupModule : SonarrRestModule<BackupResource>
|
|
||||||
{
|
|
||||||
private readonly IBackupService _backupService;
|
|
||||||
|
|
||||||
public BackupModule(IBackupService backupService) : base("system/backup")
|
|
||||||
{
|
|
||||||
_backupService = backupService;
|
|
||||||
GetResourceAll = GetBackupFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<BackupResource> GetBackupFiles()
|
|
||||||
{
|
|
||||||
var backups = _backupService.GetBackups();
|
|
||||||
|
|
||||||
return backups.Select(b => new BackupResource
|
|
||||||
{
|
|
||||||
Id = b.Name.GetHashCode(),
|
|
||||||
Name = b.Name,
|
|
||||||
Path = $"/backup/{b.Type.ToString().ToLower()}/{b.Name}",
|
|
||||||
Type = b.Type,
|
|
||||||
Time = b.Time
|
|
||||||
}).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
using System;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
using NzbDrone.Core.Backup;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.System.Backup
|
|
||||||
{
|
|
||||||
public class BackupResource : RestResource
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string Path { get; set; }
|
|
||||||
public BackupType Type { get; set; }
|
|
||||||
public DateTime Time { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
using Nancy.Routing;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using NzbDrone.Core.Lifecycle;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.System
|
|
||||||
{
|
|
||||||
public class SystemModule : NzbDroneApiModule
|
|
||||||
{
|
|
||||||
private readonly IAppFolderInfo _appFolderInfo;
|
|
||||||
private readonly IRuntimeInfo _runtimeInfo;
|
|
||||||
private readonly IPlatformInfo _platformInfo;
|
|
||||||
private readonly IOsInfo _osInfo;
|
|
||||||
private readonly IRouteCacheProvider _routeCacheProvider;
|
|
||||||
private readonly IConfigFileProvider _configFileProvider;
|
|
||||||
private readonly IMainDatabase _database;
|
|
||||||
private readonly ILifecycleService _lifecycleService;
|
|
||||||
|
|
||||||
public SystemModule(IAppFolderInfo appFolderInfo,
|
|
||||||
IRuntimeInfo runtimeInfo,
|
|
||||||
IPlatformInfo platformInfo,
|
|
||||||
IOsInfo osInfo,
|
|
||||||
IRouteCacheProvider routeCacheProvider,
|
|
||||||
IConfigFileProvider configFileProvider,
|
|
||||||
IMainDatabase database,
|
|
||||||
ILifecycleService lifecycleService) : base("system")
|
|
||||||
{
|
|
||||||
_appFolderInfo = appFolderInfo;
|
|
||||||
_runtimeInfo = runtimeInfo;
|
|
||||||
_platformInfo = platformInfo;
|
|
||||||
_osInfo = osInfo;
|
|
||||||
_routeCacheProvider = routeCacheProvider;
|
|
||||||
_configFileProvider = configFileProvider;
|
|
||||||
_database = database;
|
|
||||||
_lifecycleService = lifecycleService;
|
|
||||||
Get("/status", x => GetStatus());
|
|
||||||
Get("/routes", x => GetRoutes());
|
|
||||||
Post("/shutdown", x => Shutdown());
|
|
||||||
Post("/restart", x => Restart());
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetStatus()
|
|
||||||
{
|
|
||||||
return new
|
|
||||||
{
|
|
||||||
Version = BuildInfo.Version.ToString(),
|
|
||||||
BuildTime = BuildInfo.BuildDateTime,
|
|
||||||
IsDebug = BuildInfo.IsDebug,
|
|
||||||
IsProduction = RuntimeInfo.IsProduction,
|
|
||||||
IsAdmin = _runtimeInfo.IsAdmin,
|
|
||||||
IsUserInteractive = RuntimeInfo.IsUserInteractive,
|
|
||||||
StartupPath = _appFolderInfo.StartUpFolder,
|
|
||||||
AppData = _appFolderInfo.GetAppDataPath(),
|
|
||||||
OsName = _osInfo.Name,
|
|
||||||
OsVersion = _osInfo.Version,
|
|
||||||
IsMonoRuntime = PlatformInfo.IsMono,
|
|
||||||
IsMono = PlatformInfo.IsMono,
|
|
||||||
IsLinux = OsInfo.IsLinux,
|
|
||||||
IsOsx = OsInfo.IsOsx,
|
|
||||||
IsWindows = OsInfo.IsWindows,
|
|
||||||
Branch = _configFileProvider.Branch,
|
|
||||||
Authentication = _configFileProvider.AuthenticationMethod,
|
|
||||||
SqliteVersion = _database.Version,
|
|
||||||
UrlBase = _configFileProvider.UrlBase,
|
|
||||||
RuntimeVersion = _platformInfo.Version,
|
|
||||||
RuntimeName = PlatformInfo.Platform
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetRoutes()
|
|
||||||
{
|
|
||||||
return _routeCacheProvider.GetCache().Values;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object Shutdown()
|
|
||||||
{
|
|
||||||
_lifecycleService.Shutdown();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private object Restart()
|
|
||||||
{
|
|
||||||
_lifecycleService.Restart();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using NzbDrone.Core.Datastore.Events;
|
|
||||||
using NzbDrone.Core.Jobs;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Sonarr.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.System.Tasks
|
|
||||||
{
|
|
||||||
public class TaskModule : SonarrRestModuleWithSignalR<TaskResource, ScheduledTask>, IHandle<CommandExecutedEvent>
|
|
||||||
{
|
|
||||||
private readonly ITaskManager _taskManager;
|
|
||||||
|
|
||||||
private static readonly Regex NameRegex = new Regex("(?<!^)[A-Z]", RegexOptions.Compiled);
|
|
||||||
|
|
||||||
public TaskModule(ITaskManager taskManager, IBroadcastSignalRMessage broadcastSignalRMessage)
|
|
||||||
: base(broadcastSignalRMessage, "system/task")
|
|
||||||
{
|
|
||||||
_taskManager = taskManager;
|
|
||||||
GetResourceAll = GetAll;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TaskResource> GetAll()
|
|
||||||
{
|
|
||||||
return _taskManager.GetAll().Select(ConvertToResource).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TaskResource ConvertToResource(ScheduledTask scheduledTask)
|
|
||||||
{
|
|
||||||
var taskName = scheduledTask.TypeName.Split('.').Last().Replace("Command", "");
|
|
||||||
|
|
||||||
return new TaskResource
|
|
||||||
{
|
|
||||||
Id = scheduledTask.Id,
|
|
||||||
Name = NameRegex.Replace(taskName, match => " " + match.Value),
|
|
||||||
TaskName = taskName,
|
|
||||||
Interval = scheduledTask.Interval,
|
|
||||||
LastExecution = scheduledTask.LastExecution,
|
|
||||||
NextExecution = scheduledTask.LastExecution.AddMinutes(scheduledTask.Interval)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(CommandExecutedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Sync);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
using System;
|
|
||||||
using Sonarr.Http.REST;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.System.Tasks
|
|
||||||
{
|
|
||||||
public class TaskResource : RestResource
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string TaskName { get; set; }
|
|
||||||
public int Interval { get; set; }
|
|
||||||
public DateTime LastExecution { get; set; }
|
|
||||||
public DateTime NextExecution { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using NzbDrone.Core.Datastore.Events;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Core.Tags;
|
|
||||||
using NzbDrone.SignalR;
|
|
||||||
using Sonarr.Http;
|
|
||||||
using Sonarr.Http.Mapping;
|
|
||||||
|
|
||||||
namespace NzbDrone.Api.Tags
|
|
||||||
{
|
|
||||||
public class TagModule : SonarrRestModuleWithSignalR<TagResource, Tag>, IHandle<TagsUpdatedEvent>
|
|
||||||
{
|
|
||||||
private readonly ITagService _tagService;
|
|
||||||
|
|
||||||
public TagModule(IBroadcastSignalRMessage signalRBroadcaster,
|
|
||||||
ITagService tagService)
|
|
||||||
: base(signalRBroadcaster)
|
|
||||||
{
|
|
||||||
_tagService = tagService;
|
|
||||||
|
|
||||||
GetResourceById = GetTag;
|
|
||||||
GetResourceAll = GetAllTags;
|
|
||||||
CreateResource = CreateTag;
|
|
||||||
UpdateResource = UpdateTag;
|
|
||||||
DeleteResource = DeleteTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TagResource GetTag(int id)
|
|
||||||
{
|
|
||||||
return _tagService.GetTag(id).ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<TagResource> GetAllTags()
|
|
||||||
{
|
|
||||||
return _tagService.All().ToResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CreateTag(TagResource resource)
|
|
||||||
{
|
|
||||||
var model = resource.ToModel();
|
|
||||||
|
|
||||||
return _tagService.Add(model).Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateTag(TagResource resource)
|
|
||||||
{
|
|
||||||
var model = resource.ToModel();
|
|
||||||
|
|
||||||
_tagService.Update(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DeleteTag(int id)
|
|
||||||
{
|
|
||||||
_tagService.Delete(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(TagsUpdatedEvent message)
|
|
||||||
{
|
|
||||||
BroadcastResourceChange(ModelAction.Sync);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue