diff --git a/src/NzbDrone.Api/Config/IndexerConfigModule.cs b/src/NzbDrone.Api/Config/IndexerConfigModule.cs index 73c2442b8..ebb8f7cd8 100644 --- a/src/NzbDrone.Api/Config/IndexerConfigModule.cs +++ b/src/NzbDrone.Api/Config/IndexerConfigModule.cs @@ -13,6 +13,9 @@ namespace NzbDrone.Api.Config SharedValidator.RuleFor(c => c.MinimumAge) .GreaterThanOrEqualTo(0); + SharedValidator.RuleFor(c => c.MaximumSize) + .GreaterThanOrEqualTo(0); + SharedValidator.RuleFor(c => c.Retention) .GreaterThanOrEqualTo(0); @@ -25,4 +28,4 @@ namespace NzbDrone.Api.Config return IndexerConfigResourceMapper.ToResource(model); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Api/Config/IndexerConfigResource.cs b/src/NzbDrone.Api/Config/IndexerConfigResource.cs index 179e28c3f..59fcb48e3 100644 --- a/src/NzbDrone.Api/Config/IndexerConfigResource.cs +++ b/src/NzbDrone.Api/Config/IndexerConfigResource.cs @@ -6,6 +6,7 @@ namespace NzbDrone.Api.Config public class IndexerConfigResource : RestResource { public int MinimumAge { get; set; } + public int MaximumSize { get; set; } public int Retention { get; set; } public int RssSyncInterval { get; set; } } @@ -17,6 +18,7 @@ namespace NzbDrone.Api.Config return new IndexerConfigResource { MinimumAge = model.MinimumAge, + MaximumSize = model.MaximumSize, Retention = model.Retention, RssSyncInterval = model.RssSyncInterval, }; diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/MaximumSizeSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/MaximumSizeSpecificationFixture.cs new file mode 100644 index 000000000..1b8d7ab37 --- /dev/null +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/MaximumSizeSpecificationFixture.cs @@ -0,0 +1,75 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.DecisionEngine.Specifications; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.DecisionEngineTests +{ + public class MaximumSizeSpecificationFixture : CoreTest + { + private RemoteEpisode _remoteEpisode; + + [SetUp] + public void Setup() + { + _remoteEpisode = new RemoteEpisode() { Release = new ReleaseInfo() }; + } + + private void WithMaximumSize(int size) + { + Mocker.GetMock().SetupGet(c => c.MaximumSize).Returns(size); + } + + private void WithSize(int size) + { + _remoteEpisode.Release.Size = size * 1024 * 1024; + } + + [Test] + public void should_return_true_when_maximum_size_is_set_to_zero() + { + WithMaximumSize(0); + WithSize(1000); + + Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_return_true_when_size_is_smaller_than_maximum_size() + { + WithMaximumSize(2000); + WithSize(1999); + + Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_return_true_when_size_is_equals_to_maximum_size() + { + WithMaximumSize(2000); + WithSize(2000); + + Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_return_false_when_size_is_bigger_than_maximum_size() + { + WithMaximumSize(2000); + WithSize(2001); + + Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); + } + + [Test] + public void should_return_true_when_size_is_zero() + { + WithMaximumSize(2000); + WithSize(0); + + Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + } + } +} diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index f2b759c8d..8099210ec 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -146,6 +146,7 @@ + diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index d9b57d68a..3da6a13a3 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -106,6 +106,12 @@ namespace NzbDrone.Core.Configuration set { SetValue("RssSyncInterval", value); } } + public int MaximumSize + { + get { return GetValueInt("MaximumSize", 0); } + set { SetValue("MaximumSize", value); } + } + public int MinimumAge { get { return GetValueInt("MinimumAge", 0); } diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index 589e003e1..9f781becd 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -45,6 +45,7 @@ namespace NzbDrone.Core.Configuration //Indexers int Retention { get; set; } int RssSyncInterval { get; set; } + int MaximumSize { get; set; } int MinimumAge { get; set; } //UI diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs new file mode 100644 index 000000000..9f7044876 --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/MaximumSizeSpecification.cs @@ -0,0 +1,53 @@ +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications +{ + public class MaximumSizeSpecification : IDecisionEngineSpecification + { + private readonly IConfigService _configService; + private readonly Logger _logger; + + public MaximumSizeSpecification(IConfigService configService, Logger logger) + { + _configService = configService; + _logger = logger; + } + + public SpecificationPriority Priority => SpecificationPriority.Default; + public RejectionType Type => RejectionType.Permanent; + + public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + { + var size = subject.Release.Size; + var maximumSize = _configService.MaximumSize.Megabytes(); + + if (maximumSize == 0) + { + _logger.Debug("Maximum size is not set."); + return Decision.Accept(); + } + + if (subject.Release.Size == 0) + { + _logger.Debug("Release has unknown size, skipping size check."); + return Decision.Accept(); + } + + _logger.Debug("Checking if release meets maximum size requirements. {0}", size.SizeSuffix()); + + if (size > maximumSize) + { + var message = $"{size.SizeSuffix()} is too big, maximum size is {maximumSize.SizeSuffix()}"; + + _logger.Debug(message); + return Decision.Reject(message); + } + + return Decision.Accept(); + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index e630edae3..ed2ff9853 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -332,6 +332,7 @@ + diff --git a/src/UI/Settings/Indexers/Options/IndexerOptionsViewTemplate.hbs b/src/UI/Settings/Indexers/Options/IndexerOptionsViewTemplate.hbs index 374945620..094ef2884 100644 --- a/src/UI/Settings/Indexers/Options/IndexerOptionsViewTemplate.hbs +++ b/src/UI/Settings/Indexers/Options/IndexerOptionsViewTemplate.hbs @@ -12,6 +12,18 @@ + +
+ + +
+ +
+ +
+ +
+