parent
fe956f340c
commit
c247d07e84
@ -1,26 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Http;
|
||||
using Nancy;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
|
||||
namespace Lidarr.Api.V1.Albums
|
||||
{
|
||||
public class AlbumLookupModule : LidarrRestModule<AlbumResource>
|
||||
[V1ApiController("album/lookup")]
|
||||
public class AlbumLookupController : Controller
|
||||
{
|
||||
private readonly ISearchForNewAlbum _searchProxy;
|
||||
|
||||
public AlbumLookupModule(ISearchForNewAlbum searchProxy)
|
||||
: base("/album/lookup")
|
||||
public AlbumLookupController(ISearchForNewAlbum searchProxy)
|
||||
{
|
||||
_searchProxy = searchProxy;
|
||||
Get("/", x => Search());
|
||||
}
|
||||
|
||||
private object Search()
|
||||
[HttpGet]
|
||||
public object Search(string term)
|
||||
{
|
||||
var searchResults = _searchProxy.SearchForNewAlbum((string)Request.Query.term, null);
|
||||
var searchResults = _searchProxy.SearchForNewAlbum(term, null);
|
||||
return MapToResource(searchResults).ToList();
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.Extensions;
|
||||
using Nancy;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace Lidarr.Api.V1.Artist
|
||||
{
|
||||
public class ArtistImportModule : LidarrRestModule<ArtistResource>
|
||||
{
|
||||
private readonly IAddArtistService _addArtistService;
|
||||
|
||||
public ArtistImportModule(IAddArtistService addArtistService)
|
||||
: base("/artist/import")
|
||||
{
|
||||
_addArtistService = addArtistService;
|
||||
Post("/", x => Import());
|
||||
}
|
||||
|
||||
private object Import()
|
||||
{
|
||||
var resource = Request.Body.FromJson<List<ArtistResource>>();
|
||||
var newArtists = resource.ToModel();
|
||||
|
||||
return _addArtistService.AddArtists(newArtists).ToResource();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Http;
|
||||
using Nancy;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.MetadataSource;
|
||||
|
||||
namespace Lidarr.Api.V1.Artist
|
||||
{
|
||||
public class ArtistLookupModule : LidarrRestModule<ArtistResource>
|
||||
[V1ApiController("artist/lookup")]
|
||||
public class ArtistLookupController : Controller
|
||||
{
|
||||
private readonly ISearchForNewArtist _searchProxy;
|
||||
|
||||
public ArtistLookupModule(ISearchForNewArtist searchProxy)
|
||||
: base("/artist/lookup")
|
||||
public ArtistLookupController(ISearchForNewArtist searchProxy)
|
||||
{
|
||||
_searchProxy = searchProxy;
|
||||
Get("/", x => Search());
|
||||
}
|
||||
|
||||
private object Search()
|
||||
[HttpGet]
|
||||
public object Search([FromQuery] string term)
|
||||
{
|
||||
var searchResults = _searchProxy.SearchForNewArtist((string)Request.Query.term);
|
||||
var searchResults = _searchProxy.SearchForNewArtist(term);
|
||||
return MapToResource(searchResults).ToList();
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.Extensions;
|
||||
using Lidarr.Http.REST.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace Lidarr.Api.V1.Blacklist
|
||||
{
|
||||
[V1ApiController]
|
||||
public class BlacklistController : Controller
|
||||
{
|
||||
private readonly IBlacklistService _blacklistService;
|
||||
|
||||
public BlacklistController(IBlacklistService blacklistService)
|
||||
{
|
||||
_blacklistService = blacklistService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public PagingResource<BlacklistResource> GetBlacklist()
|
||||
{
|
||||
var pagingResource = Request.ReadPagingResourceFromRequest<BlacklistResource>();
|
||||
var pagingSpec = pagingResource.MapToPagingSpec<BlacklistResource, NzbDrone.Core.Blacklisting.Blacklist>("date", SortDirection.Descending);
|
||||
|
||||
return pagingSpec.ApplyToPage(_blacklistService.Paged, BlacklistResourceMapper.MapToResource);
|
||||
}
|
||||
|
||||
[RestDeleteById]
|
||||
public void DeleteBlacklist(int id)
|
||||
{
|
||||
_blacklistService.Delete(id);
|
||||
}
|
||||
|
||||
[HttpDelete("bulk")]
|
||||
public object Remove([FromBody] BlacklistBulkResource resource)
|
||||
{
|
||||
_blacklistService.Delete(resource.Ids);
|
||||
|
||||
return new object();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.Extensions;
|
||||
using NzbDrone.Core.Blacklisting;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace Lidarr.Api.V1.Blacklist
|
||||
{
|
||||
public class BlacklistModule : LidarrRestModule<BlacklistResource>
|
||||
{
|
||||
private readonly IBlacklistService _blacklistService;
|
||||
|
||||
public BlacklistModule(IBlacklistService blacklistService)
|
||||
{
|
||||
_blacklistService = blacklistService;
|
||||
GetResourcePaged = GetBlacklist;
|
||||
DeleteResource = DeleteBlacklist;
|
||||
|
||||
Delete("/bulk", x => Remove());
|
||||
}
|
||||
|
||||
private PagingResource<BlacklistResource> GetBlacklist(PagingResource<BlacklistResource> pagingResource)
|
||||
{
|
||||
var pagingSpec = pagingResource.MapToPagingSpec<BlacklistResource, NzbDrone.Core.Blacklisting.Blacklist>("date", SortDirection.Descending);
|
||||
|
||||
return ApplyToPage(_blacklistService.Paged, pagingSpec, BlacklistResourceMapper.MapToResource);
|
||||
}
|
||||
|
||||
private void DeleteBlacklist(int id)
|
||||
{
|
||||
_blacklistService.Delete(id);
|
||||
}
|
||||
|
||||
private object Remove()
|
||||
{
|
||||
var resource = Request.Body.FromJson<BlacklistBulkResource>();
|
||||
|
||||
_blacklistService.Delete(resource.Ids);
|
||||
|
||||
return new object();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Api.V1.Albums;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.ArtistStats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace Lidarr.Api.V1.Calendar
|
||||
{
|
||||
[V1ApiController]
|
||||
public class CalendarController : AlbumControllerWithSignalR
|
||||
{
|
||||
public CalendarController(IAlbumService albumService,
|
||||
IArtistStatisticsService artistStatisticsService,
|
||||
IMapCoversToLocal coverMapper,
|
||||
IUpgradableSpecification upgradableSpecification,
|
||||
IBroadcastSignalRMessage signalRBroadcaster)
|
||||
: base(albumService, artistStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public List<AlbumResource> GetCalendar(DateTime? start, DateTime? end, bool unmonitored = false, bool includeArtist = false)
|
||||
{
|
||||
//TODO: Add Album Image support to AlbumControllerWithSignalR
|
||||
var includeAlbumImages = Request.GetBooleanQueryParameter("includeAlbumImages");
|
||||
|
||||
var startUse = start ?? DateTime.Today;
|
||||
var endUse = end ?? DateTime.Today.AddDays(2);
|
||||
|
||||
var resources = MapToResource(_albumService.AlbumsBetweenDates(startUse, endUse, unmonitored), includeArtist);
|
||||
|
||||
return resources.OrderBy(e => e.ReleaseDate).ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Api.V1.Albums;
|
||||
using Lidarr.Http.Extensions;
|
||||
using NzbDrone.Core.ArtistStats;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.MediaCover;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace Lidarr.Api.V1.Calendar
|
||||
{
|
||||
public class CalendarModule : AlbumModuleWithSignalR
|
||||
{
|
||||
public CalendarModule(IAlbumService albumService,
|
||||
IArtistStatisticsService artistStatisticsService,
|
||||
IMapCoversToLocal coverMapper,
|
||||
IUpgradableSpecification upgradableSpecification,
|
||||
IBroadcastSignalRMessage signalRBroadcaster)
|
||||
: base(albumService, artistStatisticsService, coverMapper, upgradableSpecification, signalRBroadcaster, "calendar")
|
||||
{
|
||||
GetResourceAll = GetCalendar;
|
||||
}
|
||||
|
||||
private List<AlbumResource> GetCalendar()
|
||||
{
|
||||
var start = DateTime.Today;
|
||||
var end = DateTime.Today.AddDays(2);
|
||||
var includeUnmonitored = Request.GetBooleanQueryParameter("unmonitored");
|
||||
var includeArtist = Request.GetBooleanQueryParameter("includeArtist");
|
||||
|
||||
//TODO: Add Album Image support to AlbumModuleWithSignalR
|
||||
var includeAlbumImages = Request.GetBooleanQueryParameter("includeAlbumImages");
|
||||
|
||||
var queryStart = Request.Query.Start;
|
||||
var queryEnd = Request.Query.End;
|
||||
|
||||
if (queryStart.HasValue)
|
||||
{
|
||||
start = DateTime.Parse(queryStart.Value);
|
||||
}
|
||||
|
||||
if (queryEnd.HasValue)
|
||||
{
|
||||
end = DateTime.Parse(queryEnd.Value);
|
||||
}
|
||||
|
||||
var resources = MapToResource(_albumService.AlbumsBetweenDates(start, end, includeUnmonitored), includeArtist);
|
||||
|
||||
return resources.OrderBy(e => e.ReleaseDate).ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Lidarr.Http.REST;
|
||||
using Lidarr.Http.REST.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Lidarr.Api.V1.Config
|
||||
{
|
||||
public abstract class ConfigController<TResource> : RestController<TResource>
|
||||
where TResource : RestResource, new()
|
||||
{
|
||||
private readonly IConfigService _configService;
|
||||
|
||||
protected ConfigController(IConfigService configService)
|
||||
{
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
public override TResource GetResourceById(int id)
|
||||
{
|
||||
return GetConfig();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public TResource GetConfig()
|
||||
{
|
||||
var resource = ToResource(_configService);
|
||||
resource.Id = 1;
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
[RestPutById]
|
||||
public ActionResult<TResource> SaveConfig(TResource resource)
|
||||
{
|
||||
var dictionary = resource.GetType()
|
||||
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
||||
.ToDictionary(prop => prop.Name, prop => prop.GetValue(resource, null));
|
||||
|
||||
_configService.SaveConfigDictionary(dictionary);
|
||||
|
||||
return Accepted(resource.Id);
|
||||
}
|
||||
|
||||
protected abstract TResource ToResource(IConfigService model);
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
using Lidarr.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Lidarr.Api.V1.Config
|
||||
{
|
||||
public class DownloadClientConfigModule : LidarrConfigModule<DownloadClientConfigResource>
|
||||
[V1ApiController("config/downloadclient")]
|
||||
public class DownloadClientConfigController : ConfigController<DownloadClientConfigResource>
|
||||
{
|
||||
public DownloadClientConfigModule(IConfigService configService)
|
||||
public DownloadClientConfigController(IConfigService configService)
|
||||
: base(configService)
|
||||
{
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
using FluentValidation;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.Validation;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Lidarr.Api.V1.Config
|
||||
{
|
||||
public class IndexerConfigModule : LidarrConfigModule<IndexerConfigResource>
|
||||
[V1ApiController("config/indexer")]
|
||||
public class IndexerConfigController : ConfigController<IndexerConfigResource>
|
||||
{
|
||||
public IndexerConfigModule(IConfigService configService)
|
||||
public IndexerConfigController(IConfigService configService)
|
||||
: base(configService)
|
||||
{
|
||||
SharedValidator.RuleFor(c => c.MinimumAge)
|
@ -1,53 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Lidarr.Api.V1.Config
|
||||
{
|
||||
public abstract class LidarrConfigModule<TResource> : LidarrRestModule<TResource>
|
||||
where TResource : RestResource, new()
|
||||
{
|
||||
private readonly IConfigService _configService;
|
||||
|
||||
protected LidarrConfigModule(IConfigService configService)
|
||||
: this(new TResource().ResourceName.Replace("config", ""), configService)
|
||||
{
|
||||
}
|
||||
|
||||
protected LidarrConfigModule(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,13 +1,15 @@
|
||||
using FluentValidation;
|
||||
using Lidarr.Http;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace Lidarr.Api.V1.Config
|
||||
{
|
||||
public class MetadataProviderConfigModule : LidarrConfigModule<MetadataProviderConfigResource>
|
||||
[V1ApiController("config/metadataprovider")]
|
||||
public class MetadataProviderConfigController : ConfigController<MetadataProviderConfigResource>
|
||||
{
|
||||
public MetadataProviderConfigModule(IConfigService configService)
|
||||
public MetadataProviderConfigController(IConfigService configService)
|
||||
: base(configService)
|
||||
{
|
||||
SharedValidator.RuleFor(c => c.MetadataSource).IsValidUrl().When(c => !c.MetadataSource.IsNullOrWhiteSpace());
|
@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using Lidarr.Http.REST.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.CustomFilters;
|
||||
|
||||
namespace Lidarr.Api.V1.CustomFilters
|
||||
{
|
||||
[V1ApiController]
|
||||
public class CustomFilterController : RestController<CustomFilterResource>
|
||||
{
|
||||
private readonly ICustomFilterService _customFilterService;
|
||||
|
||||
public CustomFilterController(ICustomFilterService customFilterService)
|
||||
{
|
||||
_customFilterService = customFilterService;
|
||||
}
|
||||
|
||||
public override CustomFilterResource GetResourceById(int id)
|
||||
{
|
||||
return _customFilterService.Get(id).ToResource();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public List<CustomFilterResource> GetCustomFilters()
|
||||
{
|
||||
return _customFilterService.All().ToResource();
|
||||
}
|
||||
|
||||
[RestPostById]
|
||||
public ActionResult<CustomFilterResource> AddCustomFilter(CustomFilterResource resource)
|
||||
{
|
||||
var customFilter = _customFilterService.Add(resource.ToModel());
|
||||
|
||||
return Created(customFilter.Id);
|
||||
}
|
||||
|
||||
[RestPutById]
|
||||
public ActionResult<CustomFilterResource> UpdateCustomFilter(CustomFilterResource resource)
|
||||
{
|
||||
_customFilterService.Update(resource.ToModel());
|
||||
return Accepted(resource.Id);
|
||||
}
|
||||
|
||||
[RestDeleteById]
|
||||
public void DeleteCustomResource(int id)
|
||||
{
|
||||
_customFilterService.Delete(id);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Lidarr.Http;
|
||||
using NzbDrone.Core.CustomFilters;
|
||||
|
||||
namespace Lidarr.Api.V1.CustomFilters
|
||||
{
|
||||
public class CustomFilterModule : LidarrRestModule<CustomFilterResource>
|
||||
{
|
||||
private readonly ICustomFilterService _customFilterService;
|
||||
|
||||
public CustomFilterModule(ICustomFilterService customFilterService)
|
||||
{
|
||||
_customFilterService = customFilterService;
|
||||
|
||||
GetResourceById = GetCustomFilter;
|
||||
GetResourceAll = GetCustomFilters;
|
||||
CreateResource = AddCustomFilter;
|
||||
UpdateResource = UpdateCustomFilter;
|
||||
DeleteResource = DeleteCustomResource;
|
||||
}
|
||||
|
||||
private CustomFilterResource GetCustomFilter(int id)
|
||||
{
|
||||
return _customFilterService.Get(id).ToResource();
|
||||
}
|
||||
|
||||
private List<CustomFilterResource> GetCustomFilters()
|
||||
{
|
||||
return _customFilterService.All().ToResource();
|
||||
}
|
||||
|
||||
private int AddCustomFilter(CustomFilterResource resource)
|
||||
{
|
||||
var customFilter = _customFilterService.Add(resource.ToModel());
|
||||
|
||||
return customFilter.Id;
|
||||
}
|
||||
|
||||
private void UpdateCustomFilter(CustomFilterResource resource)
|
||||
{
|
||||
_customFilterService.Update(resource.ToModel());
|
||||
}
|
||||
|
||||
private void DeleteCustomResource(int id)
|
||||
{
|
||||
_customFilterService.Delete(id);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
using Lidarr.Http;
|
||||
using NzbDrone.Core.ImportLists;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
|
||||
namespace Lidarr.Api.V1.ImportLists
|
||||
{
|
||||
public class ImportListModule : ProviderModuleBase<ImportListResource, IImportList, ImportListDefinition>
|
||||
[V1ApiController]
|
||||
public class ImportListController : ProviderControllerBase<ImportListResource, IImportList, ImportListDefinition>
|
||||
{
|
||||
public static readonly ImportListResourceMapper ResourceMapper = new ImportListResourceMapper();
|
||||
|
||||
public ImportListModule(ImportListFactory importListFactory,
|
||||
public ImportListController(ImportListFactory importListFactory,
|
||||
QualityProfileExistsValidator qualityProfileExistsValidator,
|
||||
MetadataProfileExistsValidator metadataProfileExistsValidator)
|
||||
: base(importListFactory, "importlist", ResourceMapper)
|
@ -1,54 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using Lidarr.Http.REST.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.ImportLists.Exclusions;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace Lidarr.Api.V1.ImportLists
|
||||
{
|
||||
public class ImportListExclusionModule : LidarrRestModule<ImportListExclusionResource>
|
||||
[V1ApiController]
|
||||
public class ImportListExclusionController : RestController<ImportListExclusionResource>
|
||||
{
|
||||
private readonly IImportListExclusionService _importListExclusionService;
|
||||
|
||||
public ImportListExclusionModule(IImportListExclusionService importListExclusionService,
|
||||
public ImportListExclusionController(IImportListExclusionService importListExclusionService,
|
||||
ImportListExclusionExistsValidator importListExclusionExistsValidator,
|
||||
GuidValidator guidValidator)
|
||||
{
|
||||
_importListExclusionService = importListExclusionService;
|
||||
|
||||
GetResourceById = GetImportListExclusion;
|
||||
GetResourceAll = GetImportListExclusions;
|
||||
CreateResource = AddImportListExclusion;
|
||||
UpdateResource = UpdateImportListExclusion;
|
||||
DeleteResource = DeleteImportListExclusionResource;
|
||||
|
||||
SharedValidator.RuleFor(c => c.ForeignId).NotEmpty().SetValidator(guidValidator).SetValidator(importListExclusionExistsValidator);
|
||||
SharedValidator.RuleFor(c => c.ArtistName).NotEmpty();
|
||||
}
|
||||
|
||||
private ImportListExclusionResource GetImportListExclusion(int id)
|
||||
public override ImportListExclusionResource GetResourceById(int id)
|
||||
{
|
||||
return _importListExclusionService.Get(id).ToResource();
|
||||
}
|
||||
|
||||
private List<ImportListExclusionResource> GetImportListExclusions()
|
||||
[HttpGet]
|
||||
public List<ImportListExclusionResource> GetImportListExclusions()
|
||||
{
|
||||
return _importListExclusionService.All().ToResource();
|
||||
}
|
||||
|
||||
private int AddImportListExclusion(ImportListExclusionResource resource)
|
||||
[RestPostById]
|
||||
public ActionResult<ImportListExclusionResource> AddImportListExclusion(ImportListExclusionResource resource)
|
||||
{
|
||||
var customFilter = _importListExclusionService.Add(resource.ToModel());
|
||||
|
||||
return customFilter.Id;
|
||||
return Created(customFilter.Id);
|
||||
}
|
||||
|
||||
private void UpdateImportListExclusion(ImportListExclusionResource resource)
|
||||
[RestPutById]
|
||||
public ActionResult<ImportListExclusionResource> UpdateImportListExclusion(ImportListExclusionResource resource)
|
||||
{
|
||||
_importListExclusionService.Update(resource.ToModel());
|
||||
return Accepted(resource.Id);
|
||||
}
|
||||
|
||||
private void DeleteImportListExclusionResource(int id)
|
||||
[RestDeleteById]
|
||||
public void DeleteImportListExclusionResource(int id)
|
||||
{
|
||||
_importListExclusionService.Delete(id);
|
||||
}
|
@ -1,11 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
|
||||
namespace Lidarr.Api.V1.Indexers
|
||||
{
|
||||
public abstract class ReleaseModuleBase : LidarrRestModule<ReleaseResource>
|
||||
public abstract class ReleaseControllerBase : RestController<ReleaseResource>
|
||||
{
|
||||
public override ReleaseResource GetResourceById(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected virtual List<ReleaseResource> MapDecisions(IEnumerable<DownloadDecision> decisions)
|
||||
{
|
||||
var result = new List<ReleaseResource>();
|
@ -1,12 +0,0 @@
|
||||
using Lidarr.Http;
|
||||
|
||||
namespace Lidarr.Api.V1
|
||||
{
|
||||
public abstract class LidarrV1FeedModule : LidarrModule
|
||||
{
|
||||
protected LidarrV1FeedModule(string resource)
|
||||
: base("/feed/v1/" + resource.Trim('/'))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
using Lidarr.Http;
|
||||
|
||||
namespace Lidarr.Api.V1
|
||||
{
|
||||
public abstract class LidarrV1Module : LidarrModule
|
||||
{
|
||||
protected LidarrV1Module(string resource)
|
||||
: base("/api/v1/" + resource.Trim('/'))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +1,24 @@
|
||||
using Lidarr.Api.V1.Albums;
|
||||
using Lidarr.Api.V1.Artist;
|
||||
using Lidarr.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace Lidarr.Api.V1.Parse
|
||||
{
|
||||
public class ParseModule : LidarrRestModule<ParseResource>
|
||||
[V1ApiController]
|
||||
public class ParseController : Controller
|
||||
{
|
||||
private readonly IParsingService _parsingService;
|
||||
|
||||
public ParseModule(IParsingService parsingService)
|
||||
public ParseController(IParsingService parsingService)
|
||||
{
|
||||
_parsingService = parsingService;
|
||||
|
||||
GetResourceSingle = Parse;
|
||||
}
|
||||
|
||||
private ParseResource Parse()
|
||||
[HttpGet]
|
||||
public ParseResource Parse(string title)
|
||||
{
|
||||
var title = Request.Query.Title.Value as string;
|
||||
var parsedAlbumInfo = Parser.ParseAlbumTitle(title);
|
||||
|
||||
if (parsedAlbumInfo == null)
|
@ -1,54 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using Lidarr.Http.REST.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Profiles.Metadata;
|
||||
|
||||
namespace Lidarr.Api.V1.Profiles.Metadata
|
||||
{
|
||||
public class MetadataProfileModule : LidarrRestModule<MetadataProfileResource>
|
||||
[V1ApiController]
|
||||
public class MetadataProfileController : RestController<MetadataProfileResource>
|
||||
{
|
||||
private readonly IMetadataProfileService _profileService;
|
||||
|
||||
public MetadataProfileModule(IMetadataProfileService profileService)
|
||||
public MetadataProfileController(IMetadataProfileService profileService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
|
||||
SharedValidator.RuleFor(c => c.Name).NotEqual("None").WithMessage("'None' is a reserved profile name").NotEmpty();
|
||||
SharedValidator.RuleFor(c => c.PrimaryAlbumTypes).MustHaveAllowedPrimaryType();
|
||||
SharedValidator.RuleFor(c => c.SecondaryAlbumTypes).MustHaveAllowedSecondaryType();
|
||||
SharedValidator.RuleFor(c => c.ReleaseStatuses).MustHaveAllowedReleaseStatus();
|
||||
|
||||
GetResourceAll = GetAll;
|
||||
GetResourceById = GetById;
|
||||
UpdateResource = Update;
|
||||
CreateResource = Create;
|
||||
DeleteResource = DeleteProfile;
|
||||
}
|
||||
|
||||
private int Create(MetadataProfileResource resource)
|
||||
[RestPostById]
|
||||
public ActionResult<MetadataProfileResource> Create(MetadataProfileResource resource)
|
||||
{
|
||||
var model = resource.ToModel();
|
||||
model = _profileService.Add(model);
|
||||
return model.Id;
|
||||
return Created(model.Id);
|
||||
}
|
||||
|
||||
private void DeleteProfile(int id)
|
||||
[RestDeleteById]
|
||||
public void DeleteProfile(int id)
|
||||
{
|
||||
_profileService.Delete(id);
|
||||
}
|
||||
|
||||
private void Update(MetadataProfileResource resource)
|
||||
[RestPutById]
|
||||
public ActionResult<MetadataProfileResource> Update(MetadataProfileResource resource)
|
||||
{
|
||||
var model = resource.ToModel();
|
||||
|
||||
_profileService.Update(model);
|
||||
|
||||
return Accepted(model.Id);
|
||||
}
|
||||
|
||||
private MetadataProfileResource GetById(int id)
|
||||
public override MetadataProfileResource GetResourceById(int id)
|
||||
{
|
||||
return _profileService.Get(id).ToResource();
|
||||
}
|
||||
|
||||
private List<MetadataProfileResource> GetAll()
|
||||
[HttpGet]
|
||||
public List<MetadataProfileResource> GetAll()
|
||||
{
|
||||
var profiles = _profileService.All().ToResource();
|
||||
|
@ -1,18 +1,15 @@
|
||||
using System.Linq;
|
||||
using Lidarr.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Profiles.Metadata;
|
||||
|
||||
namespace Lidarr.Api.V1.Profiles.Metadata
|
||||
{
|
||||
public class MetadataProfileSchemaModule : LidarrRestModule<MetadataProfileResource>
|
||||
[V1ApiController("metadataprofile/schema")]
|
||||
public class MetadataProfileSchemaController : Controller
|
||||
{
|
||||
public MetadataProfileSchemaModule()
|
||||
: base("/metadataprofile/schema")
|
||||
{
|
||||
GetResourceSingle = GetAll;
|
||||
}
|
||||
|
||||
private MetadataProfileResource GetAll()
|
||||
[HttpGet]
|
||||
public MetadataProfileResource GetAll()
|
||||
{
|
||||
var orderedPrimTypes = NzbDrone.Core.Music.PrimaryAlbumType.All
|
||||
.OrderByDescending(l => l.Id)
|
@ -1,53 +1,57 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using Lidarr.Http.REST.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Profiles.Qualities;
|
||||
|
||||
namespace Lidarr.Api.V1.Profiles.Quality
|
||||
{
|
||||
public class ProfileModule : LidarrRestModule<QualityProfileResource>
|
||||
[V1ApiController]
|
||||
public class QualityProfileController : RestController<QualityProfileResource>
|
||||
{
|
||||
private readonly IProfileService _profileService;
|
||||
|
||||
public ProfileModule(IProfileService profileService)
|
||||
public QualityProfileController(IProfileService profileService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||
SharedValidator.RuleFor(c => c.Cutoff).ValidCutoff();
|
||||
SharedValidator.RuleFor(c => c.Items).ValidItems();
|
||||
|
||||
GetResourceAll = GetAll;
|
||||
GetResourceById = GetById;
|
||||
UpdateResource = Update;
|
||||
CreateResource = Create;
|
||||
DeleteResource = DeleteProfile;
|
||||
}
|
||||
|
||||
private int Create(QualityProfileResource resource)
|
||||
[RestPostById]
|
||||
public ActionResult<QualityProfileResource> Create(QualityProfileResource resource)
|
||||
{
|
||||
var model = resource.ToModel();
|
||||
model = _profileService.Add(model);
|
||||
return model.Id;
|
||||
return Created(model.Id);
|
||||
}
|
||||
|
||||
private void DeleteProfile(int id)
|
||||
[RestDeleteById]
|
||||
public void DeleteProfile(int id)
|
||||
{
|
||||
_profileService.Delete(id);
|
||||
}
|
||||
|
||||
private void Update(QualityProfileResource resource)
|
||||
[RestPutById]
|
||||
public ActionResult<QualityProfileResource> Update(QualityProfileResource resource)
|
||||
{
|
||||
var model = resource.ToModel();
|
||||
|
||||
_profileService.Update(model);
|
||||
|
||||
return Accepted(model.Id);
|
||||
}
|
||||
|
||||
private QualityProfileResource GetById(int id)
|
||||
public override QualityProfileResource GetResourceById(int id)
|
||||
{
|
||||
return _profileService.Get(id).ToResource();
|
||||
}
|
||||
|
||||
private List<QualityProfileResource> GetAll()
|
||||
[HttpGet]
|
||||
public List<QualityProfileResource> GetAll()
|
||||
{
|
||||
return _profileService.All().ToResource();
|
||||
}
|
@ -1,20 +1,21 @@
|
||||
using Lidarr.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Profiles.Qualities;
|
||||
|
||||
namespace Lidarr.Api.V1.Profiles.Quality
|
||||
{
|
||||
public class QualityProfileSchemaModule : LidarrRestModule<QualityProfileResource>
|
||||
[V1ApiController("qualityprofile/schema")]
|
||||
public class QualityProfileSchemaController : Controller
|
||||
{
|
||||
private readonly IProfileService _profileService;
|
||||
|
||||
public QualityProfileSchemaModule(IProfileService profileService)
|
||||
: base("/qualityprofile/schema")
|
||||
public QualityProfileSchemaController(IProfileService profileService)
|
||||
{
|
||||
_profileService = profileService;
|
||||
GetResourceSingle = GetSchema;
|
||||
}
|
||||
|
||||
private QualityProfileResource GetSchema()
|
||||
[HttpGet]
|
||||
public QualityProfileResource GetSchema()
|
||||
{
|
||||
QualityProfile qualityProfile = _profileService.GetDefaultProfile(string.Empty);
|
||||
|
@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using Lidarr.Http.REST.Attributes;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Qualities;
|
||||
|
||||
namespace Lidarr.Api.V1.Qualities
|
||||
{
|
||||
[V1ApiController]
|
||||
public class QualityDefinitionController : RestController<QualityDefinitionResource>
|
||||
{
|
||||
private readonly IQualityDefinitionService _qualityDefinitionService;
|
||||
|
||||
public QualityDefinitionController(IQualityDefinitionService qualityDefinitionService)
|
||||
{
|
||||
_qualityDefinitionService = qualityDefinitionService;
|
||||
}
|
||||
|
||||
[RestPutById]
|
||||
public ActionResult<QualityDefinitionResource> Update(QualityDefinitionResource resource)
|
||||
{
|
||||
var model = resource.ToModel();
|
||||
_qualityDefinitionService.Update(model);
|
||||
return Accepted(model.Id);
|
||||
}
|
||||
|
||||
public override QualityDefinitionResource GetResourceById(int id)
|
||||
{
|
||||
return _qualityDefinitionService.GetById(id).ToResource();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public List<QualityDefinitionResource> GetAll()
|
||||
{
|
||||
return _qualityDefinitionService.All().ToResource();
|
||||
}
|
||||
|
||||
[HttpPut("update")]
|
||||
public object UpdateMany([FromBody] List<QualityDefinitionResource> resource)
|
||||
{
|
||||
//Read from request
|
||||
var qualityDefinitions = resource
|
||||
.ToModel()
|
||||
.ToList();
|
||||
|
||||
_qualityDefinitionService.UpdateMany(qualityDefinitions);
|
||||
|
||||
return Accepted(_qualityDefinitionService.All()
|
||||
.ToResource());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
|
||||
namespace Lidarr.Api.V1.Queue
|
||||
{
|
||||
[V1ApiController("queue")]
|
||||
public class QueueActionController : Controller
|
||||
{
|
||||
private readonly IPendingReleaseService _pendingReleaseService;
|
||||
private readonly IDownloadService _downloadService;
|
||||
|
||||
public QueueActionController(IPendingReleaseService pendingReleaseService,
|
||||
IDownloadService downloadService)
|
||||
{
|
||||
_pendingReleaseService = pendingReleaseService;
|
||||
_downloadService = downloadService;
|
||||
}
|
||||
|
||||
[HttpPost("grab/{id:int}")]
|
||||
public object Grab(int id)
|
||||
{
|
||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
||||
|
||||
if (pendingRelease == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
_downloadService.DownloadReport(pendingRelease.RemoteAlbum);
|
||||
|
||||
return new object();
|
||||
}
|
||||
|
||||
[HttpPost("grab/bulk")]
|
||||
public object Grab([FromBody] QueueBulkResource resource)
|
||||
{
|
||||
foreach (var id in resource.Ids)
|
||||
{
|
||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
||||
|
||||
if (pendingRelease == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
_downloadService.DownloadReport(pendingRelease.RemoteAlbum);
|
||||
}
|
||||
|
||||
return new object();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.Extensions;
|
||||
using Lidarr.Http.REST;
|
||||
using Nancy;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.Queue;
|
||||
|
||||
namespace Lidarr.Api.V1.Queue
|
||||
{
|
||||
public class QueueActionModule : LidarrRestModule<QueueResource>
|
||||
{
|
||||
private readonly IQueueService _queueService;
|
||||
private readonly ITrackedDownloadService _trackedDownloadService;
|
||||
private readonly IFailedDownloadService _failedDownloadService;
|
||||
private readonly IIgnoredDownloadService _ignoredDownloadService;
|
||||
private readonly IProvideDownloadClient _downloadClientProvider;
|
||||
private readonly IPendingReleaseService _pendingReleaseService;
|
||||
private readonly IDownloadService _downloadService;
|
||||
|
||||
public QueueActionModule(IQueueService queueService,
|
||||
ITrackedDownloadService trackedDownloadService,
|
||||
IFailedDownloadService failedDownloadService,
|
||||
IIgnoredDownloadService ignoredDownloadService,
|
||||
IProvideDownloadClient downloadClientProvider,
|
||||
IPendingReleaseService pendingReleaseService,
|
||||
IDownloadService downloadService)
|
||||
{
|
||||
_queueService = queueService;
|
||||
_trackedDownloadService = trackedDownloadService;
|
||||
_failedDownloadService = failedDownloadService;
|
||||
_ignoredDownloadService = ignoredDownloadService;
|
||||
_downloadClientProvider = downloadClientProvider;
|
||||
_pendingReleaseService = pendingReleaseService;
|
||||
_downloadService = downloadService;
|
||||
|
||||
Post(@"/grab/(?<id>[\d]{1,10})", x => Grab((int)x.Id));
|
||||
Post("/grab/bulk", x => Grab());
|
||||
|
||||
Delete(@"/(?<id>[\d]{1,10})", x => Remove((int)x.Id));
|
||||
Delete("/bulk", x => Remove());
|
||||
}
|
||||
|
||||
private object Grab(int id)
|
||||
{
|
||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
||||
|
||||
if (pendingRelease == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
_downloadService.DownloadReport(pendingRelease.RemoteAlbum);
|
||||
|
||||
return new object();
|
||||
}
|
||||
|
||||
private object Grab()
|
||||
{
|
||||
var resource = Request.Body.FromJson<QueueBulkResource>();
|
||||
|
||||
foreach (var id in resource.Ids)
|
||||
{
|
||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
||||
|
||||
if (pendingRelease == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
_downloadService.DownloadReport(pendingRelease.RemoteAlbum);
|
||||
}
|
||||
|
||||
return new object();
|
||||
}
|
||||
|
||||
private object Remove(int id)
|
||||
{
|
||||
var removeFromClient = Request.GetBooleanQueryParameter("removeFromClient", true);
|
||||
var blacklist = Request.GetBooleanQueryParameter("blacklist");
|
||||
var skipReDownload = Request.GetBooleanQueryParameter("skipredownload");
|
||||
|
||||
var trackedDownload = Remove(id, removeFromClient, blacklist, skipReDownload);
|
||||
|
||||
if (trackedDownload != null)
|
||||
{
|
||||
_trackedDownloadService.StopTracking(trackedDownload.DownloadItem.DownloadId);
|
||||
}
|
||||
|
||||
return new object();
|
||||
}
|
||||
|
||||
private object Remove()
|
||||
{
|
||||
var removeFromClient = Request.GetBooleanQueryParameter("removeFromClient", true);
|
||||
var blacklist = Request.GetBooleanQueryParameter("blacklist");
|
||||
var skipReDownload = Request.GetBooleanQueryParameter("skipredownload");
|
||||
|
||||
var resource = Request.Body.FromJson<QueueBulkResource>();
|
||||
var trackedDownloadIds = new List<string>();
|
||||
|
||||
foreach (var id in resource.Ids)
|
||||
{
|
||||
var trackedDownload = Remove(id, removeFromClient, blacklist, skipReDownload);
|
||||
|
||||
if (trackedDownload != null)
|
||||
{
|
||||
trackedDownloadIds.Add(trackedDownload.DownloadItem.DownloadId);
|
||||
}
|
||||
}
|
||||
|
||||
_trackedDownloadService.StopTracking(trackedDownloadIds);
|
||||
|
||||
return new object();
|
||||
}
|
||||
|
||||
private TrackedDownload Remove(int id, bool removeFromClient, bool blacklist, bool skipReDownload)
|
||||
{
|
||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
||||
|
||||
if (pendingRelease != null)
|
||||
{
|
||||
_pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var trackedDownload = GetTrackedDownload(id);
|
||||
|
||||
if (trackedDownload == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (removeFromClient)
|
||||
{
|
||||
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, skipReDownload);
|
||||
}
|
||||
|
||||
if (!removeFromClient && !blacklist)
|
||||
{
|
||||
if (!_ignoredDownloadService.IgnoreDownload(trackedDownload))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return trackedDownload;
|
||||
}
|
||||
|
||||
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,28 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Tags;
|
||||
|
||||
namespace Lidarr.Api.V1.Tags
|
||||
{
|
||||
public class TagDetailsModule : LidarrRestModule<TagDetailsResource>
|
||||
[V1ApiController("tag/detail")]
|
||||
public class TagDetailsController : RestController<TagDetailsResource>
|
||||
{
|
||||
private readonly ITagService _tagService;
|
||||
|
||||
public TagDetailsModule(ITagService tagService)
|
||||
: base("/tag/detail")
|
||||
public TagDetailsController(ITagService tagService)
|
||||
{
|
||||
_tagService = tagService;
|
||||
|
||||
GetResourceById = GetTagDetails;
|
||||
GetResourceAll = GetAll;
|
||||
}
|
||||
|
||||
private TagDetailsResource GetTagDetails(int id)
|
||||
public override TagDetailsResource GetResourceById(int id)
|
||||
{
|
||||
return _tagService.Details(id).ToResource();
|
||||
}
|
||||
|
||||
private List<TagDetailsResource> GetAll()
|
||||
[HttpGet]
|
||||
public List<TagDetailsResource> GetAll()
|
||||
{
|
||||
var tags = _tagService.Details().ToResource();
|
||||
|
@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using Lidarr.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
||||
namespace Lidarr.Api.V1.Tracks
|
||||
{
|
||||
[V1ApiController("rename")]
|
||||
public class RenameTrackController : Controller
|
||||
{
|
||||
private readonly IRenameTrackFileService _renameTrackFileService;
|
||||
|
||||
public RenameTrackController(IRenameTrackFileService renameTrackFileService)
|
||||
{
|
||||
_renameTrackFileService = renameTrackFileService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public List<RenameTrackResource> GetTracks(int artistId, int? albumId)
|
||||
{
|
||||
if (albumId.HasValue)
|
||||
{
|
||||
return _renameTrackFileService.GetRenamePreviews(artistId, albumId.Value).ToResource();
|
||||
}
|
||||
|
||||
return _renameTrackFileService.GetRenamePreviews(artistId).ToResource();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
||||
namespace Lidarr.Api.V1.Tracks
|
||||
{
|
||||
public class RenameTrackModule : LidarrRestModule<RenameTrackResource>
|
||||
{
|
||||
private readonly IRenameTrackFileService _renameTrackFileService;
|
||||
|
||||
public RenameTrackModule(IRenameTrackFileService renameTrackFileService)
|
||||
: base("rename")
|
||||
{
|
||||
_renameTrackFileService = renameTrackFileService;
|
||||
|
||||
GetResourceAll = GetTracks;
|
||||
}
|
||||
|
||||
private List<RenameTrackResource> GetTracks()
|
||||
{
|
||||
int artistId;
|
||||
|
||||
if (Request.Query.ArtistId.HasValue)
|
||||
{
|
||||
artistId = (int)Request.Query.ArtistId;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BadRequestException("artistId is missing");
|
||||
}
|
||||
|
||||
if (Request.Query.albumId.HasValue)
|
||||
{
|
||||
var albumId = (int)Request.Query.albumId;
|
||||
return _renameTrackFileService.GetRenamePreviews(artistId, albumId).ToResource();
|
||||
}
|
||||
|
||||
return _renameTrackFileService.GetRenamePreviews(artistId).ToResource();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
||||
namespace Lidarr.Api.V1.Tracks
|
||||
{
|
||||
[V1ApiController("retag")]
|
||||
public class RetagTrackController : Controller
|
||||
{
|
||||
private readonly IAudioTagService _audioTagService;
|
||||
|
||||
public RetagTrackController(IAudioTagService audioTagService)
|
||||
{
|
||||
_audioTagService = audioTagService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public List<RetagTrackResource> GetTracks(int artistId, int? albumId)
|
||||
{
|
||||
if (albumId.HasValue)
|
||||
{
|
||||
return _audioTagService.GetRetagPreviewsByAlbum(albumId.Value).Where(x => x.Changes.Any()).ToResource();
|
||||
}
|
||||
|
||||
return _audioTagService.GetRetagPreviewsByArtist(artistId).Where(x => x.Changes.Any()).ToResource();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
|
||||
namespace Lidarr.Api.V1.Tracks
|
||||
{
|
||||
public class RetagTrackModule : LidarrRestModule<RetagTrackResource>
|
||||
{
|
||||
private readonly IAudioTagService _audioTagService;
|
||||
|
||||
public RetagTrackModule(IAudioTagService audioTagService)
|
||||
: base("retag")
|
||||
{
|
||||
_audioTagService = audioTagService;
|
||||
|
||||
GetResourceAll = GetTracks;
|
||||
}
|
||||
|
||||
private List<RetagTrackResource> GetTracks()
|
||||
{
|
||||
if (Request.Query.albumId.HasValue)
|
||||
{
|
||||
var albumId = (int)Request.Query.albumId;
|
||||
return _audioTagService.GetRetagPreviewsByAlbum(albumId).Where(x => x.Changes.Any()).ToResource();
|
||||
}
|
||||
else if (Request.Query.ArtistId.HasValue)
|
||||
{
|
||||
var artistId = (int)Request.Query.ArtistId;
|
||||
return _audioTagService.GetRetagPreviewsByArtist(artistId).Where(x => x.Changes.Any()).ToResource();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BadRequestException("One of artistId or albumId must be specified");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Http;
|
||||
using Lidarr.Http.REST;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace Lidarr.Api.V1.Tracks
|
||||
{
|
||||
[V1ApiController]
|
||||
public class TrackController : TrackControllerWithSignalR
|
||||
{
|
||||
public TrackController(IArtistService artistService,
|
||||
ITrackService trackService,
|
||||
IUpgradableSpecification upgradableSpecification,
|
||||
IBroadcastSignalRMessage signalRBroadcaster)
|
||||
: base(trackService, artistService, upgradableSpecification, signalRBroadcaster)
|
||||
{
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public List<TrackResource> GetTracks([FromQuery]int? artistId,
|
||||
[FromQuery]int? albumId,
|
||||
[FromQuery]int? albumReleaseId,
|
||||
[FromQuery]List<int> trackIds)
|
||||
{
|
||||
if (!artistId.HasValue && !trackIds.Any() && !albumId.HasValue && !albumReleaseId.HasValue)
|
||||
{
|
||||
throw new BadRequestException("One of artistId, albumId, albumReleaseId or trackIds must be provided");
|
||||
}
|
||||
|
||||
if (artistId.HasValue && !albumId.HasValue)
|
||||
{
|
||||
return MapToResource(_trackService.GetTracksByArtist(artistId.Value), false, false);
|
||||
}
|
||||
|
||||
if (albumReleaseId.HasValue)
|
||||
{
|
||||
return MapToResource(_trackService.GetTracksByRelease(albumReleaseId.Value), false, false);
|
||||
}
|
||||
|
||||
if (albumId.HasValue)
|
||||
{
|
||||
return MapToResource(_trackService.GetTracksByAlbum(albumId.Value), false, false);
|
||||
}
|
||||
|
||||
return MapToResource(_trackService.GetTracks(trackIds), false, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lidarr.Http.REST;
|
||||
using Nancy;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.SignalR;
|
||||
|
||||
namespace Lidarr.Api.V1.Tracks
|
||||
{
|
||||
public class TrackModule : TrackModuleWithSignalR
|
||||
{
|
||||
public TrackModule(IArtistService artistService,
|
||||
ITrackService trackService,
|
||||
IUpgradableSpecification upgradableSpecification,
|
||||
IBroadcastSignalRMessage signalRBroadcaster)
|
||||
: base(trackService, artistService, upgradableSpecification, signalRBroadcaster)
|
||||
{
|
||||
GetResourceAll = GetTracks;
|
||||
}
|
||||
|
||||
private List<TrackResource> GetTracks()
|
||||
{
|
||||
var artistIdQuery = Request.Query.ArtistId;
|
||||
var albumIdQuery = Request.Query.AlbumId;
|
||||
var albumReleaseIdQuery = Request.Query.AlbumReleaseId;
|
||||
var trackIdsQuery = Request.Query.TrackIds;
|
||||
|
||||
if (!artistIdQuery.HasValue && !trackIdsQuery.HasValue && !albumIdQuery.HasValue && !albumReleaseIdQuery.HasValue)
|
||||
{
|
||||
throw new BadRequestException("One of artistId, albumId, albumReleaseId or trackIds must be provided");
|
||||
}
|
||||
|
||||
if (artistIdQuery.HasValue && !albumIdQuery.HasValue)
|
||||
{
|
||||
int artistId = Convert.ToInt32(artistIdQuery.Value);
|
||||
|
||||
return MapToResource(_trackService.GetTracksByArtist(artistId), false, false);
|
||||
}
|
||||
|
||||
if (albumReleaseIdQuery.HasValue)
|
||||
{
|
||||
int releaseId = Convert.ToInt32(albumReleaseIdQuery.Value);
|
||||
|
||||
return MapToResource(_trackService.GetTracksByRelease(releaseId), false, false);
|
||||
}
|
||||
|
||||
if (albumIdQuery.HasValue)
|
||||
{
|
||||
int albumId = Convert.ToInt32(albumIdQuery.Value);
|
||||
|
||||
return MapToResource(_trackService.GetTracksByAlbum(albumId), false, false);
|
||||
}
|
||||
|
||||
string trackIdsValue = trackIdsQuery.Value.ToString();
|
||||
|
||||
var trackIds = trackIdsValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(e => Convert.ToInt32(e))
|
||||
.ToList();
|
||||
|
||||
return MapToResource(_trackService.GetTracks(trackIds), false, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Lidarr.Http.Authentication
|
||||
{
|
||||
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
|
||||
{
|
||||
public const string DefaultScheme = "API Key";
|
||||
public string Scheme => DefaultScheme;
|
||||
public string AuthenticationType = DefaultScheme;
|
||||
|
||||
public string HeaderName { get; set; }
|
||||
public string QueryName { get; set; }
|
||||
public string ApiKey { get; set; }
|
||||
}
|
||||
|
||||
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
|
||||
{
|
||||
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock)
|
||||
: base(options, logger, encoder, clock)
|
||||
{
|
||||
}
|
||||
|
||||
private string ParseApiKey()
|
||||
{
|
||||
// Try query parameter
|
||||
if (Request.Query.TryGetValue(Options.QueryName, out var value))
|
||||
{
|
||||
return value.FirstOrDefault();
|
||||
}
|
||||
|
||||
// No ApiKey query parameter found try headers
|
||||
if (Request.Headers.TryGetValue(Options.HeaderName, out var headerValue))
|
||||
{
|
||||
return headerValue.FirstOrDefault();
|
||||
}
|
||||
|
||||
return Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ", "");
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
var providedApiKey = ParseApiKey();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(providedApiKey))
|
||||
{
|
||||
return Task.FromResult(AuthenticateResult.NoResult());
|
||||
}
|
||||
|
||||
if (Options.ApiKey == providedApiKey)
|
||||
{
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim("ApiKey", "true")
|
||||
};
|
||||
|
||||
var identity = new ClaimsIdentity(claims, Options.AuthenticationType);
|
||||
var identities = new List<ClaimsIdentity> { identity };
|
||||
var principal = new ClaimsPrincipal(identities);
|
||||
var ticket = new AuthenticationTicket(principal, Options.Scheme);
|
||||
|
||||
return Task.FromResult(AuthenticateResult.Success(ticket));
|
||||
}
|
||||
|
||||
return Task.FromResult(AuthenticateResult.NoResult());
|
||||
}
|
||||
|
||||
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
|
||||
{
|
||||
Response.StatusCode = 401;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Lidarr.Http.Authentication
|
||||
{
|
||||
public static class AuthenticationBuilderExtensions
|
||||
{
|
||||
public static AuthenticationBuilder AddApiKey(this AuthenticationBuilder authenticationBuilder, string name, Action<ApiKeyAuthenticationOptions> options)
|
||||
{
|
||||
return authenticationBuilder.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(name, options);
|
||||
}
|
||||
|
||||
public static AuthenticationBuilder AddBasicAuthentication(this AuthenticationBuilder authenticationBuilder)
|
||||
{
|
||||
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>(AuthenticationType.Basic.ToString(), options => { });
|
||||
}
|
||||
|
||||
public static AuthenticationBuilder AddNoAuthentication(this AuthenticationBuilder authenticationBuilder)
|
||||
{
|
||||
return authenticationBuilder.AddScheme<AuthenticationSchemeOptions, NoAuthenticationHandler>(AuthenticationType.None.ToString(), options => { });
|
||||
}
|
||||
|
||||
public static AuthenticationBuilder AddAppAuthentication(this IServiceCollection services, IConfigFileProvider config)
|
||||
{
|
||||
var authBuilder = services.AddAuthentication(config.AuthenticationMethod.ToString());
|
||||
|
||||
if (config.AuthenticationMethod == AuthenticationType.Basic)
|
||||
{
|
||||
authBuilder.AddBasicAuthentication();
|
||||
}
|
||||
else if (config.AuthenticationMethod == AuthenticationType.Forms)
|
||||
{
|
||||
authBuilder.AddCookie(AuthenticationType.Forms.ToString(), options =>
|
||||
{
|
||||
options.AccessDeniedPath = "/login?loginFailed=true";
|
||||
options.LoginPath = "/login";
|
||||
options.ExpireTimeSpan = TimeSpan.FromDays(7);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
authBuilder.AddNoAuthentication();
|
||||
}
|
||||
|
||||
authBuilder.AddApiKey("API", options =>
|
||||
{
|
||||
options.HeaderName = "X-Api-Key";
|
||||
options.QueryName = "apikey";
|
||||
options.ApiKey = config.ApiKey;
|
||||
});
|
||||
|
||||
authBuilder.AddApiKey("SignalR", options =>
|
||||
{
|
||||
options.HeaderName = "X-Api-Key";
|
||||
options.QueryName = "access_token";
|
||||
options.ApiKey = config.ApiKey;
|
||||
});
|
||||
|
||||
return authBuilder;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Lidarr.Http.Authentication
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
public class AuthenticationController : Controller
|
||||
{
|
||||
private readonly IAuthenticationService _authService;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public AuthenticationController(IAuthenticationService authService, IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_authService = authService;
|
||||
_configFileProvider = configFileProvider;
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromForm] LoginResource resource, [FromQuery] string returnUrl = null)
|
||||
{
|
||||
var user = _authService.Login(HttpContext.Request, resource.Username, resource.Password);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return Redirect($"~/login?returnUrl={returnUrl}&loginFailed=true");
|
||||
}
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim("user", user.Username),
|
||||
new Claim("identifier", user.Identifier.ToString()),
|
||||
new Claim("UiAuth", "true")
|
||||
};
|
||||
|
||||
var authProperties = new AuthenticationProperties
|
||||
{
|
||||
IsPersistent = resource.RememberMe == "on"
|
||||
};
|
||||
await HttpContext.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(claims, "Cookies", "user", "identifier")), authProperties);
|
||||
|
||||
return Redirect("/");
|
||||
}
|
||||
|
||||
[HttpGet("logout")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
_authService.Logout(HttpContext);
|
||||
await HttpContext.SignOutAsync();
|
||||
return Redirect("/");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
using System;
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Extensions;
|
||||
using Nancy.ModelBinding;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Lidarr.Http.Authentication
|
||||
{
|
||||
public class AuthenticationModule : NancyModule
|
||||
{
|
||||
private readonly IAuthenticationService _authService;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
|
||||
public AuthenticationModule(IAuthenticationService authService, IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_authService = authService;
|
||||
_configFileProvider = configFileProvider;
|
||||
Post("/login", x => Login(this.Bind<LoginResource>()));
|
||||
Get("/logout", x => Logout());
|
||||
}
|
||||
|
||||
private Response Login(LoginResource resource)
|
||||
{
|
||||
var user = _authService.Login(Context, resource.Username, resource.Password);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
var returnUrl = (string)Request.Query.returnUrl;
|
||||
return Context.GetRedirect($"~/login?returnUrl={returnUrl}&loginFailed=true");
|
||||
}
|
||||
|
||||
DateTime? expiry = null;
|
||||
|
||||
if (resource.RememberMe)
|
||||
{
|
||||
expiry = DateTime.UtcNow.AddDays(7);
|
||||
}
|
||||
|
||||
return this.LoginAndRedirect(user.Identifier, expiry, _configFileProvider.UrlBase + "/");
|
||||
}
|
||||
|
||||
private Response Logout()
|
||||
{
|
||||
_authService.Logout(Context);
|
||||
|
||||
return this.LogoutAndRedirect(_configFileProvider.UrlBase + "/");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
|
||||
namespace Lidarr.Http.Authentication
|
||||
{
|
||||
public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
private readonly IAuthenticationService _authService;
|
||||
|
||||
public BasicAuthenticationHandler(IAuthenticationService authService,
|
||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock)
|
||||
: base(options, logger, encoder, clock)
|
||||
{
|
||||
_authService = authService;
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
if (!Request.Headers.ContainsKey("Authorization"))
|
||||
{
|
||||
return Task.FromResult(AuthenticateResult.Fail("Authorization header missing."));
|
||||
}
|
||||
|
||||
// Get authorization key
|
||||
var authorizationHeader = Request.Headers["Authorization"].ToString();
|
||||
var authHeaderRegex = new Regex(@"Basic (.*)");
|
||||
|
||||
if (!authHeaderRegex.IsMatch(authorizationHeader))
|
||||
{
|
||||
return Task.FromResult(AuthenticateResult.Fail("Authorization code not formatted properly."));
|
||||
}
|
||||
|
||||
var authBase64 = Encoding.UTF8.GetString(Convert.FromBase64String(authHeaderRegex.Replace(authorizationHeader, "$1")));
|
||||
var authSplit = authBase64.Split(':', 2);
|
||||
var authUsername = authSplit[0];
|
||||
var authPassword = authSplit.Length > 1 ? authSplit[1] : throw new Exception("Unable to get password");
|
||||
|
||||
var user = _authService.Login(Request, authUsername, authPassword);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return Task.FromResult(AuthenticateResult.Fail("The username or password is not correct."));
|
||||
}
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim("user", user.Username),
|
||||
new Claim("identifier", user.Identifier.ToString()),
|
||||
new Claim("UiAuth", "true")
|
||||
};
|
||||
|
||||
var identity = new ClaimsIdentity(claims, "Basic", "user", "identifier");
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
var ticket = new AuthenticationTicket(principal, "Basic");
|
||||
|
||||
return Task.FromResult(AuthenticateResult.Success(ticket));
|
||||
}
|
||||
|
||||
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
|
||||
{
|
||||
Response.Headers.Add("WWW-Authenticate", $"Basic realm=\"{BuildInfo.AppName}\"");
|
||||
Response.StatusCode = 401;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Lidarr.Http.Extensions;
|
||||
using Lidarr.Http.Extensions.Pipelines;
|
||||
using Nancy;
|
||||
using Nancy.Authentication.Basic;
|
||||
using Nancy.Authentication.Forms;
|
||||
using Nancy.Bootstrapper;
|
||||
using Nancy.Cryptography;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Authentication;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace Lidarr.Http.Authentication
|
||||
{
|
||||
public class EnableAuthInNancy : IRegisterNancyPipeline
|
||||
{
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private FormsAuthenticationConfiguration _formsAuthConfig;
|
||||
|
||||
public EnableAuthInNancy(IAuthenticationService authenticationService,
|
||||
IConfigService configService,
|
||||
IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_authenticationService = authenticationService;
|
||||
_configService = configService;
|
||||
_configFileProvider = configFileProvider;
|
||||
}
|
||||
|
||||
public int Order => 10;
|
||||
|
||||
public void Register(IPipelines pipelines)
|
||||
{
|
||||
if (_configFileProvider.AuthenticationMethod == AuthenticationType.Forms)
|
||||
{
|
||||
RegisterFormsAuth(pipelines);
|
||||
pipelines.AfterRequest.AddItemToEndOfPipeline(SlidingAuthenticationForFormsAuth);
|
||||
}
|
||||
else if (_configFileProvider.AuthenticationMethod == AuthenticationType.Basic)
|
||||
{
|
||||
pipelines.EnableBasicAuthentication(new BasicAuthenticationConfiguration(_authenticationService, BuildInfo.AppName));
|
||||
pipelines.BeforeRequest.AddItemToStartOfPipeline(CaptureContext);
|
||||
}
|
||||
|
||||
pipelines.BeforeRequest.AddItemToEndOfPipeline(RequiresAuthentication);
|
||||
pipelines.AfterRequest.AddItemToEndOfPipeline(RemoveLoginHooksForApiCalls);
|
||||
}
|
||||
|
||||
private Response CaptureContext(NancyContext context)
|
||||
{
|
||||
_authenticationService.SetContext(context);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Response RequiresAuthentication(NancyContext context)
|
||||
{
|
||||
Response response = null;
|
||||
|
||||
if (!_authenticationService.IsAuthenticated(context))
|
||||
{
|
||||
_authenticationService.LogUnauthorized(context);
|
||||
response = new Response { StatusCode = HttpStatusCode.Unauthorized };
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private void RegisterFormsAuth(IPipelines pipelines)
|
||||
{
|
||||
FormsAuthentication.FormsAuthenticationCookieName = "LidarrAuth";
|
||||
|
||||
var cryptographyConfiguration = new CryptographyConfiguration(
|
||||
new AesEncryptionProvider(new PassphraseKeyGenerator(_configService.RijndaelPassphrase, Encoding.ASCII.GetBytes(_configService.RijndaelSalt))),
|
||||
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase, Encoding.ASCII.GetBytes(_configService.HmacSalt))));
|
||||
|
||||
_formsAuthConfig = new FormsAuthenticationConfiguration
|
||||
{
|
||||
RedirectUrl = _configFileProvider.UrlBase + "/login",
|
||||
UserMapper = _authenticationService,
|
||||
Path = GetCookiePath(),
|
||||
CryptographyConfiguration = cryptographyConfiguration
|
||||
};
|
||||
|
||||
FormsAuthentication.Enable(pipelines, _formsAuthConfig);
|
||||
}
|
||||
|
||||
private void RemoveLoginHooksForApiCalls(NancyContext context)
|
||||
{
|
||||
if (context.Request.IsApiRequest())
|
||||
{
|
||||
if ((context.Response.StatusCode == HttpStatusCode.SeeOther &&
|
||||
context.Response.Headers["Location"].StartsWith($"{_configFileProvider.UrlBase}/login", StringComparison.InvariantCultureIgnoreCase)) ||
|
||||
context.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
context.Response = new { Error = "Unauthorized" }.AsResponse(context, HttpStatusCode.Unauthorized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SlidingAuthenticationForFormsAuth(NancyContext context)
|
||||
{
|
||||
if (context.CurrentUser == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var formsAuthCookieName = FormsAuthentication.FormsAuthenticationCookieName;
|
||||
|
||||
if (!context.Request.Path.Equals("/logout") &&
|
||||
context.Request.Cookies.ContainsKey(formsAuthCookieName))
|
||||
{
|
||||
var formsAuthCookieValue = context.Request.Cookies[formsAuthCookieName];
|
||||
|
||||
if (FormsAuthentication.DecryptAndValidateAuthenticationCookie(formsAuthCookieValue, _formsAuthConfig).IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var formsAuthCookie = new LidarrNancyCookie(formsAuthCookieName, formsAuthCookieValue, true, false, DateTime.UtcNow.AddDays(7))
|
||||
{
|
||||
Path = GetCookiePath()
|
||||
};
|
||||
|
||||
context.Response.WithCookie(formsAuthCookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetCookiePath()
|
||||
{
|
||||
var urlBase = _configFileProvider.UrlBase;
|
||||
|
||||
if (urlBase.IsNullOrWhiteSpace())
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
|
||||
return urlBase;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using Nancy.Cookies;
|
||||
|
||||
namespace Lidarr.Http.Authentication
|
||||
{
|
||||
public class LidarrNancyCookie : NancyCookie
|
||||
{
|
||||
public LidarrNancyCookie(string name, string value)
|
||||
: base(name, value)
|
||||
{
|
||||
}
|
||||
|
||||
public LidarrNancyCookie(string name, string value, DateTime expires)
|
||||
: base(name, value, expires)
|
||||
{
|
||||
}
|
||||
|
||||
public LidarrNancyCookie(string name, string value, bool httpOnly)
|
||||
: base(name, value, httpOnly)
|
||||
{
|
||||
}
|
||||
|
||||
public LidarrNancyCookie(string name, string value, bool httpOnly, bool secure)
|
||||
: base(name, value, httpOnly, secure)
|
||||
{
|
||||
}
|
||||
|
||||
public LidarrNancyCookie(string name, string value, bool httpOnly, bool secure, DateTime? expires)
|
||||
: base(name, value, httpOnly, secure, expires)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString() + "; SameSite=Lax";
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue