New: Setting for absolute maximum size for a release (#2662)

* New: Setting for absolute maximum size for a release

* Change to Advanced Setting
This commit is contained in:
Qstick 2018-03-26 03:03:43 -04:00 committed by Leonardo Galli
parent 80b698a749
commit 8ee71fd0fa
9 changed files with 214 additions and 60 deletions

View File

@ -1,4 +1,4 @@
using FluentValidation;
using FluentValidation;
using NzbDrone.Api.Validation;
using NzbDrone.Core.Configuration;
@ -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);
}
}
}
}

View File

@ -1,4 +1,4 @@
using NzbDrone.Api.REST;
using NzbDrone.Api.REST;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser;
@ -7,6 +7,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; }
public bool PreferIndexerFlags { get; set; }
@ -23,6 +24,7 @@ namespace NzbDrone.Api.Config
return new IndexerConfigResource
{
MinimumAge = model.MinimumAge,
MaximumSize = model.MaximumSize,
Retention = model.Retention,
RssSyncInterval = model.RssSyncInterval,
PreferIndexerFlags = model.PreferIndexerFlags,

View File

@ -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<MaximumSizeSpecification>
{
private RemoteMovie _remoteMovie;
[SetUp]
public void Setup()
{
_remoteMovie = new RemoteMovie { Release = new ReleaseInfo() };
}
private void WithMaximumSize(int size)
{
Mocker.GetMock<IConfigService>().SetupGet(c => c.MaximumSize).Returns(size);
}
private void WithSize(int size)
{
_remoteMovie.Release.Size = size * 1024 * 1024;
}
[Test]
public void should_return_true_when_maximum_size_is_set_to_zero()
{
WithMaximumSize(0);
WithSize(1000);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_when_size_is_smaller_than_maximum_size()
{
WithMaximumSize(2000);
WithSize(1999);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_when_size_is_equals_to_maximum_size()
{
WithMaximumSize(2000);
WithSize(2000);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_when_size_is_bigger_than_maximum_size()
{
WithMaximumSize(2000);
WithSize(2001);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_true_when_size_is_zero()
{
WithMaximumSize(2000);
WithSize(0);
Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue();
}
}
}

View File

@ -138,6 +138,7 @@
<Compile Include="Datastore\ReflectionStrategyFixture\Benchmarks.cs" />
<Compile Include="Datastore\SqliteSchemaDumperTests\SqliteSchemaDumperFixture.cs" />
<Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\MaximumSizeSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\ProtocolSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\CutoffSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\DownloadDecisionMakerFixture.cs" />

View File

@ -106,11 +106,11 @@ namespace NzbDrone.Core.Configuration
set { SetValue("RssSyncInterval", value); }
}
public int AvailabilityDelay
{
get { return GetValueInt("AvailabilityDelay",0); }
set { SetValue("AvailabilityDelay", value); }
}
public int AvailabilityDelay
{
get { return GetValueInt("AvailabilityDelay", 0); }
set { SetValue("AvailabilityDelay", value); }
}
public int NetImportSyncInterval
{
@ -118,57 +118,64 @@ namespace NzbDrone.Core.Configuration
set { SetValue("NetImportSyncInterval", value); }
}
public string TraktAuthToken
{
{
get { return GetValue("TraktAuthToken", string.Empty); }
set { SetValue("TraktAuthToken", value); }
}
}
public string TraktRefreshToken
{
get { return GetValue("TraktRefreshToken", string.Empty); }
set {SetValue("TraktRefreshToken", value); }
}
public int TraktTokenExpiry
{
get { return GetValueInt("TraktTokenExpiry", 0); }
set { SetValue("TraktTokenExpiry", value); }
}
public string TraktRefreshToken
{
get { return GetValue("TraktRefreshToken", string.Empty); }
public string NewTraktAuthToken
{
get {return GetValue("NewTraktAuthToken", string.Empty); }
set { SetValue("NewTraktAuthToken", value); }
}
set { SetValue("TraktRefreshToken", value); }
}
public string NewTraktRefreshToken
{
get {return GetValue("NewTraktRefreshToken", string.Empty); }
set { SetValue("NewTraktRefreshToken", value); }
}
public int TraktTokenExpiry
{
get { return GetValueInt("TraktTokenExpiry", 0); }
public int NewTraktTokenExpiry
{
get {return GetValueInt("NewTraktTokenExpiry", 0); }
set { SetValue("NewTraktTokenExpiry", value); }
}
set { SetValue("TraktTokenExpiry", value); }
}
public string ListSyncLevel
{
get { return GetValue("ListSyncLevel", "disabled"); }
set { SetValue("ListSyncLevel", value); }
}
public string NewTraktAuthToken
{
get { return GetValue("NewTraktAuthToken", string.Empty); }
set { SetValue("NewTraktAuthToken", value); }
}
public string ImportExclusions
{
get { return GetValue("ImportExclusions", string.Empty); }
set { SetValue("ImportExclusions", value); }
}
public string NewTraktRefreshToken
{
get { return GetValue("NewTraktRefreshToken", string.Empty); }
set { SetValue("NewTraktRefreshToken", value); }
}
public int NewTraktTokenExpiry
{
get { return GetValueInt("NewTraktTokenExpiry", 0); }
set { SetValue("NewTraktTokenExpiry", value); }
}
public string ListSyncLevel
{
get { return GetValue("ListSyncLevel", "disabled"); }
set { SetValue("ListSyncLevel", value); }
}
public string ImportExclusions
{
get { return GetValue("ImportExclusions", string.Empty); }
set { SetValue("ImportExclusions", value); }
}
public int MaximumSize
{
get { return GetValueInt("MaximumSize", 0); }
set { SetValue("MaximumSize", value); }
}
public int MinimumAge
{
@ -195,22 +202,22 @@ namespace NzbDrone.Core.Configuration
{
get { return GetValueBoolean("PreferIndexerFlags", false); }
set {SetValue("PreferIndexerFlags", value);}
set { SetValue("PreferIndexerFlags", value); }
}
public bool AllowHardcodedSubs
{
get { return GetValueBoolean("AllowHardcodedSubs", false); }
public bool AllowHardcodedSubs
{
get { return GetValueBoolean("AllowHardcodedSubs", false); }
set { SetValue("AllowHardcodedSubs", value); }
}
set { SetValue("AllowHardcodedSubs", value); }
}
public string WhitelistedHardcodedSubs
{
get { return GetValue("WhitelistedHardcodedSubs", ""); }
public string WhitelistedHardcodedSubs
{
get { return GetValue("WhitelistedHardcodedSubs", ""); }
set { SetValue("WhitelistedHardcodedSubs", value); }
}
set { SetValue("WhitelistedHardcodedSubs", value); }
}
public ParsingLeniencyType ParsingLeniency
{

View File

@ -48,6 +48,7 @@ namespace NzbDrone.Core.Configuration
//Indexers
int Retention { get; set; }
int RssSyncInterval { get; set; }
int MaximumSize { get; set; }
int MinimumAge { get; set; }
bool PreferIndexerFlags { get; set; }

View File

@ -0,0 +1,52 @@
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 RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteMovie 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 (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();
}
}
}

View File

@ -128,6 +128,7 @@
<Compile Include="Datastore\Migration\142_movie_extras.cs" />
<Compile Include="Datastore\Migration\140_add_alternative_titles_table.cs" />
<Compile Include="Datastore\Migration\141_fix_duplicate_alt_titles.cs" />
<Compile Include="DecisionEngine\Specifications\MaximumSizeSpecification.cs" />
<Compile Include="DecisionEngine\Specifications\RequiredIndexerFlagsSpecification.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedAlternativeTitles.cs" />
<Compile Include="MediaFiles\MovieImport\Specifications\GrabbedReleaseQualitySpecification.cs" />

View File

@ -25,6 +25,18 @@
</div>
</div>
<div class="form-group advanced-setting">
<label class="col-sm-3 control-label">Maximum Size</label>
<div class="col-sm-1 col-sm-push-2 help-inline">
<i class="icon-sonarr-form-info" title="Maximum size for a release to be grabbed in MB. Set to zero to set to unlimited"/>
</div>
<div class="col-sm-2 col-sm-pull-1">
<input type="number" min="0" name="maximumSize" class="form-control"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Prefer Special Indexer Flags</label>
<div class="col-sm-1 col-sm-push-2 help-inline">