Remove Previous API

pull/94/head
Qstick 7 years ago
parent 0201aa812e
commit 8d64a987e0

@ -47,8 +47,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{1E6B3C
.nuget\NuGet.exe = .nuget\NuGet.exe
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Api", "NzbDrone.Api\NzbDrone.Api.csproj", "{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Host", "Host", "{486ADF86-DD89-4E19-B805-9D94F19800D9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Console", "NzbDrone.Console\NzbDrone.Console.csproj", "{3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}"
@ -185,12 +183,6 @@ Global
{F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Mono|x86.Build.0 = Release|x86
{F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Release|x86.ActiveCfg = Release|x86
{F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Release|x86.Build.0 = Release|x86
{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Debug|x86.ActiveCfg = Debug|x86
{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Debug|x86.Build.0 = Debug|x86
{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Mono|x86.ActiveCfg = Release|x86
{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Mono|x86.Build.0 = Release|x86
{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Release|x86.ActiveCfg = Release|x86
{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Release|x86.Build.0 = Release|x86
{3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Debug|x86.ActiveCfg = Debug|x86
{3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Debug|x86.Build.0 = Debug|x86
{3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Mono|x86.ActiveCfg = Debug|x86

@ -70,14 +70,14 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lidarr.Api.V3\Lidarr.Api.V3.csproj">
<Project>{7140ff1f-79be-492f-9188-b21a050bf708}</Project>
<Name>Lidarr.Api.V3</Name>
</ProjectReference>
<ProjectReference Include="..\Marr.Data\Marr.Data.csproj">
<Project>{F6FC6BE7-0847-4817-A1ED-223DC647C3D7}</Project>
<Name>Marr.Data</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.Api\NzbDrone.Api.csproj">
<Project>{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}</Project>
<Name>NzbDrone.Api</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.Common\NzbDrone.Common.csproj">
<Project>{F2BE0FDF-6E47-4827-A420-DD4EF82407F8}</Project>
<Name>NzbDrone.Common</Name>

@ -1,31 +0,0 @@
using Nancy;
using Lidarr.Http.Extensions;
using NzbDrone.Core.Music;
namespace NzbDrone.Api.AlbumPass
{
public class AlbumStudioModule : NzbDroneApiModule
{
private readonly IAlbumMonitoredService _albumMonitoredService;
public AlbumStudioModule(IAlbumMonitoredService albumMonitoredService)
: base("/albumstudio")
{
_albumMonitoredService = albumMonitoredService;
Post["/"] = artist => UpdateAll();
}
private Response UpdateAll()
{
//Read from request
var request = Request.Body.FromJson<AlbumStudioResource>();
foreach (var s in request.Artist)
{
_albumMonitoredService.SetAlbumMonitoredStatus(s, request.MonitoringOptions);
}
return "ok".AsResponse(HttpStatusCode.Accepted);
}
}
}

@ -1,11 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Core.Music;
namespace NzbDrone.Api.AlbumPass
{
public class AlbumStudioResource
{
public List<Core.Music.Artist> Artist { get; set; }
public MonitoringOptions MonitoringOptions { get; set; }
}
}

@ -1,42 +0,0 @@
using System.Collections.Generic;
using Lidarr.Http.REST;
using NzbDrone.Core.Music;
using NzbDrone.Core.ArtistStats;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.SignalR;
namespace NzbDrone.Api.Albums
{
public class AlbumModule : AlbumModuleWithSignalR
{
public AlbumModule(IArtistService artistService,
IArtistStatisticsService artistStatisticsService,
IAlbumService albumService,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(albumService, artistStatisticsService, artistService, qualityUpgradableSpecification, signalRBroadcaster)
{
GetResourceAll = GetAlbums;
UpdateResource = SetMonitored;
}
private List<AlbumResource> GetAlbums()
{
if (!Request.Query.ArtistId.HasValue)
{
throw new BadRequestException("artistId is missing");
}
var artistId = (int)Request.Query.ArtistId;
var resources = MapToResource(_albumService.GetAlbumsByArtist(artistId), false);
return resources;
}
private void SetMonitored(AlbumResource albumResource)
{
_albumService.SetAlbumMonitored(albumResource.Id, albumResource.Monitored);
}
}
}

@ -1,153 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Api.TrackFiles;
using NzbDrone.Api.Music;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Music;
using NzbDrone.Core.ArtistStats;
using NzbDrone.SignalR;
using Lidarr.Http;
namespace NzbDrone.Api.Albums
{
public abstract class AlbumModuleWithSignalR : LidarrRestModuleWithSignalR<AlbumResource, Album>
{
protected readonly IAlbumService _albumService;
protected readonly IArtistStatisticsService _artistStatisticsService;
protected readonly IArtistService _artistService;
protected readonly IUpgradableSpecification _qualityUpgradableSpecification;
protected AlbumModuleWithSignalR(IAlbumService albumService,
IArtistStatisticsService artistStatisticsService,
IArtistService artistService,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(signalRBroadcaster)
{
_albumService = albumService;
_artistStatisticsService = artistStatisticsService;
_artistService = artistService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetAlbum;
}
protected AlbumModuleWithSignalR(IAlbumService albumService,
IArtistStatisticsService artistStatisticsService,
IArtistService artistService,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster,
string resource)
: base(signalRBroadcaster, resource)
{
_albumService = albumService;
_artistStatisticsService = artistStatisticsService;
_artistService = artistService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetAlbum;
}
protected AlbumResource GetAlbum(int id)
{
var album = _albumService.GetAlbum(id);
var resource = MapToResource(album, true);
return resource;
}
protected AlbumResource MapToResource(Album album, bool includeArtist)
{
var resource = album.ToResource();
if (includeArtist)
{
var artist = album.Artist ?? _artistService.GetArtist(album.ArtistId);
if (includeArtist)
{
resource.Artist = artist.ToResource();
}
}
FetchAndLinkAlbumStatistics(resource);
return resource;
}
protected List<AlbumResource> MapToResource(List<Album> albums, bool includeArtist)
{
var result = albums.ToResource();
if (includeArtist)
{
var artistDict = new Dictionary<int, Core.Music.Artist>();
for (var i = 0; i < albums.Count; i++)
{
var album = albums[i];
var resource = result[i];
var artist = album.Artist ?? artistDict.GetValueOrDefault(albums[i].ArtistId) ?? _artistService.GetArtist(albums[i].ArtistId);
artistDict[artist.Id] = artist;
if (includeArtist)
{
resource.Artist = artist.ToResource();
}
}
}
for (var i = 0; i < albums.Count; i++)
{
var resource = result[i];
FetchAndLinkAlbumStatistics(resource);
}
return result;
}
private void FetchAndLinkAlbumStatistics(AlbumResource resource)
{
LinkArtistStatistics(resource, _artistStatisticsService.ArtistStatistics(resource.ArtistId));
}
private void LinkArtistStatistics(AlbumResource resource, ArtistStatistics artistStatistics)
{
if (artistStatistics.AlbumStatistics != null)
{
var dictAlbumStats = artistStatistics.AlbumStatistics.ToDictionary(v => v.AlbumId);
resource.Statistics = dictAlbumStats.GetValueOrDefault(resource.Id).ToResource();
}
}
//TODO: Implement Track or Album Grabbed/Dowloaded Events
//public void Handle(TrackGrabbedEvent message)
//{
// foreach (var track in message.Track.Tracks)
// {
// var resource = track.ToResource();
// resource.Grabbed = true;
// BroadcastResourceChange(ModelAction.Updated, resource);
// }
//}
//public void Handle(TrackDownloadedEvent message)
//{
// foreach (var album in message.Album.Albums)
// {
// BroadcastResourceChange(ModelAction.Updated, album.Id);
// }
//}
}
}

@ -1,87 +0,0 @@
using NzbDrone.Core.Music;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidarr.Http.REST;
using NzbDrone.Api.Music;
using NzbDrone.Core.MediaCover;
namespace NzbDrone.Api.Albums
{
public class AlbumResource : RestResource
{
public string Title { get; set; }
public int ArtistId { get; set; }
public List<string> Label { get; set; }
public bool Monitored { get; set; }
public string Path { get; set; }
public int ProfileId { get; set; }
public int Duration { get; set; }
public string AlbumType { get; set; }
public Ratings Ratings { get; set; }
public DateTime? ReleaseDate { get; set; }
public List<string> Genres { get; set; }
public ArtistResource Artist { get; set; }
public List<MediaCover> Images { get; set; }
public AlbumStatisticsResource Statistics { get; set; }
}
public static class AlbumResourceMapper
{
public static AlbumResource ToResource(this Core.Music.Album model)
{
if (model == null) return null;
return new AlbumResource
{
Id = model.Id,
ArtistId = model.ArtistId,
Label = model.Label,
Path = model.Path,
ProfileId = model.ProfileId,
Monitored = model.Monitored,
ReleaseDate = model.ReleaseDate,
Genres = model.Genres,
Title = model.Title,
Images = model.Images,
Ratings = model.Ratings,
Duration = model.Duration,
AlbumType = model.AlbumType
};
}
public static Album ToModel(this AlbumResource resource)
{
if (resource == null) return null;
return new Core.Music.Album
{
Id = resource.Id,
ArtistId = resource.ArtistId,
Label = resource.Label,
Path = resource.Path,
Monitored = resource.Monitored,
ProfileId = resource.ProfileId,
ReleaseDate = resource.ReleaseDate,
Genres = resource.Genres,
Title = resource.Title,
Images = resource.Images,
Ratings = resource.Ratings,
AlbumType = resource.AlbumType
};
}
public static List<AlbumResource> ToResource(this IEnumerable<Album> models)
{
return models.Select(ToResource).ToList();
}
public static List<Album> ToModel(this IEnumerable<AlbumResource> resources)
{
return resources?.Select(ToModel).ToList() ?? new List<Album>();
}
}
}

@ -1,45 +0,0 @@
using System;
using NzbDrone.Core.ArtistStats;
namespace NzbDrone.Api.Albums
{
public class AlbumStatisticsResource
{
public int TrackFileCount { get; set; }
public int TrackCount { get; set; }
public int TotalTrackCount { get; set; }
public long SizeOnDisk { get; set; }
public decimal PercentOfTracks
{
get
{
if (TrackCount == 0)
{
return 0;
}
return (decimal)TrackFileCount / (decimal)TrackCount * 100;
}
}
}
public static class AlbumStatisticsResourceMapper
{
public static AlbumStatisticsResource ToResource(this AlbumStatistics model)
{
if (model == null)
{
return null;
}
return new AlbumStatisticsResource
{
TrackFileCount = model.TrackFileCount,
TrackCount = model.TrackCount,
TotalTrackCount = model.TotalTrackCount,
SizeOnDisk = model.SizeOnDisk
};
}
}
}

@ -1,30 +0,0 @@
using NzbDrone.Core.Blacklisting;
using NzbDrone.Core.Datastore;
using Lidarr.Http;
namespace NzbDrone.Api.Blacklist
{
public class BlacklistModule : LidarrRestModule<BlacklistResource>
{
private readonly IBlacklistService _blacklistService;
public BlacklistModule(IBlacklistService blacklistService)
{
_blacklistService = blacklistService;
GetResourcePaged = GetBlacklist;
DeleteResource = DeleteBlacklist;
}
private PagingResource<BlacklistResource> GetBlacklist(PagingResource<BlacklistResource> pagingResource)
{
var pagingSpec = pagingResource.MapToPagingSpec<BlacklistResource, Core.Blacklisting.Blacklist>("id", SortDirection.Ascending);
return ApplyToPage(_blacklistService.Paged, pagingSpec, BlacklistResourceMapper.MapToResource);
}
private void DeleteBlacklist(int id)
{
_blacklistService.Delete(id);
}
}
}

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using Lidarr.Http.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Api.Music;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Languages;
namespace NzbDrone.Api.Blacklist
{
public class BlacklistResource : RestResource
{
public int ArtistId { get; set; }
public List<int> AlbumIds { 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 ArtistResource Artist { get; set; }
}
public static class BlacklistResourceMapper
{
public static BlacklistResource MapToResource(this Core.Blacklisting.Blacklist model)
{
if (model == null) return null;
return new BlacklistResource
{
Id = model.Id,
ArtistId = model.ArtistId,
AlbumIds = model.AlbumIds,
SourceTitle = model.SourceTitle,
Quality = model.Quality,
Date = model.Date,
Protocol = model.Protocol,
Indexer = model.Indexer,
Message = model.Message,
Artist = model.Artist.ToResource()
};
}
}
}

@ -1,130 +0,0 @@
using Nancy;
using System;
using System.Collections.Generic;
using System.Linq;
using Ical.Net;
using Ical.Net.DataTypes;
using Ical.Net.Interfaces.Serialization;
using Ical.Net.Serialization;
using Ical.Net.Serialization.iCalendar.Factory;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Music;
using Nancy.Responses;
using NzbDrone.Core.Tags;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Api.Calendar
{
public class CalendarFeedModule : NzbDroneFeedModule
{
private readonly IAlbumService _albumService;
private readonly ITagService _tagService;
public CalendarFeedModule(IAlbumService albumService, ITagService tagService)
: base("calendar")
{
_albumService = albumService;
_tagService = tagService;
Get["/NzbDrone.ics"] = options => GetCalendarFeed();
Get["/Lidarr.ics"] = options => GetCalendarFeed();
}
private Response GetCalendarFeed()
{
var pastDays = 7;
var futureDays = 28;
var start = DateTime.Today.AddDays(-pastDays);
var end = DateTime.Today.AddDays(futureDays);
var unmonitored = false;
var premiersOnly = 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 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 (queryPremiersOnly.HasValue)
{
premiersOnly = 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 albums = _albumService.AlbumsBetweenDates(start, end, unmonitored);
var calendar = new Ical.Net.Calendar
{
// This will need to point to the hosted web site
// TODO
ProductId = "-//lidarr.audio//Lidarr//EN"
};
foreach (var album in albums.OrderBy(v => v.ReleaseDate))
{
//if (premiersOnly && (album.SeasonNumber == 0 || album.EpisodeNumber != 1))
//{
// continue;
//}
if (tags.Any() && tags.None(album.Artist.Tags.Contains))
{
continue;
}
var occurrence = calendar.Create<Event>();
occurrence.Uid = "NzbDrone_album_" + album.Id;
//occurrence.Status = album.HasFile ? EventStatus.Confirmed : EventStatus.Tentative;
//occurrence.Description = album.Overview;
//occurrence.Categories = new List<string>() { album.Artist. };
occurrence.Start = new CalDateTime(album.ReleaseDate.Value) { HasTime = false };
occurrence.Summary =$"{album.Artist.Name} - {album.Title}";
}
var serializer = (IStringSerializer) new SerializerFactory().Build(calendar.GetType(), new SerializationContext());
var icalendar = serializer.SerializeToString(calendar);
return new TextResponse(icalendar, "text/calendar");
}
}
}

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.Episodes;
using NzbDrone.Api.Albums;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Music;
using NzbDrone.Core.ArtistStats;
using NzbDrone.SignalR;
namespace NzbDrone.Api.Calendar
{
public class CalendarModule : AlbumModuleWithSignalR
{
public CalendarModule(IAlbumService albumService,
IArtistStatisticsService artistStatisticsService,
IArtistService artistService,
IUpgradableSpecification upgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(albumService, artistStatisticsService, artistService, upgradableSpecification, signalRBroadcaster, "calendar")
{
GetResourceAll = GetCalendar;
}
private List<AlbumResource> 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(_albumService.AlbumsBetweenDates(start, end, includeUnmonitored), true);
return resources.OrderBy(e => e.ReleaseDate).ToList();
}
}
}

@ -1,68 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Lidarr.Http.Extensions;
using NzbDrone.Common;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ProgressMessaging;
using NzbDrone.SignalR;
using Lidarr.Http;
using Lidarr.Http.Mapping;
using Lidarr.Http.Validation;
namespace NzbDrone.Api.Commands
{
public class CommandModule : LidarrRestModuleWithSignalR<CommandResource, CommandModel>, IHandle<CommandUpdatedEvent>
{
private readonly IManageCommandQueue _commandQueueManager;
private readonly IServiceFactory _serviceFactory;
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();
}
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)
{
BroadcastResourceChange(ModelAction.Updated, message.Command.ToResource());
}
}
}
}

@ -1,130 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Lidarr.Http.REST;
using NzbDrone.Core.Messaging.Commands;
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; }
[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,
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,33 +0,0 @@
using Lidarr.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 RemoveCompletedDownloads { get; set; }
public bool AutoRedownloadFailed { get; set; }
public bool RemoveFailedDownloads { get; set; }
}
public static class DownloadClientConfigResourceMapper
{
public static DownloadClientConfigResource ToResource(IConfigService model)
{
return new DownloadClientConfigResource
{
DownloadClientWorkingFolders = model.DownloadClientWorkingFolders,
EnableCompletedDownloadHandling = model.EnableCompletedDownloadHandling,
RemoveCompletedDownloads = model.RemoveCompletedDownloads,
AutoRedownloadFailed = model.AutoRedownloadFailed,
RemoveFailedDownloads = model.RemoveFailedDownloads
};
}
}
}

@ -1,86 +0,0 @@
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 Lidarr.Http;
namespace NzbDrone.Api.Config
{
public class HostConfigModule : LidarrRestModule<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);
}
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,73 +0,0 @@
using Lidarr.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 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 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,
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
};
}
}
}

@ -1,28 +0,0 @@
using FluentValidation;
using Lidarr.Http.Validation;
using NzbDrone.Core.Configuration;
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.Retention)
.GreaterThanOrEqualTo(0);
SharedValidator.RuleFor(c => c.RssSyncInterval)
.IsValidRssSyncInterval();
}
protected override IndexerConfigResource ToResource(IConfigService model)
{
return IndexerConfigResourceMapper.ToResource(model);
}
}
}

@ -1,25 +0,0 @@
using Lidarr.Http.REST;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Config
{
public class IndexerConfigResource : RestResource
{
public int MinimumAge { 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,
Retention = model.Retention,
RssSyncInterval = model.RssSyncInterval,
};
}
}
}

@ -1,22 +0,0 @@
using FluentValidation;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Validation.Paths;
namespace NzbDrone.Api.Config
{
public class MediaManagementConfigModule : NzbDroneConfigModule<MediaManagementConfigResource>
{
public MediaManagementConfigModule(IConfigService configService, PathExistsValidator pathExistsValidator)
: base(configService)
{
SharedValidator.RuleFor(c => c.FileChmod).NotEmpty();
SharedValidator.RuleFor(c => c.FolderChmod).NotEmpty();
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,54 +0,0 @@
using Lidarr.Http.REST;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles;
namespace NzbDrone.Api.Config
{
public class MediaManagementConfigResource : RestResource
{
public bool AutoUnmonitorPreviouslyDownloadedTracks { get; set; }
public string RecycleBin { get; set; }
public bool AutoDownloadPropers { get; set; }
public bool CreateEmptyArtistFolders { get; set; }
public FileDateType FileDate { get; set; }
public bool SetPermissionsLinux { get; set; }
public string FileChmod { get; set; }
public string FolderChmod { get; set; }
public string ChownUser { 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
{
AutoUnmonitorPreviouslyDownloadedTracks = model.AutoUnmonitorPreviouslyDownloadedTracks,
RecycleBin = model.RecycleBin,
AutoDownloadPropers = model.AutoDownloadPropers,
CreateEmptyArtistFolders = model.CreateEmptyArtistFolders,
FileDate = model.FileDate,
SetPermissionsLinux = model.SetPermissionsLinux,
FileChmod = model.FileChmod,
FolderChmod = model.FolderChmod,
ChownUser = model.ChownUser,
ChownGroup = model.ChownGroup,
SkipFreeSpaceCheckWhenImporting = model.SkipFreeSpaceCheckWhenImporting,
CopyUsingHardlinks = model.CopyUsingHardlinks,
ImportExtraFiles = model.ImportExtraFiles,
ExtraFileExtensions = model.ExtraFileExtensions,
EnableMediaInfo = model.EnableMediaInfo
};
}
}
}

@ -1,112 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using FluentValidation.Results;
using Nancy.Responses;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Organizer;
using Nancy.ModelBinding;
using Lidarr.Http.Extensions;
using Lidarr.Http;
using Lidarr.Http.Mapping;
namespace NzbDrone.Api.Config
{
public class NamingConfigModule : LidarrRestModule<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.StandardTrackFormat).ValidTrackFormat();
SharedValidator.RuleFor(c => c.ArtistFolderFormat).ValidArtistFolderFormat();
SharedValidator.RuleFor(c => c.AlbumFolderFormat).ValidAlbumFolderFormat();
}
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.StandardTrackFormat.IsNotNullOrWhiteSpace())
{
var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec);
basicConfig.AddToResource(resource);
}
return resource;
}
private NamingConfigResource GetNamingConfig(int id)
{
return GetNamingConfig();
}
private JsonResponse<NamingSampleResource> GetExamples(NamingConfigResource config)
{
var nameSpec = config.ToModel();
var sampleResource = new NamingSampleResource();
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
sampleResource.SingleTrackExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null
? "Invalid format"
: singleTrackSampleResult.FileName;
sampleResource.ArtistFolderExample = nameSpec.ArtistFolderFormat.IsNullOrWhiteSpace()
? "Invalid format"
: _filenameSampleService.GetArtistFolderSample(nameSpec);
sampleResource.AlbumFolderExample = nameSpec.AlbumFolderFormat.IsNullOrWhiteSpace()
? "Invalid format"
: _filenameSampleService.GetAlbumFolderSample(nameSpec);
return sampleResource.AsResponse();
}
private void ValidateFormatResult(NamingConfig nameSpec)
{
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
var singleTrackValidationResult = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult);
var validationFailures = new List<ValidationFailure>();
validationFailures.AddIfNotNull(singleTrackValidationResult);
if (validationFailures.Any())
{
throw new ValidationException(validationFailures.DistinctBy(v => v.PropertyName).ToArray());
}
}
}
}

@ -1,68 +0,0 @@
using Lidarr.Http.REST;
using NzbDrone.Core.Organizer;
namespace NzbDrone.Api.Config
{
public class NamingConfigResource : RestResource
{
public bool RenameTracks { get; set; }
public bool ReplaceIllegalCharacters { get; set; }
public int MultiEpisodeStyle { get; set; }
public string StandardTrackFormat { get; set; }
public string ArtistFolderFormat { get; set; }
public string AlbumFolderFormat { get; set; }
public bool IncludeArtistName { get; set; }
public bool IncludeAlbumTitle { 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,
RenameTracks = model.RenameTracks,
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
StandardTrackFormat = model.StandardTrackFormat,
ArtistFolderFormat = model.ArtistFolderFormat,
AlbumFolderFormat = model.AlbumFolderFormat
//IncludeSeriesTitle
//IncludeEpisodeTitle
//IncludeQuality
//ReplaceSpaces
//Separator
//NumberStyle
};
}
public static void AddToResource(this BasicNamingConfig basicNamingConfig, NamingConfigResource resource)
{
resource.IncludeArtistName = basicNamingConfig.IncludeArtistName;
resource.IncludeAlbumTitle = basicNamingConfig.IncludeAlbumTitle;
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,
RenameTracks = resource.RenameTracks,
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
StandardTrackFormat = resource.StandardTrackFormat,
ArtistFolderFormat = resource.ArtistFolderFormat,
AlbumFolderFormat = resource.AlbumFolderFormat
};
}
}
}

@ -1,16 +0,0 @@
namespace NzbDrone.Api.Config
{
public class NamingSampleResource
{
public string SingleEpisodeExample { get; set; }
public string SingleTrackExample { 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 ArtistFolderExample { get; set; }
public string AlbumFolderExample { get; set; }
}
}

@ -1,52 +0,0 @@
using System.Linq;
using System.Reflection;
using Lidarr.Http.REST;
using NzbDrone.Core.Configuration;
using Lidarr.Http;
namespace NzbDrone.Api.Config
{
public abstract class NzbDroneConfigModule<TResource> : LidarrRestModule<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 Lidarr.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 Lidarr.Http;
namespace NzbDrone.Api.DiskSpace
{
public class DiskSpaceModule : LidarrRestModule<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 Lidarr.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;
}
protected override void MapToModel(DownloadClientDefinition definition, DownloadClientResource resource)
{
base.MapToModel(definition, resource);
definition.Enable = resource.Enable;
definition.Protocol = resource.Protocol;
}
protected override void Validate(DownloadClientDefinition definition, bool includeWarnings)
{
if (!definition.Enable) return;
base.Validate(definition, includeWarnings);
}
}
}

@ -1,10 +0,0 @@
using NzbDrone.Core.Indexers;
namespace NzbDrone.Api.DownloadClient
{
public class DownloadClientResource : ProviderResource
{
public bool Enable { get; set; }
public DownloadProtocol Protocol { get; set; }
}
}

@ -1,98 +0,0 @@
using System.Collections.Generic;
using System.IO;
using NLog;
using Lidarr.Http;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
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.SignalR;
using System;
namespace NzbDrone.Api.EpisodeFiles
{
public class EpisodeFileModule : LidarrRestModuleWithSignalR<EpisodeFileResource, EpisodeFile>,
IHandle<EpisodeFileAddedEvent>
{
private readonly IMediaFileService _mediaFileService;
private readonly IDiskProvider _diskProvider;
private readonly IRecycleBinProvider _recycleBinProvider;
private readonly ISeriesService _seriesService;
private readonly IUpgradableSpecification _upgradableSpecification;
private readonly Logger _logger;
public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster,
IMediaFileService mediaFileService,
IDiskProvider diskProvider,
IRecycleBinProvider recycleBinProvider,
ISeriesService seriesService,
IUpgradableSpecification upgradableSpecification,
Logger logger)
: base(signalRBroadcaster)
{
_mediaFileService = mediaFileService;
_diskProvider = diskProvider;
_recycleBinProvider = recycleBinProvider;
_seriesService = seriesService;
_upgradableSpecification = upgradableSpecification;
_logger = logger;
GetResourceById = GetEpisodeFile;
GetResourceAll = GetEpisodeFiles;
UpdateResource = SetQuality;
DeleteResource = DeleteEpisodeFile;
}
private EpisodeFileResource GetEpisodeFile(int id)
{
throw new NotImplementedException();
//var episodeFile = _mediaFileService.Get(id);
//var series = _seriesService.GetSeries(episodeFile.SeriesId);
//return episodeFile.ToResource(series, _qualityUpgradableSpecification);
}
private List<EpisodeFileResource> GetEpisodeFiles()
{
throw new NotImplementedException();
//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, _qualityUpgradableSpecification));
}
private void SetQuality(EpisodeFileResource episodeFileResource)
{
var episodeFile = _mediaFileService.Get(episodeFileResource.Id);
episodeFile.Quality = episodeFileResource.Quality;
_mediaFileService.Update(episodeFile);
}
private void DeleteEpisodeFile(int id)
{
throw new NotImplementedException();
//var episodeFile = _mediaFileService.Get(id);
//var series = _seriesService.GetSeries(episodeFile.SeriesId);
//var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
//var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(fullPath));
//_logger.Info("Deleting episode file: {0}", fullPath);
//_recycleBinProvider.DeleteFile(fullPath, subfolder);
//_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
}
public void Handle(EpisodeFileAddedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id);
}
}
}

@ -1,69 +0,0 @@
using System;
using System.IO;
using Lidarr.Http.REST;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.MediaFiles;
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 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,
//QualityCutoffNotMet
};
}
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.CutoffNotMet(series.Profile.Value, series.LanguageProfile.Value, model.Quality, model.Language)
};
}
}
}

@ -1,40 +0,0 @@
using System.Collections.Generic;
using Lidarr.Http.REST;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.SignalR;
namespace NzbDrone.Api.Episodes
{
public class EpisodeModule : EpisodeModuleWithSignalR
{
public EpisodeModule(ISeriesService seriesService,
IEpisodeService episodeService,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(episodeService, seriesService, qualityUpgradableSpecification, 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,127 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Common.Extensions;
using NzbDrone.Api.EpisodeFiles;
using NzbDrone.Api.Series;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.SignalR;
using Lidarr.Http;
namespace NzbDrone.Api.Episodes
{
public abstract class EpisodeModuleWithSignalR : LidarrRestModuleWithSignalR<EpisodeResource, Episode>,
IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeDownloadedEvent>
{
protected readonly IEpisodeService _episodeService;
protected readonly ISeriesService _seriesService;
protected readonly IUpgradableSpecification _qualityUpgradableSpecification;
protected EpisodeModuleWithSignalR(IEpisodeService episodeService,
ISeriesService seriesService,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster)
: base(signalRBroadcaster)
{
_episodeService = episodeService;
_seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
GetResourceById = GetEpisode;
}
protected EpisodeModuleWithSignalR(IEpisodeService episodeService,
ISeriesService seriesService,
IUpgradableSpecification qualityUpgradableSpecification,
IBroadcastSignalRMessage signalRBroadcaster,
string resource)
: base(signalRBroadcaster, resource)
{
_episodeService = episodeService;
_seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
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, _qualityUpgradableSpecification);
}
}
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, _qualityUpgradableSpecification);
}
}
}
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(EpisodeDownloadedEvent message)
{
foreach (var episode in message.Episode.Episodes)
{
BroadcastResourceChange(ModelAction.Updated, episode.Id);
}
}
}
}

@ -1,78 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NzbDrone.Api.EpisodeFiles;
using Lidarr.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; }
//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(),
};
}
public static List<EpisodeResource> ToResource(this IEnumerable<Episode> models)
{
if (models == null) return null;
return models.Select(ToResource).ToList();
}
}
}

@ -1,76 +0,0 @@
using System;
using System.IO;
using System.Linq;
using Nancy;
using Lidarr.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 Response GetContents()
{
var pathQuery = Request.Query.path;
var includeFilesQuery = Request.Query.includeFiles;
bool includeFiles = false;
if (includeFilesQuery.HasValue)
{
includeFiles = Convert.ToBoolean(includeFilesQuery.Value);
}
return _fileSystemLookupService.LookupContents((string)pathQuery.Value, includeFiles).AsResponse();
}
private Response GetEntityType()
{
var pathQuery = Request.Query.path;
var path = (string)pathQuery.Value;
if (_diskProvider.FileExists(path))
{
return new { type = "file" }.AsResponse();
}
//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" }.AsResponse();
}
private Response GetMediaFiles()
{
var pathQuery = Request.Query.path;
var path = (string)pathQuery.Value;
if (!_diskProvider.FolderExists(path))
{
return new string[0].AsResponse();
}
return _diskScanService.GetAudioFiles(path).Select(f => new {
Path = f,
RelativePath = path.GetRelativePath(f),
Name = Path.GetFileName(f)
}).AsResponse();
}
}
}

@ -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 Lidarr.Http;
namespace NzbDrone.Api.Health
{
public class HealthModule : LidarrRestModuleWithSignalR<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 Lidarr.Http.REST;
using NzbDrone.Common.Http;
using NzbDrone.Core.HealthCheck;
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,80 +0,0 @@
using System;
using Nancy;
using NzbDrone.Api.Episodes;
using NzbDrone.Api.Albums;
using Lidarr.Http.Extensions;
using NzbDrone.Api.Series;
using NzbDrone.Api.Music;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.History;
using Lidarr.Http;
namespace NzbDrone.Api.History
{
public class HistoryModule : LidarrRestModule<HistoryResource>
{
private readonly IHistoryService _historyService;
private readonly IUpgradableSpecification _upgradableSpecification;
private readonly IFailedDownloadService _failedDownloadService;
public HistoryModule(IHistoryService historyService,
IUpgradableSpecification qualityUpgradableSpecification,
IFailedDownloadService failedDownloadService)
{
_historyService = historyService;
_upgradableSpecification = qualityUpgradableSpecification;
_failedDownloadService = failedDownloadService;
GetResourcePaged = GetHistory;
Post["/failed"] = x => MarkAsFailed();
}
protected HistoryResource MapToResource(Core.History.History model)
{
var resource = model.ToResource();
resource.Artist = model.Artist.ToResource();
resource.Album = model.Album.ToResource();
if (model.Artist != null)
{
resource.QualityCutoffNotMet = _upgradableSpecification.CutoffNotMet(model.Artist.Profile.Value,
model.Artist.LanguageProfile,
model.Quality,
model.Language);
}
return resource;
}
private PagingResource<HistoryResource> GetHistory(PagingResource<HistoryResource> pagingResource)
{
var albumId = Request.Query.AlbumId;
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, Core.History.History>("date", SortDirection.Descending);
if (pagingResource.FilterKey == "eventType")
{
var filterValue = (HistoryEventType)Convert.ToInt32(pagingResource.FilterValue);
pagingSpec.FilterExpression = v => v.EventType == filterValue;
}
if (albumId.HasValue)
{
int i = (int)albumId;
pagingSpec.FilterExpression = h => h.AlbumId == i;
}
return ApplyToPage(_historyService.Paged, pagingSpec, MapToResource);
}
private Response MarkAsFailed()
{
var id = (int)Request.Form.Id;
_failedDownloadService.MarkAsFailed(id);
return new object().AsResponse();
}
}
}

@ -1,60 +0,0 @@
using System;
using System.Collections.Generic;
using NzbDrone.Api.Episodes;
using NzbDrone.Api.Albums;
using Lidarr.Http.REST;
using NzbDrone.Api.Series;
using NzbDrone.Api.Music;
using NzbDrone.Core.History;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Languages;
namespace NzbDrone.Api.History
{
public class HistoryResource : RestResource
{
public int ArtistId { get; set; }
public int AlbumId { 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 HistoryEventType EventType { get; set; }
public Dictionary<string, string> Data { get; set; }
public AlbumResource Album { get; set; }
public ArtistResource Artist { get; set; }
}
public static class HistoryResourceMapper
{
public static HistoryResource ToResource(this Core.History.History model)
{
if (model == null) return null;
return new HistoryResource
{
Id = model.Id,
AlbumId = model.AlbumId,
ArtistId = model.ArtistId,
SourceTitle = model.SourceTitle,
Quality = model.Quality,
//QualityCutoffNotMet
Date = model.Date,
DownloadId = model.DownloadId,
EventType = model.EventType,
Data = model.Data
//Episode
//Series
};
}
}
}

@ -1,37 +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.EnableSearch;
resource.SupportsRss = definition.SupportsRss;
resource.SupportsSearch = definition.SupportsSearch;
resource.Protocol = definition.Protocol;
}
protected override void MapToModel(IndexerDefinition definition, IndexerResource resource)
{
base.MapToModel(definition, resource);
definition.EnableRss = resource.EnableRss;
definition.EnableSearch = resource.EnableSearch;
}
protected override void Validate(IndexerDefinition definition, bool includeWarnings)
{
if (!definition.Enable) return;
base.Validate(definition, includeWarnings);
}
}
}

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

@ -1,120 +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 Lidarr.Http.Extensions;
using NzbDrone.Common.Cache;
using HttpStatusCode = System.Net.HttpStatusCode;
namespace NzbDrone.Api.Indexers
{
public class ReleaseModule : ReleaseModuleBase
{
private readonly IFetchAndParseRss _rssFetcherAndParser;
private readonly ISearchForNzb _nzbSearchService;
private readonly IMakeDownloadDecision _downloadDecisionMaker;
private readonly IPrioritizeDownloadDecision _prioritizeDownloadDecision;
private readonly IDownloadService _downloadService;
private readonly Logger _logger;
private readonly ICached<RemoteAlbum> _remoteAlbumCache;
public ReleaseModule(IFetchAndParseRss rssFetcherAndParser,
ISearchForNzb nzbSearchService,
IMakeDownloadDecision downloadDecisionMaker,
IPrioritizeDownloadDecision prioritizeDownloadDecision,
IDownloadService downloadService,
ICacheManager cacheManager,
Logger logger)
{
_rssFetcherAndParser = rssFetcherAndParser;
_nzbSearchService = nzbSearchService;
_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();
_remoteAlbumCache = cacheManager.GetCache<RemoteAlbum>(GetType(), "remoteAlbums");
}
private Response DownloadRelease(ReleaseResource release)
{
var remoteAlbum = _remoteAlbumCache.Find(release.Guid);
if (remoteAlbum == null)
{
_logger.Debug("Couldn't find requested release in cache, cache timeout probably expired.");
return new NotFoundResponse();
}
try
{
_downloadService.DownloadReport(remoteAlbum);
}
catch (ReleaseDownloadException ex)
{
_logger.Error(ex);
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
}
return release.AsResponse();
}
private List<ReleaseResource> GetReleases()
{
if (Request.Query.albumId != null)
{
return GetAlbumReleases(Request.Query.albumId);
}
return GetRss();
}
private List<ReleaseResource> GetAlbumReleases(int albumId)
{
try
{
var decisions = _nzbSearchService.AlbumSearch(albumId, true, true);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
return MapDecisions(prioritizedDecisions);
}
catch (Exception ex)
{
_logger.Error(ex, "Album 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)
{
_remoteAlbumCache.Set(decision.RemoteAlbum.Release.Guid, decision.RemoteAlbum, TimeSpan.FromMinutes(30));
return base.MapDecision(decision, initialWeight);
}
}
}

@ -1,42 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Core.DecisionEngine;
using Lidarr.Http;
namespace NzbDrone.Api.Indexers
{
public abstract class ReleaseModuleBase : LidarrRestModule<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.RemoteAlbum.Artist != null)
{
release.QualityWeight = decision.RemoteAlbum.Artist
.Profile.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,50 +0,0 @@
using Nancy;
using Nancy.ModelBinding;
using FluentValidation;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Parser.Model;
using Lidarr.Http.Extensions;
using NLog;
namespace NzbDrone.Api.Indexers
{
class ReleasePushModule : ReleaseModuleBase
{
private readonly IMakeDownloadDecision _downloadDecisionMaker;
private readonly IProcessDownloadDecisions _downloadDecisionProcessor;
private readonly Logger _logger;
public ReleasePushModule(IMakeDownloadDecision downloadDecisionMaker,
IProcessDownloadDecisions downloadDecisionProcessor,
Logger logger)
{
_downloadDecisionMaker = downloadDecisionMaker;
_downloadDecisionProcessor = downloadDecisionProcessor;
_logger = logger;
Post["/push"] = x => ProcessRelease(this.Bind<ReleaseResource>());
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 Response ProcessRelease(ReleaseResource release)
{
_logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl);
var info = release.ToModel();
info.Guid = "PUSH-" + info.DownloadUrl;
var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
_downloadDecisionProcessor.ProcessDecisions(decisions);
return MapDecisions(decisions).First().AsResponse();
}
}
}

@ -1,158 +0,0 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Lidarr.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 Language Language { get; set; }
public string ReleaseDate { get; set; }
public string ArtistName { get; set; }
public string AlbumTitle { get; set; }
public bool Approved { get; set; }
public bool TemporarilyRejected { get; set; }
public bool Rejected { 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; }
// TODO: Remove in v3
// Used to support the original Release Push implementation
// 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.RemoteAlbum.Release;
var parsedAlbumInfo = model.RemoteAlbum.ParsedAlbumInfo;
var remoteAlbum = model.RemoteAlbum;
var torrentInfo = (model.RemoteAlbum.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 = parsedAlbumInfo.Quality,
//QualityWeight
Age = releaseInfo.Age,
AgeHours = releaseInfo.AgeHours,
AgeMinutes = releaseInfo.AgeMinutes,
Size = releaseInfo.Size,
IndexerId = releaseInfo.IndexerId,
Indexer = releaseInfo.Indexer,
ReleaseGroup = parsedAlbumInfo.ReleaseGroup,
ReleaseHash = parsedAlbumInfo.ReleaseHash,
Title = releaseInfo.Title,
Language = parsedAlbumInfo.Language,
ReleaseDate = parsedAlbumInfo.ReleaseDate,
ArtistName = parsedAlbumInfo.ArtistName,
AlbumTitle = parsedAlbumInfo.AlbumTitle,
Approved = model.Approved,
TemporarilyRejected = model.TemporarilyRejected,
Rejected = model.Rejected,
Rejections = model.Rejections.Select(r => r.Reason).ToList(),
PublishDate = releaseInfo.PublishDate,
CommentUrl = releaseInfo.CommentUrl,
DownloadUrl = releaseInfo.DownloadUrl,
InfoUrl = releaseInfo.InfoUrl,
DownloadAllowed = remoteAlbum.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,
};
}
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.PublishDate = resource.PublishDate;
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,72 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NzbDrone.Common.Disk;
using Nancy;
using Nancy.Responses;
using NzbDrone.Core.Configuration;
using Lidarr.Http;
namespace NzbDrone.Api.Logs
{
public abstract class LogFileModuleBase : LidarrRestModule<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 Response GetLogFileResponse(string filename)
{
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 Lidarr.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,54 +0,0 @@
using NzbDrone.Core.Instrumentation;
using Lidarr.Http;
using Lidarr.Http.Mapping;
namespace NzbDrone.Api.Logs
{
public class LogModule : LidarrRestModule<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";
}
if (pagingResource.FilterKey == "level")
{
switch (pagingResource.FilterValue)
{
case "Fatal":
pageSpec.FilterExpression = h => h.Level == "Fatal";
break;
case "Error":
pageSpec.FilterExpression = h => h.Level == "Fatal" || h.Level == "Error";
break;
case "Warn":
pageSpec.FilterExpression = h => h.Level == "Fatal" || h.Level == "Error" || h.Level == "Warn";
break;
case "Info":
pageSpec.FilterExpression = h => h.Level == "Fatal" || h.Level == "Error" || h.Level == "Warn" || h.Level == "Info";
break;
case "Debug":
pageSpec.FilterExpression = h => h.Level == "Fatal" || h.Level == "Error" || h.Level == "Warn" || h.Level == "Info" || h.Level == "Debug";
break;
case "Trace":
pageSpec.FilterExpression = 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 Lidarr.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,44 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.MediaFiles.TrackImport.Manual;
using NzbDrone.Core.Qualities;
using Lidarr.Http;
namespace NzbDrone.Api.ManualImport
{
public class ManualImportModule : LidarrRestModule<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;
return _manualImportService.GetMediaFiles(folder, downloadId).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,50 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.Episodes;
using Lidarr.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 Name { get; set; }
public long Size { 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.TrackImport.Manual.ManualImportItem model)
{
if (model == null) return null;
return new ManualImportResource
{
Id = HashConverter.GetHashInt31(model.Path),
Path = model.Path,
RelativePath = model.RelativePath,
Name = model.Name,
Size = model.Size,
Quality = model.Quality,
//QualityWeight
DownloadId = model.DownloadId,
Rejections = model.Rejections
};
}
public static List<ManualImportResource> ToResource(this IEnumerable<Core.MediaFiles.TrackImport.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 Response 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,32 +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;
}
protected override void Validate(MetadataDefinition definition, bool includeWarnings)
{
if (!definition.Enable) return;
base.Validate(definition, includeWarnings);
}
}
}

@ -1,7 +0,0 @@
namespace NzbDrone.Api.Metadata
{
public class MetadataResource : ProviderResource
{
public bool Enable { get; set; }
}
}

@ -1,153 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using Nancy;
using Lidarr.Http.REST;
using Lidarr.Http.Extensions;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Parser;
using System.Linq;
using System;
using Marr.Data;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.TrackImport;
using NzbDrone.Core.RootFolders;
using NzbDrone.Common.Cache;
using NzbDrone.Core.Music;
using Lidarr.Http;
namespace NzbDrone.Api.Music
{
public class UnmappedComparer : IComparer<UnmappedFolder>
{
public int Compare(UnmappedFolder a, UnmappedFolder b)
{
return a.Name.CompareTo(b.Name);
}
}
public class MusicBulkImportModule : LidarrRestModule<ArtistResource>
{
private readonly ISearchForNewArtist _searchProxy;
private readonly IRootFolderService _rootFolderService;
private readonly IMakeImportDecision _importDecisionMaker;
private readonly IDiskScanService _diskScanService;
private readonly ICached<Core.Music.Artist> _mappedArtists;
private readonly IArtistService _artistService;
public MusicBulkImportModule(ISearchForNewArtist searchProxy,
IRootFolderService rootFolderService,
IMakeImportDecision importDecisionMaker,
IDiskScanService diskScanService,
ICacheManager cacheManager,
IArtistService artistService
)
: base("/artist/bulkimport")
{
_searchProxy = searchProxy;
_rootFolderService = rootFolderService;
_importDecisionMaker = importDecisionMaker;
_diskScanService = diskScanService;
_mappedArtists = cacheManager.GetCache<Artist>(GetType(), "mappedArtistsCache");
_artistService = artistService;
Get["/"] = x => Search();
}
private Response Search()
{
if (Request.Query.Id == 0)
{
throw new BadRequestException("Invalid Query");
}
RootFolder rootFolder = _rootFolderService.Get(Request.Query.Id);
var unmapped = rootFolder.UnmappedFolders.OrderBy(f => f.Name).ToList();
var paged = unmapped;
var mapped = paged.Select(page =>
{
Artist m = null;
var mappedArtist = _mappedArtists.Find(page.Name);
if (mappedArtist != null)
{
return mappedArtist;
}
var files = _diskScanService.GetAudioFiles(page.Path);
// Check for music files in directory
if (files.Count() == 0)
{
return null;
}
var parsedTitle = Parser.ParseMusicPath(files.FirstOrDefault());
if (parsedTitle == null || parsedTitle.ArtistTitle == null)
{
m = new Artist
{
Name = page.Name.Replace(".", " ").Replace("-", " "),
Path = page.Path,
};
}
else
{
m = new Artist
{
Name = parsedTitle.ArtistTitle,
Path = page.Path
};
}
var searchResults = _searchProxy.SearchForNewArtist(m.Name);
if (searchResults == null || searchResults.Count == 0)
{
return null;
};
mappedArtist = searchResults.First();
if (mappedArtist != null)
{
mappedArtist.Monitored = true;
mappedArtist.Path = page.Path;
_mappedArtists.Set(page.Name, mappedArtist, TimeSpan.FromDays(2));
return mappedArtist;
}
return null;
});
var mapping = MapToResource(mapped.Where(m => m != null)).ToList().AsResponse();
return mapping;
}
private static IEnumerable<ArtistResource> MapToResource(IEnumerable<Artist> artists)
{
foreach (var currentArtist in artists)
{
var resource = currentArtist.ToResource();
var poster = currentArtist.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
resource.RemotePoster = poster.Url;
}
yield return resource;
}
}
}
}

@ -1,31 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Nancy;
using Lidarr.Http.Extensions;
using NzbDrone.Core.Music;
namespace NzbDrone.Api.Music
{
public class ArtistEditorModule : NzbDroneApiModule
{
private readonly IArtistService _artistService;
public ArtistEditorModule(IArtistService seriesService)
: base("/artist/editor")
{
_artistService = seriesService;
Put["/"] = artist => SaveAll();
}
private Response SaveAll()
{
var resources = Request.Body.FromJson<List<ArtistResource>>();
var artist = resources.Select(artistResource => artistResource.ToModel(_artistService.GetArtist(artistResource.Id))).ToList();
return _artistService.UpdateArtists(artist)
.ToResource()
.AsResponse(HttpStatusCode.Accepted);
}
}
}

@ -1,47 +0,0 @@
using Nancy;
using Lidarr.Http.Extensions;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidarr.Http;
namespace NzbDrone.Api.Music
{
public class ArtistLookupModule : LidarrRestModule<ArtistResource>
{
private readonly ISearchForNewArtist _searchProxy;
public ArtistLookupModule(ISearchForNewArtist searchProxy)
: base("/artist/lookup")
{
_searchProxy = searchProxy;
Get["/"] = x => Search();
}
private Response Search()
{
var iTunesResults = _searchProxy.SearchForNewArtist((string)Request.Query.term);
return MapToResource(iTunesResults).AsResponse();
}
private static IEnumerable<ArtistResource> MapToResource(IEnumerable<Core.Music.Artist> artists)
{
foreach (var currentArtist in artists)
{
var resource = currentArtist.ToResource();
var poster = currentArtist.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
resource.RemotePoster = poster.Url;
}
yield return resource;
}
}
}
}

@ -1,226 +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.Music;
using NzbDrone.Core.Music.Events;
using NzbDrone.Core.ArtistStats;
using NzbDrone.Core.Validation;
using NzbDrone.Core.Validation.Paths;
using NzbDrone.SignalR;
using Lidarr.Http;
using Lidarr.Http.Mapping;
namespace NzbDrone.Api.Music
{
public class ArtistModule : LidarrRestModuleWithSignalR<ArtistResource, Artist>,
IHandle<TrackImportedEvent>,
IHandle<TrackFileDeletedEvent>,
IHandle<ArtistUpdatedEvent>,
IHandle<ArtistEditedEvent>,
IHandle<ArtistDeletedEvent>,
IHandle<ArtistRenamedEvent>
//IHandle<MediaCoversUpdatedEvent>
{
private readonly IArtistService _artistService;
private readonly IAddArtistService _addArtistService;
private readonly IArtistStatisticsService _artistStatisticsService;
private readonly IMapCoversToLocal _coverMapper;
public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
IArtistService artistService,
IAddArtistService addArtistService,
IArtistStatisticsService artistStatisticsService,
IMapCoversToLocal coverMapper,
RootFolderValidator rootFolderValidator,
ArtistPathValidator seriesPathValidator,
ArtistExistsValidator artistExistsValidator,
SeriesAncestorValidator seriesAncestorValidator,
ProfileExistsValidator profileExistsValidator,
LanguageProfileExistsValidator languageProfileExistsValidator
)
: base(signalRBroadcaster)
{
_artistService = artistService;
_addArtistService = addArtistService;
_artistStatisticsService = artistStatisticsService;
_coverMapper = coverMapper;
GetResourceAll = AllArtist;
GetResourceById = GetArtist;
CreateResource = AddArtist;
UpdateResource = UpdatArtist;
DeleteResource = DeleteArtist;
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)
.When(s => !s.Path.IsNullOrWhiteSpace());
SharedValidator.RuleFor(s => s.ProfileId).SetValidator(profileExistsValidator);
SharedValidator.RuleFor(s => s.LanguageProfileId).SetValidator(languageProfileExistsValidator);
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.ForeignArtistId).NotEqual("").SetValidator(artistExistsValidator);
PutValidator.RuleFor(s => s.Path).IsValidPath();
}
private ArtistResource GetArtist(int id)
{
var artist = _artistService.GetArtist(id);
return MapToResource(artist);
}
private ArtistResource MapToResource(Artist artist)
{
if (artist == null) return null;
var resource = artist.ToResource();
MapCoversToLocal(resource);
FetchAndLinkArtistStatistics(resource);
//PopulateAlternateTitles(resource);
return resource;
}
private List<ArtistResource> AllArtist()
{
var artistStats = _artistStatisticsService.ArtistStatistics();
var artistResources = _artistService.GetAllArtists().ToResource();
MapCoversToLocal(artistResources.ToArray());
LinkArtistStatistics(artistResources, artistStats);
//PopulateAlternateTitles(seriesResources);
return artistResources;
}
private int AddArtist(ArtistResource artistResource)
{
var model = artistResource.ToModel();
return _addArtistService.AddArtist(model).Id;
}
private void UpdatArtist(ArtistResource artistResource)
{
var model = artistResource.ToModel(_artistService.GetArtist(artistResource.Id));
_artistService.UpdateArtist(model);
BroadcastResourceChange(ModelAction.Updated, artistResource.Id);
}
private void DeleteArtist(int id)
{
var deleteFiles = false;
var deleteFilesQuery = Request.Query.deleteFiles;
if (deleteFilesQuery.HasValue)
{
deleteFiles = Convert.ToBoolean(deleteFilesQuery.Value);
}
_artistService.DeleteArtist(id, deleteFiles);
}
private void MapCoversToLocal(params ArtistResource[] artists)
{
foreach (var artistResource in artists)
{
_coverMapper.ConvertToLocalUrls(artistResource.Id, artistResource.Images);
}
}
private void FetchAndLinkArtistStatistics(ArtistResource resource)
{
LinkArtistStatistics(resource, _artistStatisticsService.ArtistStatistics(resource.Id));
}
private void LinkArtistStatistics(List<ArtistResource> resources, List<ArtistStatistics> artistStatistics)
{
var dictArtistStats = artistStatistics.ToDictionary(v => v.ArtistId);
foreach (var artist in resources)
{
var stats = dictArtistStats.GetValueOrDefault(artist.Id);
if (stats == null) continue;
LinkArtistStatistics(artist, stats);
}
}
private void LinkArtistStatistics(ArtistResource resource, ArtistStatistics artistStatistics)
{
resource.TotalTrackCount = artistStatistics.TotalTrackCount;
resource.TrackCount = artistStatistics.TrackCount;
resource.TrackFileCount = artistStatistics.TrackFileCount;
resource.SizeOnDisk = artistStatistics.SizeOnDisk;
resource.AlbumCount = artistStatistics.AlbumCount;
//if (artistStatistics.AlbumStatistics != null)
//{
// var dictSeasonStats = artistStatistics.SeasonStatistics.ToDictionary(v => v.SeasonNumber);
// foreach (var album in resource.Albums)
// {
// album.Statistics = dictSeasonStats.GetValueOrDefault(album.Id).ToResource();
// }
//}
}
public void Handle(TrackImportedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.ImportedTrack.ArtistId);
}
public void Handle(TrackFileDeletedEvent message)
{
if (message.Reason == DeleteMediaFileReason.Upgrade) return;
BroadcastResourceChange(ModelAction.Updated, message.TrackFile.ArtistId);
}
public void Handle(ArtistUpdatedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Artist.Id);
}
public void Handle(ArtistEditedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Artist.Id);
}
public void Handle(ArtistDeletedEvent message)
{
BroadcastResourceChange(ModelAction.Deleted, message.Artist.ToResource());
}
public void Handle(ArtistRenamedEvent message)
{
BroadcastResourceChange(ModelAction.Updated, message.Artist.Id);
}
//public void Handle(MediaCoversUpdatedEvent message)
//{
// BroadcastResourceChange(ModelAction.Updated, message.Artist.Id);
//}
}
}

@ -1,189 +0,0 @@
using Lidarr.Http.REST;
using NzbDrone.Api.Albums;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.Music;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Api.Music
{
public class ArtistResource : RestResource
{
public ArtistResource()
{
Monitored = true;
}
//View Only
public string Name { get; set; }
public string ForeignArtistId { get; set; }
public string MBId { get; set; }
public int TADBId { get; set; }
public int DiscogsId { get; set; }
public string AllMusicId { get; set; }
public string Overview { get; set; }
public int? AlbumCount{ get; set; }
public int? TotalTrackCount { get; set; }
public int? TrackCount { get; set; }
public int? TrackFileCount { get; set; }
public long? SizeOnDisk { get; set; }
//public SeriesStatusType Status { get; set; }
public List<MediaCover> Images { get; set; }
public List<Member> Members { get; set; }
public string RemotePoster { get; set; }
public List<AlbumResource> Albums { get; set; }
//View & Edit
public string Path { get; set; }
public int ProfileId { get; set; }
public int LanguageProfileId { get; set; }
//Editing Only
public bool AlbumFolder { get; set; }
public bool Monitored { get; set; }
public string RootFolderPath { get; set; }
//public string Certification { get; set; }
public List<string> Genres { get; set; }
public string CleanName { get; set; }
public string SortName { get; set; }
public HashSet<int> Tags { get; set; }
public DateTime Added { get; set; }
public AddArtistOptions AddOptions { get; set; }
public Ratings Ratings { get; set; }
public string NameSlug { get; set; }
}
public static class ArtistResourceMapper
{
public static ArtistResource ToResource(this Core.Music.Artist model)
{
if (model == null) return null;
return new ArtistResource
{
Id = model.Id,
MBId = model.MBId,
TADBId = model.TADBId,
DiscogsId = model.DiscogsId,
AllMusicId = model.AMId,
Name = model.Name,
CleanName = model.CleanName,
//AlternateTitles
SortName = model.SortName,
//TotalTrackCount
//TrackCount
//TrackFileCount
//SizeOnDisk
//Status = resource.Status,
Overview = model.Overview,
//NextAiring
//PreviousAiring
//Network = resource.Network,
//AirTime = resource.AirTime,
Images = model.Images,
Members = model.Members,
//Albums = model.Albums.ToResource(),
//Year = resource.Year,
Path = model.Path,
ProfileId = model.ProfileId,
LanguageProfileId = model.LanguageProfileId,
Monitored = model.Monitored,
AlbumFolder = model.AlbumFolder,
//UseSceneNumbering = resource.UseSceneNumbering,
//Runtime = resource.Runtime,
//TvdbId = resource.TvdbId,
//TvRageId = resource.TvRageId,
//TvMazeId = resource.TvMazeId,
//FirstAired = resource.FirstAired,
//LastInfoSync = resource.LastInfoSync,
//SeriesType = resource.SeriesType,
ForeignArtistId = model.ForeignArtistId,
NameSlug = model.NameSlug,
RootFolderPath = model.RootFolderPath,
Genres = model.Genres,
Tags = model.Tags,
Added = model.Added,
AddOptions = model.AddOptions,
Ratings = model.Ratings,
};
}
public static Core.Music.Artist ToModel(this ArtistResource resource)
{
if (resource == null) return null;
return new Core.Music.Artist
{
Id = resource.Id,
Name = resource.Name,
CleanName = resource.CleanName,
//AlternateTitles
SortName = resource.SortName,
MBId = resource.MBId,
TADBId = resource.TADBId,
DiscogsId = resource.DiscogsId,
AMId = resource.AllMusicId, //TODO change model and DB to AllMusic instead of AM
//TotalEpisodeCount
//TrackCount
//TrackFileCount
//SizeOnDisk
//Status = resource.Status,
Overview = resource.Overview,
//NextAiring
//PreviousAiring
//Network = resource.Network,
//AirTime = resource.AirTime,
Images = resource.Images,
Members = resource.Members,
//Albums = resource.Albums.ToModel(),
//Year = resource.Year,
Path = resource.Path,
ProfileId = resource.ProfileId,
LanguageProfileId = resource.LanguageProfileId,
AlbumFolder = resource.AlbumFolder,
Monitored = resource.Monitored,
//LastInfoSync = resource.LastInfoSync,
ForeignArtistId = resource.ForeignArtistId,
NameSlug = resource.NameSlug,
RootFolderPath = resource.RootFolderPath,
Genres = resource.Genres,
Tags = resource.Tags,
Added = resource.Added,
AddOptions = resource.AddOptions,
Ratings = resource.Ratings
};
}
public static Core.Music.Artist ToModel(this ArtistResource resource, Core.Music.Artist artist)
{
var updatedArtist = resource.ToModel();
artist.ApplyChanges(updatedArtist);
return artist;
}
public static List<ArtistResource> ToResource(this IEnumerable<Core.Music.Artist> artist)
{
return artist.Select(ToResource).ToList();
}
}
}

@ -1,30 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Nancy;
using Nancy.Extensions;
using Lidarr.Http.Extensions;
using NzbDrone.Core.Music;
namespace NzbDrone.Api.Music
{
public class ListImportModule : NzbDroneApiModule
{
private readonly IAddArtistService _artistService;
public ListImportModule(IAddArtistService artistService)
: base("/artist/import")
{
_artistService = artistService;
Put["/"] = Artist => SaveAll();
}
private Response SaveAll()
{
var resources = Request.Body.FromJson<List<ArtistResource>>();
var Artists = resources.Select(ArtistResource => (ArtistResource.ToModel())).Where(m => m != null).DistinctBy(m => m.ForeignArtistId).ToList();
return _artistService.AddArtists(Artists).ToResource().AsResponse(HttpStatusCode.Accepted);
}
}
}

@ -1,48 +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;
}
protected override void Validate(NotificationDefinition definition, bool includeWarnings)
{
if (!definition.OnGrab && !definition.OnDownload) return;
base.Validate(definition, includeWarnings);
}
}
}

@ -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,243 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{FD286DF8-2D3A-4394-8AD5-443FADE55FB2}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NzbDrone.Api</RootNamespace>
<AssemblyName>NzbDrone.Api</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<ProductVersion>12.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\_output\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>..\..\_output\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="antlr.runtime, Version=2.7.6.2, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net46\antlr.runtime.dll</HintPath>
</Reference>
<Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\FluentValidation.6.2.1.0\lib\Net45\FluentValidation.dll</HintPath>
</Reference>
<Reference Include="Ical.Net, Version=2.1.0.18776, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net46\Ical.Net.dll</HintPath>
</Reference>
<Reference Include="Ical.Net.Collections, Version=2.1.0.18775, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net46\Ical.Net.Collections.dll</HintPath>
</Reference>
<Reference Include="Nancy, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.1.4.4\lib\net40\Nancy.dll</HintPath>
</Reference>
<Reference Include="Nancy.Authentication.Basic, Version=1.4.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.Authentication.Basic.1.4.1\lib\net40\Nancy.Authentication.Basic.dll</HintPath>
</Reference>
<Reference Include="Nancy.Authentication.Forms, Version=1.4.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Nancy.Authentication.Forms.1.4.1\lib\net40\Nancy.Authentication.Forms.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.4.3\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="NodaTime, Version=1.3.0.0, Culture=neutral, PublicKeyToken=4226afe0d9b296d1, processorArchitecture=MSIL">
<HintPath>..\packages\Ical.Net.2.2.32\lib\net46\NodaTime.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data.SQLite, Version=1.0.84.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Libraries\Sqlite\System.Data.SQLite.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\NzbDrone.Common\Properties\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Albums\AlbumModuleWithSignalR.cs" />
<Compile Include="Albums\AlbumStatisticsResource.cs" />
<Compile Include="Blacklist\BlacklistModule.cs" />
<Compile Include="Blacklist\BlacklistResource.cs" />
<Compile Include="Calendar\CalendarFeedModule.cs" />
<Compile Include="Calendar\CalendarModule.cs" />
<Compile Include="Commands\CommandModule.cs" />
<Compile Include="Commands\CommandResource.cs" />
<Compile Include="AlbumStudio\AlbumStudioModule.cs" />
<Compile Include="AlbumStudio\AlbumStudioResource.cs" />
<Compile Include="Music\ArtistBulkImportModule.cs" />
<Compile Include="Music\ArtistEditorModule.cs" />
<Compile Include="Music\ListImport.cs" />
<Compile Include="TrackFiles\TrackFileModule.cs" />
<Compile Include="TrackFiles\TrackFileResource.cs" />
<Compile Include="Albums\AlbumModule.cs" />
<Compile Include="Tracks\TrackModule.cs" />
<Compile Include="Tracks\TrackModuleWithSignalR.cs" />
<Compile Include="Tracks\TrackResource.cs" />
<Compile Include="Tracks\RenameTrackModule.cs" />
<Compile Include="Tracks\RenameTrackResource.cs" />
<Compile Include="Indexers\ReleaseModuleBase.cs" />
<Compile Include="Indexers\ReleasePushModule.cs" />
<Compile Include="Albums\AlbumResource.cs" />
<Compile Include="Music\ArtistLookupModule.cs" />
<Compile Include="Music\ArtistModule.cs" />
<Compile Include="Music\ArtistResource.cs" />
<Compile Include="Parse\ParseModule.cs" />
<Compile Include="Parse\ParseResource.cs" />
<Compile Include="ManualImport\ManualImportModule.cs" />
<Compile Include="ManualImport\ManualImportResource.cs" />
<Compile Include="Profiles\Delay\DelayProfileModule.cs" />
<Compile Include="Profiles\Delay\DelayProfileResource.cs" />
<Compile Include="Queue\QueueActionModule.cs" />
<Compile Include="RemotePathMappings\RemotePathMappingModule.cs" />
<Compile Include="RemotePathMappings\RemotePathMappingResource.cs" />
<Compile Include="Config\UiConfigModule.cs" />
<Compile Include="Config\UiConfigResource.cs" />
<Compile Include="Config\DownloadClientConfigModule.cs" />
<Compile Include="Config\DownloadClientConfigResource.cs" />
<Compile Include="Config\HostConfigModule.cs" />
<Compile Include="Config\HostConfigResource.cs" />
<Compile Include="Config\IndexerConfigModule.cs" />
<Compile Include="Config\IndexerConfigResource.cs" />
<Compile Include="Config\MediaManagementConfigModule.cs" />
<Compile Include="Config\MediaManagementConfigResource.cs" />
<Compile Include="Config\NamingConfigModule.cs" />
<Compile Include="Config\NamingConfigResource.cs" />
<Compile Include="Config\NamingSampleResource.cs" />
<Compile Include="Config\NzbDroneConfigModule.cs" />
<Compile Include="FileSystem\FileSystemModule.cs" />
<Compile Include="DiskSpace\DiskSpaceModule.cs" />
<Compile Include="DiskSpace\DiskSpaceResource.cs" />
<Compile Include="DownloadClient\DownloadClientModule.cs" />
<Compile Include="DownloadClient\DownloadClientResource.cs" />
<Compile Include="EpisodeFiles\EpisodeFileModule.cs" />
<Compile Include="EpisodeFiles\EpisodeFileResource.cs" />
<Compile Include="Episodes\EpisodeModule.cs" />
<Compile Include="Episodes\EpisodeModuleWithSignalR.cs" />
<Compile Include="Episodes\EpisodeResource.cs" />
<Compile Include="Health\HealthModule.cs" />
<Compile Include="Health\HealthResource.cs" />
<Compile Include="History\HistoryModule.cs" />
<Compile Include="History\HistoryResource.cs" />
<Compile Include="Indexers\IndexerModule.cs" />
<Compile Include="Indexers\IndexerResource.cs" />
<Compile Include="Indexers\ReleaseModule.cs" />
<Compile Include="Indexers\ReleaseResource.cs" />
<Compile Include="Logs\LogFileModule.cs" />
<Compile Include="Logs\LogFileModuleBase.cs" />
<Compile Include="Logs\LogFileResource.cs" />
<Compile Include="Logs\LogModule.cs" />
<Compile Include="Logs\LogResource.cs" />
<Compile Include="Logs\UpdateLogFileModule.cs" />
<Compile Include="MediaCovers\MediaCoverModule.cs" />
<Compile Include="Metadata\MetadataModule.cs" />
<Compile Include="Metadata\MetadataResource.cs" />
<Compile Include="Notifications\NotificationModule.cs" />
<Compile Include="Notifications\NotificationResource.cs" />
<Compile Include="NzbDroneApiModule.cs" />
<Compile Include="NzbDroneFeedModule.cs" />
<Compile Include="Profiles\Languages\LanguageModule.cs" />
<Compile Include="Profiles\Languages\LanguageResource.cs" />
<Compile Include="Profiles\LegacyProfileModule.cs" />
<Compile Include="Profiles\ProfileModule.cs" />
<Compile Include="Profiles\ProfileResource.cs" />
<Compile Include="Profiles\ProfileSchemaModule.cs" />
<Compile Include="Profiles\ProfileValidation.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProviderModuleBase.cs" />
<Compile Include="ProviderResource.cs" />
<Compile Include="Qualities\QualityDefinitionModule.cs" />
<Compile Include="Qualities\QualityDefinitionResource.cs" />
<Compile Include="Queue\QueueModule.cs" />
<Compile Include="Queue\QueueResource.cs" />
<Compile Include="Restrictions\RestrictionModule.cs" />
<Compile Include="Restrictions\RestrictionResource.cs" />
<Compile Include="RootFolders\RootFolderModule.cs" />
<Compile Include="RootFolders\RootFolderResource.cs" />
<Compile Include="SeasonPass\SeasonPassResource.cs" />
<Compile Include="Series\AlternateTitleResource.cs" />
<Compile Include="Series\SeasonResource.cs" />
<Compile Include="SeasonPass\SeasonPassModule.cs" />
<Compile Include="Series\SeriesEditorModule.cs" />
<Compile Include="Series\SeriesModule.cs" />
<Compile Include="Series\SeriesResource.cs" />
<Compile Include="Series\SeasonStatisticsResource.cs" />
<Compile Include="System\Backup\BackupModule.cs" />
<Compile Include="System\Backup\BackupResource.cs" />
<Compile Include="System\Tasks\TaskModule.cs" />
<Compile Include="System\Tasks\TaskResource.cs" />
<Compile Include="System\SystemModule.cs" />
<Compile Include="Tags\TagModule.cs" />
<Compile Include="Tags\TagResource.cs" />
<Compile Include="Update\UpdateModule.cs" />
<Compile Include="Update\UpdateResource.cs" />
<Compile Include="Wanted\CutoffModule.cs" />
<Compile Include="Wanted\LegacyMissingModule.cs" />
<Compile Include="Wanted\MissingModule.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Marr.Data\Marr.Data.csproj">
<Project>{F6FC6BE7-0847-4817-A1ED-223DC647C3D7}</Project>
<Name>Marr.Data</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.Common\NzbDrone.Common.csproj">
<Project>{F2BE0FDF-6E47-4827-A420-DD4EF82407F8}</Project>
<Name>NzbDrone.Common</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.Core\NzbDrone.Core.csproj">
<Project>{FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}</Project>
<Name>NzbDrone.Core</Name>
</ProjectReference>
<ProjectReference Include="..\NzbDrone.SignalR\NzbDrone.SignalR.csproj">
<Project>{7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}</Project>
<Name>NzbDrone.SignalR</Name>
</ProjectReference>
<ProjectReference Include="..\Lidarr.Http\Lidarr.Http.csproj">
<Project>{5370bff7-1bd7-46bc-af06-7d9ea5cda1d6}</Project>
<Name>Lidarr.Http</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

@ -1,12 +0,0 @@
using Nancy;
namespace NzbDrone.Api
{
public abstract class NzbDroneApiModule : NancyModule
{
protected NzbDroneApiModule(string resource)
: base("/api/" + resource.Trim('/'))
{
}
}
}

@ -1,12 +0,0 @@
using Nancy;
namespace NzbDrone.Api
{
public abstract class NzbDroneFeedModule : NancyModule
{
protected NzbDroneFeedModule(string resource)
: base("/feed/" + resource.Trim('/'))
{
}
}
}

@ -1,51 +0,0 @@
using NzbDrone.Api.Albums;
using NzbDrone.Api.Music;
using NzbDrone.Core.Parser;
using Lidarr.Http;
namespace NzbDrone.Api.Parse
{
public class ParseModule : LidarrRestModule<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 parsedAlbumInfo = Parser.ParseAlbumTitle(title);
if (parsedAlbumInfo == null)
{
return null;
}
var remoteAlbum = _parsingService.Map(parsedAlbumInfo);
if (remoteAlbum != null)
{
return new ParseResource
{
Title = title,
ParsedAlbumInfo = remoteAlbum.ParsedAlbumInfo,
Artist = remoteAlbum.Artist.ToResource(),
Albums = remoteAlbum.Albums.ToResource()
};
}
else
{
return new ParseResource
{
Title = title,
ParsedAlbumInfo = parsedAlbumInfo
};
}
}
}
}

@ -1,16 +0,0 @@
using System.Collections.Generic;
using Lidarr.Http.REST;
using NzbDrone.Api.Music;
using NzbDrone.Api.Albums;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Api.Parse
{
public class ParseResource : RestResource
{
public string Title { get; set; }
public ParsedAlbumInfo ParsedAlbumInfo { get; set; }
public ArtistResource Artist { get; set; }
public List<AlbumResource> Albums { get; set; }
}
}

@ -1,76 +0,0 @@
using System.Collections.Generic;
using FluentValidation;
using FluentValidation.Results;
using Lidarr.Http.REST;
using Lidarr.Http.Validation;
using NzbDrone.Core.Profiles.Delay;
using Lidarr.Http;
namespace NzbDrone.Api.Profiles.Delay
{
public class DelayProfileModule : LidarrRestModule<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.Custom(delayProfile =>
{
if (!delayProfile.EnableUsenet && !delayProfile.EnableTorrent)
{
return new ValidationFailure("", "Either Usenet or Torrent should be enabled");
}
return null;
});
}
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,63 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Lidarr.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 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,
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,
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 Lidarr.Http;
namespace NzbDrone.Api.Profiles.Languages
{
public class LanguageModule : LidarrRestModule<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 Lidarr.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
{
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,57 +0,0 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Validation;
using Lidarr.Http;
using Lidarr.Http.Mapping;
namespace NzbDrone.Api.Profiles
{
public class ProfileModule : LidarrRestModule<ProfileResource>
{
private readonly IProfileService _profileService;
public ProfileModule(IProfileService profileService)
{
_profileService = profileService;
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 _profileService.Add(model).Id;
}
private void DeleteProfile(int id)
{
_profileService.Delete(id);
}
private void Update(ProfileResource resource)
{
var model = resource.ToModel();
_profileService.Update(model);
}
private ProfileResource GetById(int id)
{
return _profileService.Get(id).ToResource();
}
private List<ProfileResource> GetAll()
{
return _profileService.All().ToResource();
}
}
}

@ -1,81 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Lidarr.Http.REST;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Api.Profiles
{
public class ProfileResource : RestResource
{
public string Name { 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 Profile model)
{
if (model == null) return null;
return new ProfileResource
{
Id = model.Id,
Name = model.Name,
Cutoff = model.Cutoff,
Items = model.Items.ConvertAll(ToResource)
};
}
public static ProfileQualityItemResource ToResource(this ProfileQualityItem model)
{
if (model == null) return null;
return new ProfileQualityItemResource
{
Quality = model.Quality,
Allowed = model.Allowed
};
}
public static Profile ToModel(this ProfileResource resource)
{
if (resource == null) return null;
return new Profile
{
Id = resource.Id,
Name = resource.Name,
Cutoff = (Quality)resource.Cutoff.Id,
Items = resource.Items.ConvertAll(ToModel)
};
}
public static ProfileQualityItem ToModel(this ProfileQualityItemResource resource)
{
if (resource == null) return null;
return new ProfileQualityItem
{
Quality = (Quality)resource.Quality.Id,
Allowed = resource.Allowed
};
}
public static List<ProfileResource> ToResource(this IEnumerable<Profile> models)
{
return models.Select(ToResource).ToList();
}
}
}

@ -1,37 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities;
using Lidarr.Http;
using Lidarr.Http.Mapping;
namespace NzbDrone.Api.Profiles
{
public class ProfileSchemaModule : LidarrRestModule<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 ProfileQualityItem { Quality = v.Quality, Allowed = false })
.ToList();
var profile = new Profile();
profile.Cutoff = Quality.Unknown;
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,11 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("NzbDrone.Api")]
[assembly: Guid("4c0922d7-979e-4ff7-b44b-b8ac2100eeb5")]
[assembly: AssemblyVersion("10.0.0.*")]
[assembly: InternalsVisibleTo("NzbDrone.Core")]

@ -1,228 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using FluentValidation.Results;
using Nancy;
using Lidarr.Http.ClientSchema;
using Lidarr.Http.Extensions;
using NzbDrone.Common.Reflection;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
using Newtonsoft.Json;
using Lidarr.Http;
using Lidarr.Http.Mapping;
namespace NzbDrone.Api
{
public abstract class ProviderModuleBase<TProviderResource, TProvider, TProviderDefinition> : LidarrRestModule<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, false);
if (providerDefinition.Enable)
{
Test(providerDefinition, false);
}
providerDefinition = _providerFactory.Create(providerDefinition);
return providerDefinition.Id;
}
private void UpdateProvider(TProviderResource providerResource)
{
var providerDefinition = GetDefinition(providerResource, false);
_providerFactory.Update(providerDefinition);
}
private TProviderDefinition GetDefinition(TProviderResource providerResource, bool includeWarnings = false, bool validate = true)
{
var definition = new TProviderDefinition();
MapToModel(definition, providerResource);
if (validate)
{
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 = string.Format("https://github.com/Lidarr/Lidarr/wiki/Supported-{0}#{1}",
typeof(TProviderResource).Name.Replace("Resource", "s"),
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 Response 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.AsResponse();
}
private Response Test(TProviderResource providerResource)
{
// Don't validate when getting the definition so we can validate afterwards (avoids validation being skipped because the provider is disabled)
var providerDefinition = GetDefinition(providerResource, true, false);
Validate(providerDefinition, true);
Test(providerDefinition, true);
return "{}";
}
private Response RequestAction(string action, TProviderResource providerResource)
{
var providerDefinition = GetDefinition(providerResource, true, 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 = JsonConvert.SerializeObject(data);
resp.ContentType = "application/json";
return resp;
}
protected virtual 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 = 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 Lidarr.Http.ClientSchema;
using Lidarr.Http.REST;
using NzbDrone.Core.ThingiProvider;
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 Lidarr.Http;
using Lidarr.Http.Mapping;
namespace NzbDrone.Api.Qualities
{
public class QualityDefinitionModule : LidarrRestModule<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 System.Collections.Generic;
using System.Linq;
using Lidarr.Http.REST;
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,133 +0,0 @@
using System;
using Nancy;
using Nancy.Responses;
using Lidarr.Http.Extensions;
using Lidarr.Http.REST;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Queue;
using Lidarr.Http;
namespace NzbDrone.Api.Queue
{
public class QueueActionModule : LidarrRestModule<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 Response Remove(int id)
{
var blacklist = false;
var blacklistQuery = Request.Query.blacklist;
if (blacklistQuery.HasValue)
{
blacklist = Convert.ToBoolean(blacklistQuery.Value);
}
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
if (pendingRelease != null)
{
_pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id);
return new object().AsResponse();
}
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.DownloadId, true);
if (blacklist)
{
_failedDownloadService.MarkAsFailed(trackedDownload.DownloadItem.DownloadId);
}
return new object().AsResponse();
}
private JsonResponse<QueueResource> Import()
{
var resource = Request.Body.FromJson<QueueResource>();
var trackedDownload = GetTrackedDownload(resource.Id);
_completedDownloadService.Process(trackedDownload, true);
return resource.AsResponse();
}
private JsonResponse<QueueResource> Grab()
{
var resource = Request.Body.FromJson<QueueResource>();
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(resource.Id);
if (pendingRelease == null)
{
throw new NotFoundException();
}
_downloadService.DownloadReport(pendingRelease.RemoteAlbum);
return resource.AsResponse();
}
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 Lidarr.Http;
namespace NzbDrone.Api.Queue
{
public class QueueModule : LidarrRestModuleWithSignalR<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();
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,63 +0,0 @@
using System;
using System.Collections.Generic;
using Lidarr.Http.REST;
using NzbDrone.Core.Qualities;
using NzbDrone.Api.Series;
using NzbDrone.Api.Episodes;
using NzbDrone.Api.Music;
using NzbDrone.Api.Albums;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Indexers;
using System.Linq;
namespace NzbDrone.Api.Queue
{
public class QueueResource : RestResource
{
public ArtistResource Artist { get; set; }
public AlbumResource Album { 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,
Artist = model.Artist.ToResource(),
Album = model.Album.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,
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 Lidarr.Http;
namespace NzbDrone.Api.RemotePathMappings
{
public class RemotePathMappingModule : LidarrRestModule<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 System.Collections.Generic;
using System.Linq;
using Lidarr.Http.REST;
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.Results;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Restrictions;
using Lidarr.Http;
namespace NzbDrone.Api.Restrictions
{
public class RestrictionModule : LidarrRestModule<RestrictionResource>
{
private readonly IRestrictionService _restrictionService;
public RestrictionModule(IRestrictionService restrictionService)
{
_restrictionService = restrictionService;
GetResourceById = GetRestriction;
GetResourceAll = GetAllRestrictions;
CreateResource = CreateRestriction;
UpdateResource = UpdateRestriction;
DeleteResource = DeleteRestriction;
SharedValidator.Custom(restriction =>
{
if (restriction.Ignored.IsNullOrWhiteSpace() && restriction.Required.IsNullOrWhiteSpace())
{
return new ValidationFailure("", "Either 'Must contain' or 'Must not contain' is required");
}
return null;
});
}
private RestrictionResource GetRestriction(int id)
{
return _restrictionService.Get(id).ToResource();
}
private List<RestrictionResource> GetAllRestrictions()
{
return _restrictionService.All().ToResource();
}
private int CreateRestriction(RestrictionResource resource)
{
return _restrictionService.Add(resource.ToModel()).Id;
}
private void UpdateRestriction(RestrictionResource resource)
{
_restrictionService.Update(resource.ToModel());
}
private void DeleteRestriction(int id)
{
_restrictionService.Delete(id);
}
}
}

@ -1,58 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Lidarr.Http.REST;
using NzbDrone.Core.Restrictions;
namespace NzbDrone.Api.Restrictions
{
public class RestrictionResource : RestResource
{
public string Required { get; set; }
public string Preferred { 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 Restriction model)
{
if (model == null) return null;
return new RestrictionResource
{
Id = model.Id,
Required = model.Required,
Preferred = model.Preferred,
Ignored = model.Ignored,
Tags = new HashSet<int>(model.Tags)
};
}
public static Restriction ToModel(this RestrictionResource resource)
{
if (resource == null) return null;
return new Restriction
{
Id = resource.Id,
Required = resource.Required,
Preferred = resource.Preferred,
Ignored = resource.Ignored,
Tags = new HashSet<int>(resource.Tags)
};
}
public static List<RestrictionResource> ToResource(this IEnumerable<Restriction> models)
{
return models.Select(ToResource).ToList();
}
}
}

@ -1,62 +0,0 @@
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Validation.Paths;
using NzbDrone.SignalR;
using Lidarr.Http;
namespace NzbDrone.Api.RootFolders
{
public class RootFolderModule : LidarrRestModuleWithSignalR<RootFolderResource, RootFolder>
{
private readonly IRootFolderService _rootFolderService;
public RootFolderModule(IRootFolderService rootFolderService,
IBroadcastSignalRMessage signalRBroadcaster,
RootFolderValidator rootFolderValidator,
PathExistsValidator pathExistsValidator,
MappedNetworkDriveValidator mappedNetworkDriveValidator,
StartupFolderValidator startupFolderValidator,
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(folderWritableValidator);
}
private RootFolderResource GetRootFolder(int id)
{
return _rootFolderService.Get(id).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,51 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Lidarr.Http.REST;
using NzbDrone.Core.RootFolders;
namespace NzbDrone.Api.RootFolders
{
public class RootFolderResource : RestResource
{
public string Path { get; set; }
public long? FreeSpace { 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,
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 Lidarr.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 Response UpdateAll()
{
//Read from request
var request = Request.Body.FromJson<SeasonPassResource>();
foreach (var s in request.Series)
{
_episodeMonitoredService.SetEpisodeMonitoredStatus(s, request.MonitoringOptions);
}
return "ok".AsResponse(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,9 +0,0 @@
namespace NzbDrone.Api.Series
{
public class AlternateTitleResource
{
public string Title { get; set; }
public int? SeasonNumber { get; set; }
public int? SceneSeasonNumber { get; set; }
}
}

@ -1,47 +0,0 @@
using System.Collections.Generic;
using System.Linq;
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 static class SeasonResourceMapper
{
public static SeasonResource ToResource(this Season model)
{
if (model == null) return null;
return new SeasonResource
{
SeasonNumber = model.SeasonNumber,
Monitored = model.Monitored
};
}
public static Season ToModel(this SeasonResource resource)
{
if (resource == null) return null;
return new Season
{
SeasonNumber = resource.SeasonNumber,
Monitored = resource.Monitored
};
}
public static List<SeasonResource> ToResource(this IEnumerable<Season> models)
{
return models.Select(ToResource).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
};
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save