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.Api.Validation;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
@ -13,6 +13,9 @@ namespace NzbDrone.Api.Config
SharedValidator.RuleFor(c => c.MinimumAge) SharedValidator.RuleFor(c => c.MinimumAge)
.GreaterThanOrEqualTo(0); .GreaterThanOrEqualTo(0);
SharedValidator.RuleFor(c => c.MaximumSize)
.GreaterThanOrEqualTo(0);
SharedValidator.RuleFor(c => c.Retention) SharedValidator.RuleFor(c => c.Retention)
.GreaterThanOrEqualTo(0); .GreaterThanOrEqualTo(0);
@ -25,4 +28,4 @@ namespace NzbDrone.Api.Config
return IndexerConfigResourceMapper.ToResource(model); 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.Configuration;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
@ -7,6 +7,7 @@ namespace NzbDrone.Api.Config
public class IndexerConfigResource : RestResource public class IndexerConfigResource : RestResource
{ {
public int MinimumAge { get; set; } public int MinimumAge { get; set; }
public int MaximumSize { get; set; }
public int Retention { get; set; } public int Retention { get; set; }
public int RssSyncInterval { get; set; } public int RssSyncInterval { get; set; }
public bool PreferIndexerFlags { get; set; } public bool PreferIndexerFlags { get; set; }
@ -23,6 +24,7 @@ namespace NzbDrone.Api.Config
return new IndexerConfigResource return new IndexerConfigResource
{ {
MinimumAge = model.MinimumAge, MinimumAge = model.MinimumAge,
MaximumSize = model.MaximumSize,
Retention = model.Retention, Retention = model.Retention,
RssSyncInterval = model.RssSyncInterval, RssSyncInterval = model.RssSyncInterval,
PreferIndexerFlags = model.PreferIndexerFlags, 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\ReflectionStrategyFixture\Benchmarks.cs" />
<Compile Include="Datastore\SqliteSchemaDumperTests\SqliteSchemaDumperFixture.cs" /> <Compile Include="Datastore\SqliteSchemaDumperTests\SqliteSchemaDumperFixture.cs" />
<Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\AcceptableSizeSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\MaximumSizeSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\ProtocolSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\ProtocolSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\CutoffSpecificationFixture.cs" /> <Compile Include="DecisionEngineTests\CutoffSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\DownloadDecisionMakerFixture.cs" /> <Compile Include="DecisionEngineTests\DownloadDecisionMakerFixture.cs" />

View File

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

View File

@ -48,6 +48,7 @@ namespace NzbDrone.Core.Configuration
//Indexers //Indexers
int Retention { get; set; } int Retention { get; set; }
int RssSyncInterval { get; set; } int RssSyncInterval { get; set; }
int MaximumSize { get; set; }
int MinimumAge { get; set; } int MinimumAge { get; set; }
bool PreferIndexerFlags { 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\142_movie_extras.cs" />
<Compile Include="Datastore\Migration\140_add_alternative_titles_table.cs" /> <Compile Include="Datastore\Migration\140_add_alternative_titles_table.cs" />
<Compile Include="Datastore\Migration\141_fix_duplicate_alt_titles.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="DecisionEngine\Specifications\RequiredIndexerFlagsSpecification.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedAlternativeTitles.cs" /> <Compile Include="Housekeeping\Housekeepers\CleanupOrphanedAlternativeTitles.cs" />
<Compile Include="MediaFiles\MovieImport\Specifications\GrabbedReleaseQualitySpecification.cs" /> <Compile Include="MediaFiles\MovieImport\Specifications\GrabbedReleaseQualitySpecification.cs" />

View File

@ -25,6 +25,18 @@
</div> </div>
</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"> <div class="form-group">
<label class="col-sm-3 control-label">Prefer Special Indexer Flags</label> <label class="col-sm-3 control-label">Prefer Special Indexer Flags</label>
<div class="col-sm-1 col-sm-push-2 help-inline"> <div class="col-sm-1 col-sm-push-2 help-inline">