New: Minimum Age setting to deal with propagation issues (Usenet only)
This commit is contained in:
parent
eeafa2cb64
commit
580f03a5bc
|
@ -9,6 +9,12 @@ namespace NzbDrone.Api.Config
|
||||||
public IndexerConfigModule(IConfigService configService)
|
public IndexerConfigModule(IConfigService configService)
|
||||||
: base(configService)
|
: base(configService)
|
||||||
{
|
{
|
||||||
|
SharedValidator.RuleFor(c => c.MinimumAge)
|
||||||
|
.GreaterThan(0);
|
||||||
|
|
||||||
|
SharedValidator.RuleFor(c => c.Retention)
|
||||||
|
.GreaterThanOrEqualTo(0);
|
||||||
|
|
||||||
SharedValidator.RuleFor(c => c.RssSyncInterval)
|
SharedValidator.RuleFor(c => c.RssSyncInterval)
|
||||||
.InclusiveBetween(10, 120)
|
.InclusiveBetween(10, 120)
|
||||||
.When(c => c.RssSyncInterval > 0);
|
.When(c => c.RssSyncInterval > 0);
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace NzbDrone.Api.Config
|
||||||
{
|
{
|
||||||
public class IndexerConfigResource : RestResource
|
public class IndexerConfigResource : RestResource
|
||||||
{
|
{
|
||||||
|
public Int32 MinimumAge { get; set; }
|
||||||
public Int32 Retention { get; set; }
|
public Int32 Retention { get; set; }
|
||||||
public Int32 RssSyncInterval { get; set; }
|
public Int32 RssSyncInterval { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
using System;
|
||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||||
|
using NzbDrone.Core.Indexers;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
|
||||||
|
public class MinimumAgeSpecificationFixture : CoreTest<MinimumAgeSpecification>
|
||||||
|
{
|
||||||
|
private RemoteEpisode _remoteEpisode;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_remoteEpisode = new RemoteEpisode
|
||||||
|
{
|
||||||
|
Release = new ReleaseInfo() { DownloadProtocol = DownloadProtocol.Usenet }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WithMinimumAge(int minutes)
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IConfigService>().SetupGet(c => c.MinimumAge).Returns(minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WithAge(int minutes)
|
||||||
|
{
|
||||||
|
_remoteEpisode.Release.PublishDate = DateTime.UtcNow.AddMinutes(-minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_true_when_minimum_age_is_set_to_zero()
|
||||||
|
{
|
||||||
|
WithMinimumAge(0);
|
||||||
|
WithAge(100);
|
||||||
|
|
||||||
|
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_true_when_age_is_greater_than_minimum_age()
|
||||||
|
{
|
||||||
|
WithMinimumAge(30);
|
||||||
|
WithAge(100);
|
||||||
|
|
||||||
|
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_false_when_age_is_less_than_minimum_age()
|
||||||
|
{
|
||||||
|
WithMinimumAge(30);
|
||||||
|
WithAge(10);
|
||||||
|
|
||||||
|
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
|
|
||||||
public class RetentionSpecificationFixture : CoreTest<RetentionSpecification>
|
public class RetentionSpecificationFixture : CoreTest<RetentionSpecification>
|
||||||
{
|
{
|
||||||
|
|
||||||
private RemoteEpisode _remoteEpisode;
|
private RemoteEpisode _remoteEpisode;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
|
@ -32,7 +31,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||||
|
|
||||||
private void WithAge(int days)
|
private void WithAge(int days)
|
||||||
{
|
{
|
||||||
_remoteEpisode.Release.PublishDate = DateTime.Now.AddDays(-days);
|
_remoteEpisode.Release.PublishDate = DateTime.UtcNow.AddDays(-days);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|
|
@ -136,6 +136,7 @@
|
||||||
<Compile Include="DecisionEngineTests\PrioritizeDownloadDecisionFixture.cs" />
|
<Compile Include="DecisionEngineTests\PrioritizeDownloadDecisionFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\QualityAllowedByProfileSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\QualityAllowedByProfileSpecificationFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\QualityUpgradeSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\QualityUpgradeSpecificationFixture.cs" />
|
||||||
|
<Compile Include="DecisionEngineTests\MinimumAgeSpecificationFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\RetentionSpecificationFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
|
||||||
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
|
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
|
||||||
|
|
|
@ -108,6 +108,13 @@ namespace NzbDrone.Core.Configuration
|
||||||
set { SetValue("RssSyncInterval", value); }
|
set { SetValue("RssSyncInterval", value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int MinimumAge
|
||||||
|
{
|
||||||
|
get { return GetValueInt("MinimumAge", 0); }
|
||||||
|
|
||||||
|
set { SetValue("MinimumAge", value); }
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean AutoDownloadPropers
|
public Boolean AutoDownloadPropers
|
||||||
{
|
{
|
||||||
get { return GetValueBoolean("AutoDownloadPropers", true); }
|
get { return GetValueBoolean("AutoDownloadPropers", true); }
|
||||||
|
|
|
@ -49,6 +49,7 @@ namespace NzbDrone.Core.Configuration
|
||||||
//Indexers
|
//Indexers
|
||||||
Int32 Retention { get; set; }
|
Int32 Retention { get; set; }
|
||||||
Int32 RssSyncInterval { get; set; }
|
Int32 RssSyncInterval { get; set; }
|
||||||
|
Int32 MinimumAge { get; set; }
|
||||||
|
|
||||||
//UI
|
//UI
|
||||||
Int32 FirstDayOfWeek { get; set; }
|
Int32 FirstDayOfWeek { get; set; }
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
|
using NzbDrone.Core.Parser.Model;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
|
{
|
||||||
|
public class MinimumAgeSpecification : IDecisionEngineSpecification
|
||||||
|
{
|
||||||
|
private readonly IConfigService _configService;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public MinimumAgeSpecification(IConfigService configService, Logger logger)
|
||||||
|
{
|
||||||
|
_configService = configService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RejectionType Type { get { return RejectionType.Temporary; } }
|
||||||
|
|
||||||
|
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||||
|
{
|
||||||
|
if (subject.Release.DownloadProtocol != Indexers.DownloadProtocol.Usenet)
|
||||||
|
{
|
||||||
|
_logger.Debug("Not checking minimum age requirement for non-usenet report");
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
var age = subject.Release.AgeMinutes;
|
||||||
|
var minimumAge = _configService.MinimumAge;
|
||||||
|
|
||||||
|
_logger.Debug("Checking if report meets minimum age requirements. {0}", age);
|
||||||
|
|
||||||
|
if (minimumAge > 0 && age < minimumAge)
|
||||||
|
{
|
||||||
|
_logger.Debug("Only {0} minutes old, minimum age is {1} minutes", age, minimumAge);
|
||||||
|
return Decision.Reject("Only {0} minutes old, minimum age is {1} minutes", age, minimumAge);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decision.Accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Indexers;
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
@ -36,6 +37,7 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
private readonly ISeriesService _seriesService;
|
private readonly ISeriesService _seriesService;
|
||||||
private readonly IParsingService _parsingService;
|
private readonly IParsingService _parsingService;
|
||||||
private readonly IDelayProfileService _delayProfileService;
|
private readonly IDelayProfileService _delayProfileService;
|
||||||
|
private readonly IConfigService _configService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
@ -43,6 +45,7 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
ISeriesService seriesService,
|
ISeriesService seriesService,
|
||||||
IParsingService parsingService,
|
IParsingService parsingService,
|
||||||
IDelayProfileService delayProfileService,
|
IDelayProfileService delayProfileService,
|
||||||
|
IConfigService configService,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
|
@ -50,6 +53,7 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
_seriesService = seriesService;
|
_seriesService = seriesService;
|
||||||
_parsingService = parsingService;
|
_parsingService = parsingService;
|
||||||
_delayProfileService = delayProfileService;
|
_delayProfileService = delayProfileService;
|
||||||
|
_configService = configService;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
@ -202,8 +206,10 @@ namespace NzbDrone.Core.Download.Pending
|
||||||
private int GetDelay(RemoteEpisode remoteEpisode)
|
private int GetDelay(RemoteEpisode remoteEpisode)
|
||||||
{
|
{
|
||||||
var delayProfile = _delayProfileService.AllForTags(remoteEpisode.Series.Tags).OrderBy(d => d.Order).First();
|
var delayProfile = _delayProfileService.AllForTags(remoteEpisode.Series.Tags).OrderBy(d => d.Order).First();
|
||||||
|
var delay = delayProfile.GetProtocolDelay(remoteEpisode.Release.DownloadProtocol);
|
||||||
|
var minimumAge = _configService.MinimumAge;
|
||||||
|
|
||||||
return delayProfile.GetProtocolDelay(remoteEpisode.Release.DownloadProtocol);
|
return new [] { delay, minimumAge }.Max();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveGrabbed(RemoteEpisode remoteEpisode)
|
private void RemoveGrabbed(RemoteEpisode remoteEpisode)
|
||||||
|
|
|
@ -268,6 +268,7 @@
|
||||||
<Compile Include="DecisionEngine\Specifications\ReleaseRestrictionsSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\ReleaseRestrictionsSpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\NotSampleSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\NotSampleSpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\QualityAllowedByProfileSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\QualityAllowedByProfileSpecification.cs" />
|
||||||
|
<Compile Include="DecisionEngine\Specifications\MinimumAgeSpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\RetentionSpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\RetentionSpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\RetrySpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\RetrySpecification.cs" />
|
||||||
<Compile Include="DecisionEngine\Specifications\RssSync\DelaySpecification.cs" />
|
<Compile Include="DecisionEngine\Specifications\RssSync\DelaySpecification.cs" />
|
||||||
|
|
|
@ -18,7 +18,7 @@ define(
|
||||||
//If the release is pending we want to use the timeleft as the time it will be processed at
|
//If the release is pending we want to use the timeleft as the time it will be processed at
|
||||||
if (this.cellValue.get('status').toLowerCase() === 'pending') {
|
if (this.cellValue.get('status').toLowerCase() === 'pending') {
|
||||||
this.$el.html('-');
|
this.$el.html('-');
|
||||||
this.$el.attr('title', 'Will be processed {0}'.format(moment(this.cellValue.get('estimatedCompletionTime')).calendar()));
|
this.$el.attr('title', 'Will be processed during the first RSS Sync after {0}'.format(moment(this.cellValue.get('estimatedCompletionTime')).calendar()));
|
||||||
this.$el.attr('data-container', 'body');
|
this.$el.attr('data-container', 'body');
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Options</legend>
|
<legend>Options</legend>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">Minimum Age</label>
|
||||||
|
|
||||||
|
<div class="col-sm-1 col-sm-push-2 help-inline">
|
||||||
|
<i class="icon-nd-form-info" title="Usenet only: Reports that do not meet minimum age will not be grabbed, until they do"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-2 col-sm-pull-1">
|
||||||
|
<input type="number" min="0" name="minimumAge" class="form-control"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-3 control-label">Retention</label>
|
<label class="col-sm-3 control-label">Retention</label>
|
||||||
|
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-1 col-sm-push-2 help-inline">
|
||||||
|
<i class="icon-nd-form-info" title="Usenet only: Set to zero to set to unlimited"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-2 col-sm-pull-1">
|
||||||
<input type="number" min="0" name="retention" class="form-control"/>
|
<input type="number" min="0" name="retention" class="form-control"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue