mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-09 04:22:30 +01:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
42c980c905
@ -115,9 +115,7 @@ Radarr is currently undergoing rapid development and pull requests are actively
|
||||
* Open `NzbDrone.sln` in Visual Studio or run the build.sh script, if Mono is installed
|
||||
* Make sure `NzbDrone.Console` is set as the startup project
|
||||
|
||||
## Sponsors
|
||||
### License
|
||||
|
||||
Thanks to [JetBrains](http://www.jetbrains.com) for providing us with free licenses to their great tools:
|
||||
* [ReSharper](http://www.jetbrains.com/resharper)
|
||||
* [WebStorm](http://www.jetbrains.com/webstorm)
|
||||
* [TeamCity](http://www.jetbrains.com/teamcity)
|
||||
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
|
||||
* Copyright 2010-2017
|
||||
|
@ -25,7 +25,7 @@ gulp.task('copyHtml', function () {
|
||||
});
|
||||
|
||||
gulp.task('copyContent', function () {
|
||||
return gulp.src([paths.src.content + '**/*.*', '!**/*.less'])
|
||||
return gulp.src([paths.src.content + '**/*.*', '!**/*.less', '!**/*.css'])
|
||||
.pipe(gulp.dest(paths.dest.content))
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
@ -16,6 +16,10 @@ gulp.task('less', function() {
|
||||
paths.src.content + 'bootstrap.less',
|
||||
paths.src.content + 'theme.less',
|
||||
paths.src.content + 'overrides.less',
|
||||
paths.src.content + 'bootstrap.toggle-switch.css',
|
||||
paths.src.content + 'fullcalendar.css',
|
||||
paths.src.content + 'Messenger/messenger.css',
|
||||
paths.src.content + 'Messenger/messenger.flat.css',
|
||||
paths.src.root + 'Series/series.less',
|
||||
paths.src.root + 'Activity/activity.less',
|
||||
paths.src.root + 'AddSeries/addSeries.less',
|
||||
|
46
src/NzbDrone.Api/Extensions/Pipelines/UrlBasePipeline.cs
Normal file
46
src/NzbDrone.Api/Extensions/Pipelines/UrlBasePipeline.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Nancy;
|
||||
using Nancy.Bootstrapper;
|
||||
using Nancy.Responses;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Api.Extensions.Pipelines
|
||||
{
|
||||
public class UrlBasePipeline : IRegisterNancyPipeline
|
||||
{
|
||||
private readonly string _urlBase;
|
||||
|
||||
public UrlBasePipeline(IConfigFileProvider configFileProvider)
|
||||
{
|
||||
_urlBase = configFileProvider.UrlBase;
|
||||
}
|
||||
|
||||
public int Order => 99;
|
||||
|
||||
public void Register(IPipelines pipelines)
|
||||
{
|
||||
if (_urlBase.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
pipelines.BeforeRequest.AddItemToStartOfPipeline((Func<NancyContext, Response>) Handle);
|
||||
}
|
||||
}
|
||||
|
||||
private Response Handle(NancyContext context)
|
||||
{
|
||||
var basePath = context.Request.Url.BasePath;
|
||||
|
||||
if (basePath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return new RedirectResponse($"{_urlBase}{context.Request.Path}{context.Request.Url.Query}");
|
||||
}
|
||||
|
||||
if (_urlBase != basePath)
|
||||
{
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nancy.Responses;
|
||||
@ -38,20 +38,6 @@ private Response Index()
|
||||
return new NotFoundResponse();
|
||||
}
|
||||
|
||||
//Redirect to the subfolder if the request went to the base URL
|
||||
if (path.Equals("/"))
|
||||
{
|
||||
var urlBase = _configFileProvider.UrlBase;
|
||||
|
||||
if (!string.IsNullOrEmpty(urlBase))
|
||||
{
|
||||
if (Request.Url.BasePath != urlBase)
|
||||
{
|
||||
return new RedirectResponse(urlBase + "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var mapper = _requestMappers.SingleOrDefault(m => m.CanHandle(path));
|
||||
|
||||
if (mapper != null)
|
||||
|
@ -113,6 +113,7 @@
|
||||
<Compile Include="Config\NetImportConfigResource.cs" />
|
||||
<Compile Include="Extensions\AccessControlHeaders.cs" />
|
||||
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
|
||||
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />
|
||||
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
|
||||
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
|
||||
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />
|
||||
|
@ -0,0 +1,111 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications.Search;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.TorrentRss;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests.Search
|
||||
{
|
||||
[TestFixture]
|
||||
public class TorrentSeedingSpecificationFixture : TestBase<TorrentSeedingSpecification>
|
||||
{
|
||||
private Series _series;
|
||||
private RemoteEpisode _remoteEpisode;
|
||||
private IndexerDefinition _indexerDefinition;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>.CreateNew().With(s => s.Id = 1).Build();
|
||||
|
||||
_remoteEpisode = new RemoteEpisode
|
||||
{
|
||||
Series = _series,
|
||||
Release = new TorrentInfo
|
||||
{
|
||||
IndexerId = 1,
|
||||
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp",
|
||||
Seeders = 0
|
||||
}
|
||||
};
|
||||
|
||||
_indexerDefinition = new IndexerDefinition
|
||||
{
|
||||
Settings = new TorrentRssIndexerSettings { MinimumSeeders = 5 }
|
||||
};
|
||||
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(v => v.Get(1))
|
||||
.Returns(_indexerDefinition);
|
||||
|
||||
}
|
||||
|
||||
private void GivenReleaseSeeders(int? seeders)
|
||||
{
|
||||
(_remoteEpisode.Release as TorrentInfo).Seeders = seeders;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_not_torrent()
|
||||
{
|
||||
_remoteEpisode.Release = new ReleaseInfo
|
||||
{
|
||||
IndexerId = 1,
|
||||
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp"
|
||||
};
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_indexer_not_specified()
|
||||
{
|
||||
_remoteEpisode.Release.IndexerId = 0;
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_indexer_no_longer_exists()
|
||||
{
|
||||
Mocker.GetMock<IIndexerFactory>()
|
||||
.Setup(v => v.Get(It.IsAny<int>()))
|
||||
.Callback<int>(i => { throw new ModelNotFoundException(typeof(IndexerDefinition), i); });
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_seeds_unknown()
|
||||
{
|
||||
GivenReleaseSeeders(null);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase(5)]
|
||||
[TestCase(6)]
|
||||
public void should_return_true_if_seeds_above_or_equal_to_limit(int seeders)
|
||||
{
|
||||
GivenReleaseSeeders(seeders);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase(0)]
|
||||
[TestCase(4)]
|
||||
public void should_return_false_if_seeds_belove_limit(int seeders)
|
||||
{
|
||||
GivenReleaseSeeders(seeders);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ public void Setup()
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
{
|
||||
Name = "IPTorrents",
|
||||
Settings = new IPTorrentsSettings() { Url = "http://fake.com/" }
|
||||
Settings = new IPTorrentsSettings() { BaseUrl = "http://fake.com/" }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ public void SetUp()
|
||||
{
|
||||
_settings = new NewznabSettings()
|
||||
{
|
||||
Url = "http://indxer.local"
|
||||
BaseUrl = "http://indxer.local"
|
||||
};
|
||||
|
||||
_caps = ReadAllText("Files/Indexers/Newznab/newznab_caps.xml");
|
||||
|
@ -24,7 +24,7 @@ public void Setup()
|
||||
Name = "Newznab",
|
||||
Settings = new NewznabSettings()
|
||||
{
|
||||
Url = "http://indexer.local/",
|
||||
BaseUrl = "http://indexer.local/",
|
||||
Categories = new int[] { 1 }
|
||||
}
|
||||
};
|
||||
|
@ -19,7 +19,7 @@ public void SetUp()
|
||||
{
|
||||
Subject.Settings = new NewznabSettings()
|
||||
{
|
||||
Url = "http://127.0.0.1:1234/",
|
||||
BaseUrl = "http://127.0.0.1:1234/",
|
||||
Categories = new [] { 1, 2 },
|
||||
AnimeCategories = new [] { 3, 4 },
|
||||
ApiKey = "abcd",
|
||||
|
@ -15,7 +15,7 @@ public void requires_apikey(string url)
|
||||
var setting = new NewznabSettings()
|
||||
{
|
||||
ApiKey = "",
|
||||
Url = url
|
||||
BaseUrl = url
|
||||
};
|
||||
|
||||
|
||||
@ -32,13 +32,13 @@ public void invalid_url_should_not_apikey(string url)
|
||||
var setting = new NewznabSettings
|
||||
{
|
||||
ApiKey = "",
|
||||
Url = url
|
||||
BaseUrl = url
|
||||
};
|
||||
|
||||
|
||||
setting.Validate().IsValid.Should().BeFalse();
|
||||
setting.Validate().Errors.Should().NotContain(c => c.PropertyName == "ApiKey");
|
||||
setting.Validate().Errors.Should().Contain(c => c.PropertyName == "Url");
|
||||
setting.Validate().Errors.Should().Contain(c => c.PropertyName == "BaseUrl");
|
||||
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ public void doesnt_requires_apikey(string url)
|
||||
var setting = new NewznabSettings()
|
||||
{
|
||||
ApiKey = "",
|
||||
Url = url
|
||||
BaseUrl = url
|
||||
};
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ public void Setup()
|
||||
Name = "Torznab",
|
||||
Settings = new TorznabSettings()
|
||||
{
|
||||
Url = "http://indexer.local/",
|
||||
BaseUrl = "http://indexer.local/",
|
||||
Categories = new int[] { 1 }
|
||||
}
|
||||
};
|
||||
|
@ -159,6 +159,7 @@
|
||||
<Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\Search\TorrentSeedingSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\SameEpisodesSpecificationFixture.cs" />
|
||||
<Compile Include="DecisionEngineTests\RawDiskSpecificationFixture.cs" />
|
||||
|
@ -1,55 +0,0 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
{
|
||||
public class TorrentSeedingSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
|
||||
public TorrentSeedingSpecification(Logger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var torrentInfo = remoteEpisode.Release as TorrentInfo;
|
||||
|
||||
if (torrentInfo == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (torrentInfo.Seeders != null && torrentInfo.Seeders < 1)
|
||||
{
|
||||
_logger.Debug("Not enough seeders. ({0})", torrentInfo.Seeders);
|
||||
return Decision.Reject("Not enough seeders. ({0})", torrentInfo.Seeders);
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var torrentInfo = remoteEpisode.Release as TorrentInfo;
|
||||
|
||||
if (torrentInfo == null)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
if (torrentInfo.Seeders != null && torrentInfo.Seeders < 1)
|
||||
{
|
||||
_logger.Debug("Not enough seeders. ({0})", torrentInfo.Seeders);
|
||||
return Decision.Reject("Not enough seeders. ({0})", torrentInfo.Seeders);
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
using NLog;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
|
||||
{
|
||||
public class TorrentSeedingSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public TorrentSeedingSpecification(IIndexerFactory indexerFactory, Logger logger)
|
||||
{
|
||||
_indexerFactory = indexerFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
//public SpecificationPriority Priority => SpecificationPriority.Default;
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var torrentInfo = remoteEpisode.Release as TorrentInfo;
|
||||
|
||||
if (torrentInfo == null || torrentInfo.IndexerId == 0)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
IndexerDefinition indexer;
|
||||
try
|
||||
{
|
||||
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
|
||||
}
|
||||
catch (ModelNotFoundException)
|
||||
{
|
||||
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
|
||||
|
||||
if (torrentIndexerSettings != null)
|
||||
{
|
||||
var minimumSeeders = torrentIndexerSettings.MinimumSeeders;
|
||||
|
||||
if (torrentInfo.Seeders.HasValue && torrentInfo.Seeders.Value < minimumSeeders)
|
||||
{
|
||||
_logger.Debug("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
|
||||
return Decision.Reject("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteMovie remoteEpisode, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var torrentInfo = remoteEpisode.Release as TorrentInfo;
|
||||
|
||||
if (torrentInfo == null || torrentInfo.IndexerId == 0)
|
||||
{
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
IndexerDefinition indexer;
|
||||
try
|
||||
{
|
||||
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
|
||||
}
|
||||
catch (ModelNotFoundException)
|
||||
{
|
||||
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
|
||||
|
||||
if (torrentIndexerSettings != null)
|
||||
{
|
||||
var minimumSeeders = torrentIndexerSettings.MinimumSeeders;
|
||||
|
||||
if (torrentInfo.Seeders.HasValue && torrentInfo.Seeders.Value < minimumSeeders)
|
||||
{
|
||||
_logger.Debug("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
|
||||
return Decision.Reject("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
@ -14,13 +14,14 @@ public AwesomeHDSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class AwesomeHDSettings : IProviderConfig
|
||||
public class AwesomeHDSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly AwesomeHDSettingsValidator Validator = new AwesomeHDSettingsValidator();
|
||||
|
||||
public AwesomeHDSettings()
|
||||
{
|
||||
BaseUrl = "https://awesome-hd.me";
|
||||
MinimumSeeders = 0;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "API URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since you Passkey will be sent to that host.")]
|
||||
@ -32,6 +33,9 @@ public AwesomeHDSettings()
|
||||
[FieldDefinition(2, Type = FieldType.Checkbox, Label = "Require Internal", HelpText = "Will only include internal releases for RSS Sync.")]
|
||||
public bool Internal { get; set; }
|
||||
|
||||
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
using System.Linq.Expressions;
|
||||
using FluentValidation.Results;
|
||||
@ -19,13 +18,14 @@ public HDBitsSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class HDBitsSettings : IProviderConfig
|
||||
public class HDBitsSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly HDBitsSettingsValidator Validator = new HDBitsSettingsValidator();
|
||||
|
||||
public HDBitsSettings()
|
||||
{
|
||||
BaseUrl = "https://hdbits.org";
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
|
||||
Categories = new int[] { (int)HdBitsCategory.Movie };
|
||||
Codecs = new int[0];
|
||||
@ -56,6 +56,9 @@ public HDBitsSettings()
|
||||
[FieldDefinition(7, Label = "Mediums", Type = FieldType.Tag, SelectOptions = typeof(HdBitsMedium), Advanced = true, HelpText = "Options: BluRay, Encode, Capture, Remux, WebDL. If unspecified, all options are used.")]
|
||||
public IEnumerable<int> Mediums { get; set; }
|
||||
|
||||
[FieldDefinition(8, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
9
src/NzbDrone.Core/Indexers/IIndexerSettings.cs
Normal file
9
src/NzbDrone.Core/Indexers/IIndexerSettings.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public interface IIndexerSettings : IProviderConfig
|
||||
{
|
||||
string BaseUrl { get; set; }
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ public virtual IndexerPageableRequestChain GetSearchRequests(SpecialEpisodeSearc
|
||||
|
||||
private IEnumerable<IndexerRequest> GetRssRequests()
|
||||
{
|
||||
yield return new IndexerRequest(Settings.Url, HttpAccept.Rss);
|
||||
yield return new IndexerRequest(Settings.BaseUrl, HttpAccept.Rss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
@ -11,26 +11,31 @@ public class IPTorrentsSettingsValidator : AbstractValidator<IPTorrentsSettings>
|
||||
{
|
||||
public IPTorrentsSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.Url).ValidRootUrl();
|
||||
RuleFor(c => c.BaseUrl).ValidRootUrl();
|
||||
|
||||
RuleFor(c => c.Url).Matches(@"/rss\?.+$");
|
||||
RuleFor(c => c.BaseUrl).Matches(@"/rss\?.+$");
|
||||
|
||||
RuleFor(c => c.Url).Matches(@"/rss\?.+;download(?:;|$)")
|
||||
RuleFor(c => c.BaseUrl).Matches(@"/rss\?.+;download(?:;|$)")
|
||||
.WithMessage("Use Direct Download Url (;download)")
|
||||
.When(v => v.Url.IsNotNullOrWhiteSpace() && Regex.IsMatch(v.Url, @"/rss\?.+$"));
|
||||
.When(v => v.BaseUrl.IsNotNullOrWhiteSpace() && Regex.IsMatch(v.BaseUrl, @"/rss\?.+$"));
|
||||
}
|
||||
}
|
||||
|
||||
public class IPTorrentsSettings : IProviderConfig
|
||||
public class IPTorrentsSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly IPTorrentsSettingsValidator Validator = new IPTorrentsSettingsValidator();
|
||||
|
||||
public IPTorrentsSettings()
|
||||
{
|
||||
BaseUrl = string.Empty;
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Feed URL", HelpText = "The full RSS feed url generated by IPTorrents, using only the categories you selected (HD, SD, x264, etc ...)")]
|
||||
public string Url { get; set; }
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
|
7
src/NzbDrone.Core/Indexers/ITorrentIndexerSettings.cs
Normal file
7
src/NzbDrone.Core/Indexers/ITorrentIndexerSettings.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public interface ITorrentIndexerSettings : IIndexerSettings
|
||||
{
|
||||
int MinimumSeeders { get; set; }
|
||||
}
|
||||
}
|
7
src/NzbDrone.Core/Indexers/IndexerDefaults.cs
Normal file
7
src/NzbDrone.Core/Indexers/IndexerDefaults.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public static class IndexerDefaults
|
||||
{
|
||||
public const int MINIMUM_SEEDERS = 1;
|
||||
}
|
||||
}
|
@ -76,7 +76,7 @@ private IndexerDefinition GetDefinition(string name, NewznabSettings settings)
|
||||
|
||||
private NewznabSettings GetSettings(string url, params int[] categories)
|
||||
{
|
||||
var settings = new NewznabSettings { Url = url };
|
||||
var settings = new NewznabSettings { BaseUrl = url };
|
||||
|
||||
if (categories.Any())
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ private NewznabCapabilities FetchCapabilities(NewznabSettings indexerSettings)
|
||||
{
|
||||
var capabilities = new NewznabCapabilities();
|
||||
|
||||
var url = string.Format("{0}/api?t=caps", indexerSettings.Url.TrimEnd('/'));
|
||||
var url = string.Format("{0}/api?t=caps", indexerSettings.BaseUrl.TrimEnd('/'));
|
||||
|
||||
if (indexerSettings.ApiKey.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
@ -59,7 +59,7 @@ private NewznabCapabilities FetchCapabilities(NewznabSettings indexerSettings)
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Debug(ex, "Failed to get newznab api capabilities from {0}", indexerSettings.Url);
|
||||
_logger.Debug(ex, "Failed to get newznab api capabilities from {0}", indexerSettings.BaseUrl);
|
||||
throw;
|
||||
}
|
||||
|
||||
@ -69,12 +69,12 @@ private NewznabCapabilities FetchCapabilities(NewznabSettings indexerSettings)
|
||||
}
|
||||
catch (XmlException ex)
|
||||
{
|
||||
_logger.Debug(ex, "Failed to parse newznab api capabilities for {0}.", indexerSettings.Url);
|
||||
_logger.Debug(ex, "Failed to parse newznab api capabilities for {0}.", indexerSettings.BaseUrl);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to determine newznab api capabilities for {0}, using the defaults instead till Sonarr restarts.", indexerSettings.Url);
|
||||
_logger.Error(ex, "Failed to determine newznab api capabilities for {0}, using the defaults instead till Sonarr restarts.", indexerSettings.BaseUrl);
|
||||
}
|
||||
|
||||
return capabilities;
|
||||
|
@ -85,7 +85,7 @@ private IEnumerable<IndexerRequest> GetPagedRequests(int maxPages, IEnumerable<i
|
||||
|
||||
var categoriesQuery = string.Join(",", categories.Distinct());
|
||||
|
||||
var baseUrl = string.Format("{0}/api?t={1}&cat={2}&extended=1{3}", Settings.Url.TrimEnd('/'), searchType, categoriesQuery, Settings.AdditionalParameters);
|
||||
var baseUrl = string.Format("{0}/api?t={1}&cat={2}&extended=1{3}", Settings.BaseUrl.TrimEnd('/'), searchType, categoriesQuery, Settings.AdditionalParameters);
|
||||
|
||||
if (Settings.ApiKey.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
|
@ -25,12 +25,12 @@ public class NewznabSettingsValidator : AbstractValidator<NewznabSettings>
|
||||
|
||||
private static bool ShouldHaveApiKey(NewznabSettings settings)
|
||||
{
|
||||
if (settings.Url == null)
|
||||
if (settings.BaseUrl == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ApiKeyWhiteList.Any(c => settings.Url.ToLowerInvariant().Contains(c));
|
||||
return ApiKeyWhiteList.Any(c => settings.BaseUrl.ToLowerInvariant().Contains(c));
|
||||
}
|
||||
|
||||
private static readonly Regex AdditionalParametersRegex = new Regex(@"(&.+?\=.+?)+", RegexOptions.Compiled);
|
||||
@ -47,14 +47,14 @@ public NewznabSettingsValidator()
|
||||
return null;
|
||||
});
|
||||
|
||||
RuleFor(c => c.Url).ValidRootUrl();
|
||||
RuleFor(c => c.BaseUrl).ValidRootUrl();
|
||||
RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey);
|
||||
RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex)
|
||||
.When(c => !c.AdditionalParameters.IsNullOrWhiteSpace());
|
||||
}
|
||||
}
|
||||
|
||||
public class NewznabSettings : IProviderConfig
|
||||
public class NewznabSettings : IIndexerSettings
|
||||
{
|
||||
private static readonly NewznabSettingsValidator Validator = new NewznabSettingsValidator();
|
||||
|
||||
@ -65,7 +65,7 @@ public NewznabSettings()
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "URL")]
|
||||
public string Url { get; set; }
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "API Key")]
|
||||
public string ApiKey { get; set; }
|
||||
@ -79,6 +79,9 @@ public NewznabSettings()
|
||||
[FieldDefinition(4, Label = "Additional Parameters", HelpText = "Additional Newznab parameters", Advanced = true)]
|
||||
public string AdditionalParameters { get; set; }
|
||||
|
||||
// Field 5 is used by TorznabSettings MinimumSeeders
|
||||
// If you need to add another field here, update TorznabSettings as well and this comment
|
||||
|
||||
public virtual NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
@ -1,6 +1,5 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
using System.Text.RegularExpressions;
|
||||
namespace NzbDrone.Core.Indexers.Nyaa
|
||||
@ -14,7 +13,7 @@ public NyaaSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class NyaaSettings : IProviderConfig
|
||||
public class NyaaSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly NyaaSettingsValidator Validator = new NyaaSettingsValidator();
|
||||
|
||||
@ -22,6 +21,7 @@ public NyaaSettings()
|
||||
{
|
||||
BaseUrl = "http://www.nyaa.se";
|
||||
AdditionalParameters = "&cats=1_37&filter=1";
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Website URL")]
|
||||
@ -30,6 +30,9 @@ public NyaaSettings()
|
||||
[FieldDefinition(1, Label = "Additional Parameters", Advanced = true, HelpText = "Please note if you change the category you will have to add required/restricted rules about the subgroups to avoid foreign language releases.")]
|
||||
public string AdditionalParameters { get; set; }
|
||||
|
||||
[FieldDefinition(2, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
@ -15,7 +15,7 @@ public OmgwtfnzbsSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class OmgwtfnzbsSettings : IProviderConfig
|
||||
public class OmgwtfnzbsSettings : IIndexerSettings
|
||||
{
|
||||
private static readonly OmgwtfnzbsSettingsValidator Validator = new OmgwtfnzbsSettingsValidator();
|
||||
|
||||
@ -24,6 +24,9 @@ public OmgwtfnzbsSettings()
|
||||
Delay = 30;
|
||||
}
|
||||
|
||||
// Unused since Omg has a hardcoded url.
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Username")]
|
||||
public string Username { get; set; }
|
||||
|
||||
|
@ -17,13 +17,14 @@ public PassThePopcornSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class PassThePopcornSettings : IProviderConfig
|
||||
public class PassThePopcornSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly PassThePopcornSettingsValidator Validator = new PassThePopcornSettingsValidator();
|
||||
|
||||
public PassThePopcornSettings()
|
||||
{
|
||||
BaseUrl = "https://passthepopcorn.me";
|
||||
MinimumSeeders = 0;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "URL", Advanced = true, HelpText = "Do not change this unless you know what you're doing. Since your cookie will be sent to that host.")]
|
||||
@ -50,6 +51,9 @@ public PassThePopcornSettings()
|
||||
[FieldDefinition(7, Label = "Require Golden", Type = FieldType.Checkbox, HelpText = "Require Golden Popcorn-releases for releases to be accepted.")]
|
||||
public bool RequireGolden { get; set; }
|
||||
|
||||
[FieldDefinition(8, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
@ -1,6 +1,5 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Rarbg
|
||||
@ -13,7 +12,7 @@ public RarbgSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class RarbgSettings : IProviderConfig
|
||||
public class RarbgSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly RarbgSettingsValidator Validator = new RarbgSettingsValidator();
|
||||
|
||||
@ -21,6 +20,7 @@ public RarbgSettings()
|
||||
{
|
||||
BaseUrl = "https://torrentapi.org";
|
||||
RankedOnly = false;
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "API URL", HelpText = "URL to Rarbg api, not the website.")]
|
||||
@ -32,6 +32,9 @@ public RarbgSettings()
|
||||
[FieldDefinition(2, Type = FieldType.Captcha, Label = "CAPTCHA Token", HelpText = "CAPTCHA Clearance token used to handle CloudFlare Anti-DDOS measures on shared-ip VPNs.")]
|
||||
public string CaptchaToken { get; set; }
|
||||
|
||||
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
@ -13,13 +13,14 @@ public TorrentPotatoSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class TorrentPotatoSettings : IProviderConfig
|
||||
public class TorrentPotatoSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly TorrentPotatoSettingsValidator Validator = new TorrentPotatoSettingsValidator();
|
||||
|
||||
public TorrentPotatoSettings()
|
||||
{
|
||||
BaseUrl = "http://127.0.0.1";
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "API URL", HelpText = "URL to TorrentPotato api.")]
|
||||
@ -30,6 +31,10 @@ public TorrentPotatoSettings()
|
||||
|
||||
[FieldDefinition(2, Label = "Passkey", HelpText = "The password you use at your Indexer.")]
|
||||
public string Passkey { get; set; }
|
||||
|
||||
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
@ -1,6 +1,5 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.TorrentRss
|
||||
@ -13,7 +12,7 @@ public TorrentRssIndexerSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class TorrentRssIndexerSettings : IProviderConfig
|
||||
public class TorrentRssIndexerSettings : ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly TorrentRssIndexerSettingsValidator validator = new TorrentRssIndexerSettingsValidator();
|
||||
|
||||
@ -21,6 +20,7 @@ public TorrentRssIndexerSettings()
|
||||
{
|
||||
BaseUrl = string.Empty;
|
||||
AllowZeroSize = false;
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Full RSS Feed URL")]
|
||||
@ -32,6 +32,9 @@ public TorrentRssIndexerSettings()
|
||||
[FieldDefinition(2, Type = FieldType.Checkbox, Label = "Allow Zero Size", HelpText="Enabling this will allow you to use feeds that don't specify release size, but be careful, size related checks will not be performed.")]
|
||||
public bool AllowZeroSize { get; set; }
|
||||
|
||||
[FieldDefinition(3, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(validator.Validate(this));
|
||||
|
@ -66,7 +66,7 @@ private IndexerDefinition GetDefinition(string name, TorznabSettings settings)
|
||||
|
||||
private TorznabSettings GetSettings(string url, params int[] categories)
|
||||
{
|
||||
var settings = new TorznabSettings { Url = url };
|
||||
var settings = new TorznabSettings { BaseUrl = url };
|
||||
|
||||
if (categories.Any())
|
||||
{
|
||||
|
@ -1,8 +1,9 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Indexers.Newznab;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
@ -17,12 +18,12 @@ public class TorznabSettingsValidator : AbstractValidator<TorznabSettings>
|
||||
|
||||
private static bool ShouldHaveApiKey(TorznabSettings settings)
|
||||
{
|
||||
if (settings.Url == null)
|
||||
if (settings.BaseUrl == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ApiKeyWhiteList.Any(c => settings.Url.ToLowerInvariant().Contains(c));
|
||||
return ApiKeyWhiteList.Any(c => settings.BaseUrl.ToLowerInvariant().Contains(c));
|
||||
}
|
||||
|
||||
private static readonly Regex AdditionalParametersRegex = new Regex(@"(&.+?\=.+?)+", RegexOptions.Compiled);
|
||||
@ -39,17 +40,25 @@ public TorznabSettingsValidator()
|
||||
return null;
|
||||
});
|
||||
|
||||
RuleFor(c => c.Url).ValidRootUrl();
|
||||
RuleFor(c => c.BaseUrl).ValidRootUrl();
|
||||
RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey);
|
||||
RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex)
|
||||
.When(c => !c.AdditionalParameters.IsNullOrWhiteSpace());
|
||||
}
|
||||
}
|
||||
|
||||
public class TorznabSettings : NewznabSettings
|
||||
public class TorznabSettings : NewznabSettings, ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly TorznabSettingsValidator Validator = new TorznabSettingsValidator();
|
||||
|
||||
public TorznabSettings()
|
||||
{
|
||||
MinimumSeeders = IndexerDefaults.MINIMUM_SEEDERS;
|
||||
}
|
||||
|
||||
[FieldDefinition(6, Type = FieldType.Textbox, Label = "Minimum Seeders", HelpText = "Minimum number of seeders required.", Advanced = true)]
|
||||
public int MinimumSeeders { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
@ -368,6 +368,11 @@ private ImportDecision GetDecision(string file, Series series, ParsedEpisodeInfo
|
||||
decision = new ImportDecision(localEpisode, new Rejection("Unexpected error processing file"));
|
||||
}
|
||||
|
||||
if (decision == null)
|
||||
{
|
||||
_logger.Error("Unable to make a decision on {0}", file);
|
||||
}
|
||||
|
||||
return decision;
|
||||
}
|
||||
|
||||
|
@ -160,7 +160,17 @@ private ManualImportItem ProcessFile(string file, string downloadId, string fold
|
||||
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file },
|
||||
movie, null, SceneSource(movie, folder), true);
|
||||
|
||||
return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null;
|
||||
return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : new ManualImportItem
|
||||
{
|
||||
DownloadId = downloadId,
|
||||
Path = file,
|
||||
RelativePath = folder.GetRelativePath(file),
|
||||
Name = Path.GetFileNameWithoutExtension(file),
|
||||
Rejections = new List<Rejection>
|
||||
{
|
||||
new Rejection("Unable to process file")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//private ManualImportItem ProcessFile(string file, string downloadId, string folder = null)
|
||||
|
@ -403,7 +403,7 @@
|
||||
<Compile Include="DecisionEngine\Specifications\Search\SeasonMatchSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\Search\SeriesSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\Search\SingleEpisodeSearchMatchSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\Search\TorrentSeedingSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\TorrentSeedingSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\SameEpisodesGrabSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\RawDiskSpecification.cs" />
|
||||
<Compile Include="DecisionEngine\Specifications\UpgradeDiskSpecification.cs" />
|
||||
@ -691,6 +691,7 @@
|
||||
<Compile Include="Indexers\IIndexer.cs" />
|
||||
<Compile Include="Indexers\IIndexerRequestGenerator.cs" />
|
||||
<Compile Include="Indexers\IndexerBase.cs" />
|
||||
<Compile Include="Indexers\IndexerDefaults.cs" />
|
||||
<Compile Include="Indexers\IndexerDefinition.cs" />
|
||||
<Compile Include="Indexers\IndexerFactory.cs" />
|
||||
<Compile Include="Indexers\IndexerPageableRequest.cs" />
|
||||
@ -706,6 +707,7 @@
|
||||
<Compile Include="Indexers\IPTorrents\IPTorrentsRequestGenerator.cs" />
|
||||
<Compile Include="Indexers\IPTorrents\IPTorrents.cs" />
|
||||
<Compile Include="Indexers\IPTorrents\IPTorrentsSettings.cs" />
|
||||
<Compile Include="Indexers\ITorrentIndexerSettings.cs" />
|
||||
<Compile Include="Indexers\Newznab\Newznab.cs" />
|
||||
<Compile Include="Indexers\Newznab\NewznabCapabilities.cs" />
|
||||
<Compile Include="Indexers\Newznab\NewznabCapabilitiesProvider.cs" />
|
||||
@ -1285,6 +1287,7 @@
|
||||
<Compile Include="NetImport\ImportExclusions\ImportExclusionsRepository.cs" />
|
||||
<Compile Include="NetImport\ImportExclusions\ImportExclusionsService.cs" />
|
||||
<Compile Include="Datastore\Migration\138_add_physical_release_note.cs" />
|
||||
<Compile Include="Indexers\IIndexerSettings.cs" />
|
||||
<Compile Include="NetImport\Radarr\RadarrProxied.cs" />
|
||||
<Compile Include="NetImport\Radarr\RadarrParser.cs" />
|
||||
<Compile Include="NetImport\Radarr\RadarrRequestGenerator.cs" />
|
||||
|
0
src/UI/Content/Bootstrap/.csscomb.json
Normal file → Executable file
0
src/UI/Content/Bootstrap/.csscomb.json
Normal file → Executable file
0
src/UI/Content/Bootstrap/.csslintrc
Normal file → Executable file
0
src/UI/Content/Bootstrap/.csslintrc
Normal file → Executable file
0
src/UI/Content/Bootstrap/alerts.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/alerts.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/badges.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/badges.less
Normal file → Executable file
4
src/UI/Content/Bootstrap/bootstrap.less
vendored
Normal file → Executable file
4
src/UI/Content/Bootstrap/bootstrap.less
vendored
Normal file → Executable file
@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Bootstrap v3.3.5 (http://getbootstrap.com)
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
|
0
src/UI/Content/Bootstrap/breadcrumbs.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/breadcrumbs.less
Normal file → Executable file
6
src/UI/Content/Bootstrap/button-groups.less
Normal file → Executable file
6
src/UI/Content/Bootstrap/button-groups.less
Normal file → Executable file
@ -59,7 +59,7 @@
|
||||
.border-right-radius(0);
|
||||
}
|
||||
}
|
||||
// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it
|
||||
// Need .dropdown-toggle since :last-child doesn't apply, given that a .dropdown-menu is used immediately after it
|
||||
.btn-group > .btn:last-child:not(:first-child),
|
||||
.btn-group > .dropdown-toggle:not(:first-child) {
|
||||
.border-left-radius(0);
|
||||
@ -173,12 +173,12 @@
|
||||
border-radius: 0;
|
||||
}
|
||||
&:first-child:not(:last-child) {
|
||||
border-top-right-radius: @btn-border-radius-base;
|
||||
.border-top-radius(@btn-border-radius-base);
|
||||
.border-bottom-radius(0);
|
||||
}
|
||||
&:last-child:not(:first-child) {
|
||||
border-bottom-left-radius: @btn-border-radius-base;
|
||||
.border-top-radius(0);
|
||||
.border-bottom-radius(@btn-border-radius-base);
|
||||
}
|
||||
}
|
||||
.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
|
||||
|
0
src/UI/Content/Bootstrap/buttons.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/buttons.less
Normal file → Executable file
13
src/UI/Content/Bootstrap/carousel.less
Normal file → Executable file
13
src/UI/Content/Bootstrap/carousel.less
Normal file → Executable file
@ -101,6 +101,7 @@
|
||||
color: @carousel-control-color;
|
||||
text-align: center;
|
||||
text-shadow: @carousel-text-shadow;
|
||||
background-color: rgba(0, 0, 0, 0); // Fix IE9 click-thru bug
|
||||
// We can't have this transition here because WebKit cancels the carousel
|
||||
// animation if you trip this while in the middle of another animation.
|
||||
|
||||
@ -240,18 +241,18 @@
|
||||
.glyphicon-chevron-right,
|
||||
.icon-prev,
|
||||
.icon-next {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-top: -15px;
|
||||
font-size: 30px;
|
||||
width: (@carousel-control-font-size * 1.5);
|
||||
height: (@carousel-control-font-size * 1.5);
|
||||
margin-top: (@carousel-control-font-size / -2);
|
||||
font-size: (@carousel-control-font-size * 1.5);
|
||||
}
|
||||
.glyphicon-chevron-left,
|
||||
.icon-prev {
|
||||
margin-left: -15px;
|
||||
margin-left: (@carousel-control-font-size / -2);
|
||||
}
|
||||
.glyphicon-chevron-right,
|
||||
.icon-next {
|
||||
margin-right: -15px;
|
||||
margin-right: (@carousel-control-font-size / -2);
|
||||
}
|
||||
}
|
||||
|
||||
|
0
src/UI/Content/Bootstrap/close.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/close.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/code.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/code.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/component-animations.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/component-animations.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/dropdowns.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/dropdowns.less
Normal file → Executable file
10
src/UI/Content/Bootstrap/forms.less
Normal file → Executable file
10
src/UI/Content/Bootstrap/forms.less
Normal file → Executable file
@ -132,6 +132,12 @@ output {
|
||||
// Placeholder
|
||||
.placeholder();
|
||||
|
||||
// Unstyle the caret on `<select>`s in IE10+.
|
||||
&::-ms-expand {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
// Disabled and read-only inputs
|
||||
//
|
||||
// HTML5 says that controls under a fieldset > legend:first-child won't be
|
||||
@ -175,7 +181,7 @@ input[type="search"] {
|
||||
// set a pixel line-height that matches the given height of the input, but only
|
||||
// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848
|
||||
//
|
||||
// Note that as of 8.3, iOS doesn't support `datetime` or `week`.
|
||||
// Note that as of 9.3, iOS doesn't support `week`.
|
||||
|
||||
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
||||
input[type="date"],
|
||||
@ -591,7 +597,7 @@ input[type="checkbox"] {
|
||||
.form-group-lg {
|
||||
@media (min-width: @screen-sm-min) {
|
||||
.control-label {
|
||||
padding-top: ((@padding-large-vertical * @line-height-large) + 1);
|
||||
padding-top: (@padding-large-vertical + 1);
|
||||
font-size: @font-size-large;
|
||||
}
|
||||
}
|
||||
|
4
src/UI/Content/Bootstrap/glyphicons.less
Normal file → Executable file
4
src/UI/Content/Bootstrap/glyphicons.less
Normal file → Executable file
@ -32,8 +32,8 @@
|
||||
}
|
||||
|
||||
// Individual icons
|
||||
.glyphicon-asterisk { &:before { content: "\2a"; } }
|
||||
.glyphicon-plus { &:before { content: "\2b"; } }
|
||||
.glyphicon-asterisk { &:before { content: "\002a"; } }
|
||||
.glyphicon-plus { &:before { content: "\002b"; } }
|
||||
.glyphicon-euro,
|
||||
.glyphicon-eur { &:before { content: "\20ac"; } }
|
||||
.glyphicon-minus { &:before { content: "\2212"; } }
|
||||
|
0
src/UI/Content/Bootstrap/grid.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/grid.less
Normal file → Executable file
10
src/UI/Content/Bootstrap/input-groups.less
Normal file → Executable file
10
src/UI/Content/Bootstrap/input-groups.less
Normal file → Executable file
@ -29,6 +29,10 @@
|
||||
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:focus {
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,18 +83,18 @@
|
||||
text-align: center;
|
||||
background-color: @input-group-addon-bg;
|
||||
border: 1px solid @input-group-addon-border-color;
|
||||
border-radius: @border-radius-base;
|
||||
border-radius: @input-border-radius;
|
||||
|
||||
// Sizing
|
||||
&.input-sm {
|
||||
padding: @padding-small-vertical @padding-small-horizontal;
|
||||
font-size: @font-size-small;
|
||||
border-radius: @border-radius-small;
|
||||
border-radius: @input-border-radius-small;
|
||||
}
|
||||
&.input-lg {
|
||||
padding: @padding-large-vertical @padding-large-horizontal;
|
||||
font-size: @font-size-large;
|
||||
border-radius: @border-radius-large;
|
||||
border-radius: @input-border-radius-large;
|
||||
}
|
||||
|
||||
// Nuke default margins from checkboxes and radios to vertically center within.
|
||||
|
2
src/UI/Content/Bootstrap/jumbotron.less
Normal file → Executable file
2
src/UI/Content/Bootstrap/jumbotron.less
Normal file → Executable file
@ -28,6 +28,8 @@
|
||||
.container &,
|
||||
.container-fluid & {
|
||||
border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container
|
||||
padding-left: (@grid-gutter-width / 2);
|
||||
padding-right: (@grid-gutter-width / 2);
|
||||
}
|
||||
|
||||
.container {
|
||||
|
0
src/UI/Content/Bootstrap/labels.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/labels.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/list-group.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/list-group.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/media.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/media.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/alerts.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/alerts.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/background-variant.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/background-variant.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/border-radius.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/border-radius.less
Normal file → Executable file
5
src/UI/Content/Bootstrap/mixins/buttons.less
Normal file → Executable file
5
src/UI/Content/Bootstrap/mixins/buttons.less
Normal file → Executable file
@ -42,12 +42,9 @@
|
||||
&.disabled,
|
||||
&[disabled],
|
||||
fieldset[disabled] & {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.focus,
|
||||
&:active,
|
||||
&.active {
|
||||
&.focus {
|
||||
background-color: @background;
|
||||
border-color: @border;
|
||||
}
|
||||
|
0
src/UI/Content/Bootstrap/mixins/center-block.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/center-block.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/clearfix.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/clearfix.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/forms.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/forms.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/gradients.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/gradients.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/grid-framework.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/grid-framework.less
Normal file → Executable file
4
src/UI/Content/Bootstrap/mixins/grid.less
Normal file → Executable file
4
src/UI/Content/Bootstrap/mixins/grid.less
Normal file → Executable file
@ -6,8 +6,8 @@
|
||||
.container-fixed(@gutter: @grid-gutter-width) {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-left: (@gutter / 2);
|
||||
padding-right: (@gutter / 2);
|
||||
padding-left: floor((@gutter / 2));
|
||||
padding-right: ceil((@gutter / 2));
|
||||
&:extend(.clearfix all);
|
||||
}
|
||||
|
||||
|
2
src/UI/Content/Bootstrap/mixins/hide-text.less
Normal file → Executable file
2
src/UI/Content/Bootstrap/mixins/hide-text.less
Normal file → Executable file
@ -6,7 +6,7 @@
|
||||
//
|
||||
// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757
|
||||
|
||||
// Deprecated as of v3.0.1 (will be removed in v4)
|
||||
// Deprecated as of v3.0.1 (has been removed in v4)
|
||||
.hide-text() {
|
||||
font: ~"0/0" a;
|
||||
color: transparent;
|
||||
|
0
src/UI/Content/Bootstrap/mixins/image.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/image.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/labels.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/labels.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/list-group.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/list-group.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/nav-divider.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/nav-divider.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/nav-vertical-align.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/nav-vertical-align.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/opacity.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/opacity.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/pagination.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/pagination.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/panels.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/panels.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/progress-bar.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/progress-bar.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/reset-filter.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/reset-filter.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/reset-text.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/reset-text.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/resize.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/resize.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/responsive-visibility.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/responsive-visibility.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/size.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/size.less
Normal file → Executable file
6
src/UI/Content/Bootstrap/mixins/tab-focus.less
Normal file → Executable file
6
src/UI/Content/Bootstrap/mixins/tab-focus.less
Normal file → Executable file
@ -1,9 +1,9 @@
|
||||
// WebKit-style focus
|
||||
|
||||
.tab-focus() {
|
||||
// Default
|
||||
outline: thin dotted;
|
||||
// WebKit
|
||||
// WebKit-specific. Other browsers will keep their default outline style.
|
||||
// (Initially tried to also force default via `outline: initial`,
|
||||
// but that seems to erroneously remove the outline in Firefox altogether.)
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
0
src/UI/Content/Bootstrap/mixins/table-row.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/table-row.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/text-emphasis.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/text-emphasis.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/text-overflow.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/mixins/text-overflow.less
Normal file → Executable file
2
src/UI/Content/Bootstrap/mixins/vendor-prefixes.less
Normal file → Executable file
2
src/UI/Content/Bootstrap/mixins/vendor-prefixes.less
Normal file → Executable file
@ -1,7 +1,7 @@
|
||||
// Vendor Prefixes
|
||||
//
|
||||
// All vendor mixins are deprecated as of v3.2.0 due to the introduction of
|
||||
// Autoprefixer in our Gruntfile. They will be removed in v4.
|
||||
// Autoprefixer in our Gruntfile. They have been removed in v4.
|
||||
|
||||
// - Animations
|
||||
// - Backface visibility
|
||||
|
2
src/UI/Content/Bootstrap/modals.less
Normal file → Executable file
2
src/UI/Content/Bootstrap/modals.less
Normal file → Executable file
@ -79,7 +79,7 @@
|
||||
.modal-header {
|
||||
padding: @modal-title-padding;
|
||||
border-bottom: 1px solid @modal-header-border-color;
|
||||
min-height: (@modal-title-padding + @modal-title-line-height);
|
||||
&:extend(.clearfix all);
|
||||
}
|
||||
// Close icon
|
||||
.modal-header .close {
|
||||
|
0
src/UI/Content/Bootstrap/navbar.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/navbar.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/navs.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/navs.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/normalize.less
vendored
Normal file → Executable file
0
src/UI/Content/Bootstrap/normalize.less
vendored
Normal file → Executable file
0
src/UI/Content/Bootstrap/pager.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/pager.less
Normal file → Executable file
4
src/UI/Content/Bootstrap/pagination.less
Normal file → Executable file
4
src/UI/Content/Bootstrap/pagination.less
Normal file → Executable file
@ -40,7 +40,7 @@
|
||||
> li > span {
|
||||
&:hover,
|
||||
&:focus {
|
||||
z-index: 3;
|
||||
z-index: 2;
|
||||
color: @pagination-hover-color;
|
||||
background-color: @pagination-hover-bg;
|
||||
border-color: @pagination-hover-border;
|
||||
@ -52,7 +52,7 @@
|
||||
&,
|
||||
&:hover,
|
||||
&:focus {
|
||||
z-index: 2;
|
||||
z-index: 3;
|
||||
color: @pagination-active-color;
|
||||
background-color: @pagination-active-bg;
|
||||
border-color: @pagination-active-border;
|
||||
|
2
src/UI/Content/Bootstrap/panels.less
Normal file → Executable file
2
src/UI/Content/Bootstrap/panels.less
Normal file → Executable file
@ -214,7 +214,7 @@
|
||||
}
|
||||
|
||||
|
||||
// Collapsable panels (aka, accordion)
|
||||
// Collapsible panels (aka, accordion)
|
||||
//
|
||||
// Wrap a series of panels in `.panel-group` to turn them into an accordion with
|
||||
// the help of our collapse JavaScript plugin.
|
||||
|
0
src/UI/Content/Bootstrap/popovers.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/popovers.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/print.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/print.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/progress-bars.less
Normal file → Executable file
0
src/UI/Content/Bootstrap/progress-bars.less
Normal file → Executable file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user