New: Use 45 minutes for runtime when episode aired within 24 hours of pilot episode
This commit is contained in:
parent
9fb29f42c4
commit
cc9fc1e3c3
|
@ -20,38 +20,50 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
private RemoteEpisode parseResultMulti;
|
||||
private RemoteEpisode parseResultSingle;
|
||||
private Series series;
|
||||
private List<Episode> episodes;
|
||||
private QualityDefinition qualityType;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
series = Builder<Series>.CreateNew()
|
||||
.Build();
|
||||
.With(s => s.Seasons = Builder<Season>.CreateListOfSize(2).Build().ToList())
|
||||
.Build();
|
||||
|
||||
episodes = Builder<Episode>.CreateListOfSize(10)
|
||||
.All()
|
||||
.With(s => s.SeasonNumber = 1)
|
||||
.BuildList();
|
||||
|
||||
parseResultMultiSet = new RemoteEpisode
|
||||
{
|
||||
Series = series,
|
||||
Release = new ReleaseInfo(),
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) },
|
||||
Episodes = new List<Episode> { new Episode(), new Episode(), new Episode(), new Episode(), new Episode(), new Episode() }
|
||||
};
|
||||
Episodes = Builder<Episode>.CreateListOfSize(6).All().With(s => s.SeasonNumber = 1).BuildList()
|
||||
};
|
||||
|
||||
parseResultMulti = new RemoteEpisode
|
||||
{
|
||||
Series = series,
|
||||
Release = new ReleaseInfo(),
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) },
|
||||
Episodes = new List<Episode> { new Episode(), new Episode() }
|
||||
};
|
||||
Episodes = Builder<Episode>.CreateListOfSize(2).All().With(s => s.SeasonNumber = 1).BuildList()
|
||||
};
|
||||
|
||||
parseResultSingle = new RemoteEpisode
|
||||
{
|
||||
Series = series,
|
||||
Release = new ReleaseInfo(),
|
||||
ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) },
|
||||
Episodes = new List<Episode> { new Episode() { Id = 2 } }
|
||||
Episodes = new List<Episode> {
|
||||
Builder<Episode>.CreateNew()
|
||||
.With(s => s.SeasonNumber = 1)
|
||||
.With(s => s.EpisodeNumber = 1)
|
||||
.Build()
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
Mocker.GetMock<IQualityDefinitionService>()
|
||||
.Setup(v => v.Get(It.IsAny<Quality>()))
|
||||
|
@ -67,18 +79,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
|
||||
Mocker.GetMock<IEpisodeService>().Setup(
|
||||
s => s.GetEpisodesBySeason(It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(new List<Episode>() {
|
||||
new Episode(), new Episode(), new Episode(), new Episode(), new Episode(),
|
||||
new Episode(), new Episode(), new Episode(), new Episode() { Id = 2 }, new Episode() });
|
||||
}
|
||||
|
||||
private void GivenLastEpisode()
|
||||
{
|
||||
Mocker.GetMock<IEpisodeService>().Setup(
|
||||
s => s.GetEpisodesBySeason(It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(new List<Episode>() {
|
||||
new Episode(), new Episode(), new Episode(), new Episode(), new Episode(),
|
||||
new Episode(), new Episode(), new Episode(), new Episode(), new Episode() { Id = 2 } });
|
||||
.Returns(episodes);
|
||||
}
|
||||
|
||||
[TestCase(30, 50, false)]
|
||||
|
@ -92,6 +93,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
series.Runtime = runtime;
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Release.Size = sizeInMegaBytes.Megabytes();
|
||||
parseResultSingle.Episodes.First().Id = 5;
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, null).Accepted.Should().Be(expectedResult);
|
||||
}
|
||||
|
@ -100,13 +102,26 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[TestCase(30, 1000, false)]
|
||||
[TestCase(60, 1000, true)]
|
||||
[TestCase(60, 2000, false)]
|
||||
public void single_episode_first_or_last(int runtime, int sizeInMegaBytes, bool expectedResult)
|
||||
public void should_return_expected_result_for_first_episode_of_season(int runtime, int sizeInMegaBytes, bool expectedResult)
|
||||
{
|
||||
GivenLastEpisode();
|
||||
|
||||
series.Runtime = runtime;
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Release.Size = sizeInMegaBytes.Megabytes();
|
||||
parseResultSingle.Episodes.First().Id = episodes.First().Id;
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, null).Accepted.Should().Be(expectedResult);
|
||||
}
|
||||
|
||||
[TestCase(30, 500, true)]
|
||||
[TestCase(30, 1000, false)]
|
||||
[TestCase(60, 1000, true)]
|
||||
[TestCase(60, 2000, false)]
|
||||
public void should_return_expected_result_for_last_episode_of_season(int runtime, int sizeInMegaBytes, bool expectedResult)
|
||||
{
|
||||
series.Runtime = runtime;
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Release.Size = sizeInMegaBytes.Megabytes();
|
||||
parseResultSingle.Episodes.First().Id = episodes.Last().Id;
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, null).Accepted.Should().Be(expectedResult);
|
||||
}
|
||||
|
@ -144,8 +159,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_return_true_if_size_is_zero()
|
||||
{
|
||||
GivenLastEpisode();
|
||||
|
||||
series.Runtime = 30;
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Release.Size = 0;
|
||||
|
@ -158,8 +171,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_return_true_if_unlimited_30_minute()
|
||||
{
|
||||
GivenLastEpisode();
|
||||
|
||||
series.Runtime = 30;
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Release.Size = 18457280000;
|
||||
|
@ -171,8 +182,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_return_true_if_unlimited_60_minute()
|
||||
{
|
||||
GivenLastEpisode();
|
||||
|
||||
series.Runtime = 60;
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Release.Size = 36857280000;
|
||||
|
@ -184,8 +193,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
[Test]
|
||||
public void should_treat_daily_series_as_single_episode()
|
||||
{
|
||||
GivenLastEpisode();
|
||||
|
||||
series.Runtime = 60;
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Series.SeriesType = SeriesTypes.Daily;
|
||||
|
@ -216,5 +223,94 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_series_runtime_is_zero_and_single_episode_is_not_from_first_season()
|
||||
{
|
||||
series.Runtime = 0;
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Episodes.First().Id = 5;
|
||||
parseResultSingle.Release.Size = 200.Megabytes();
|
||||
parseResultSingle.Episodes.First().SeasonNumber = 2;
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, null).Accepted.Should().Be(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_series_runtime_is_zero_and_single_episode_aired_more_than_24_hours_after_first_aired_episode()
|
||||
{
|
||||
series.Runtime = 0;
|
||||
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Release.Size = 200.Megabytes();
|
||||
parseResultSingle.Episodes.First().Id = 5;
|
||||
parseResultSingle.Episodes.First().SeasonNumber = 1;
|
||||
parseResultSingle.Episodes.First().EpisodeNumber = 2;
|
||||
parseResultSingle.Episodes.First().AirDateUtc = episodes.First().AirDateUtc.Value.AddDays(7);
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, null).Accepted.Should().Be(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_series_runtime_is_zero_and_single_episode_aired_less_than_24_hours_after_first_aired_episode()
|
||||
{
|
||||
series.Runtime = 0;
|
||||
|
||||
parseResultSingle.Series = series;
|
||||
parseResultSingle.Release.Size = 200.Megabytes();
|
||||
parseResultSingle.Episodes.First().Id = 5;
|
||||
parseResultSingle.Episodes.First().SeasonNumber = 1;
|
||||
parseResultSingle.Episodes.First().EpisodeNumber = 2;
|
||||
parseResultSingle.Episodes.First().AirDateUtc = episodes.First().AirDateUtc.Value.AddHours(1);
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultSingle, null).Accepted.Should().Be(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_series_runtime_is_zero_and_multi_episode_is_not_from_first_season()
|
||||
{
|
||||
series.Runtime = 0;
|
||||
parseResultMulti.Series = series;
|
||||
parseResultMulti.Release.Size = 200.Megabytes();
|
||||
parseResultMulti.Episodes.ForEach(e => e.SeasonNumber = 2);
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultMulti, null).Accepted.Should().Be(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_false_if_series_runtime_is_zero_and_multi_episode_aired_more_than_24_hours_after_first_aired_episode()
|
||||
{
|
||||
var airDateUtc = episodes.First().AirDateUtc.Value.AddDays(7);
|
||||
|
||||
series.Runtime = 0;
|
||||
|
||||
parseResultMulti.Series = series;
|
||||
parseResultMulti.Release.Size = 200.Megabytes();
|
||||
parseResultMulti.Episodes.ForEach(e =>
|
||||
{
|
||||
e.SeasonNumber = 1;
|
||||
e.AirDateUtc = airDateUtc;
|
||||
});
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultMulti, null).Accepted.Should().Be(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_true_if_series_runtime_is_zero_and_multi_episode_aired_less_than_24_hours_after_first_aired_episode()
|
||||
{
|
||||
var airDateUtc = episodes.First().AirDateUtc.Value.AddHours(1);
|
||||
|
||||
series.Runtime = 0;
|
||||
|
||||
parseResultMulti.Series = series;
|
||||
parseResultMulti.Release.Size = 200.Megabytes();
|
||||
parseResultMulti.Episodes.ForEach(e =>
|
||||
{
|
||||
e.SeasonNumber = 1;
|
||||
e.AirDateUtc = airDateUtc;
|
||||
});
|
||||
|
||||
Subject.IsSatisfiedBy(parseResultMulti, null).Accepted.Should().Be(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
@ -43,18 +44,47 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
return Decision.Accept();
|
||||
}
|
||||
|
||||
var runtime = subject.Series.Runtime;
|
||||
|
||||
if (runtime == 0)
|
||||
{
|
||||
var firstSeasonNumber = subject.Series.Seasons.Where(s => s.SeasonNumber > 0).Min(s => s.SeasonNumber);
|
||||
var pilotEpisode = _episodeService.GetEpisodesBySeason(subject.Series.Id, firstSeasonNumber).First();
|
||||
|
||||
if (subject.Episodes.First().SeasonNumber == pilotEpisode.SeasonNumber)
|
||||
{
|
||||
// If the first episode has an air date use it, otherwise use the release's publish date because like runtime it may not have updated yet.
|
||||
var gracePeriodEnd = (pilotEpisode.AirDateUtc ?? subject.Release.PublishDate).AddHours(24);
|
||||
|
||||
// If episodes don't have an air date that is okay, otherwise make sure it's within 24 hours of the first episode airing.
|
||||
if (subject.Episodes.All(e => !e.AirDateUtc.HasValue || e.AirDateUtc.Value.Before(gracePeriodEnd)))
|
||||
{
|
||||
_logger.Debug("Series runtime is 0, but all episodes in release aired within 24 hours of first episode in season, defaulting runtime to 45 minutes");
|
||||
runtime = 45;
|
||||
}
|
||||
}
|
||||
|
||||
// Reject if the run time is still 0
|
||||
if (runtime == 0)
|
||||
{
|
||||
_logger.Debug("Series runtime is 0, unable to validate size until it is available, rejecting");
|
||||
return Decision.Reject("Series runtime is 0, unable to validate size until it is available");
|
||||
}
|
||||
}
|
||||
|
||||
var qualityDefinition = _qualityDefinitionService.Get(quality);
|
||||
|
||||
if (qualityDefinition.MinSize.HasValue)
|
||||
{
|
||||
var minSize = qualityDefinition.MinSize.Value.Megabytes();
|
||||
|
||||
//Multiply maxSize by Series.Runtime
|
||||
minSize = minSize * subject.Series.Runtime * subject.Episodes.Count;
|
||||
// Multiply maxSize by Series.Runtime
|
||||
minSize = minSize * runtime * subject.Episodes.Count;
|
||||
|
||||
//If the parsed size is smaller than minSize we don't want it
|
||||
// If the parsed size is smaller than minSize we don't want it
|
||||
if (subject.Release.Size < minSize)
|
||||
{
|
||||
var runtimeMessage = subject.Episodes.Count == 1 ? $"{subject.Series.Runtime}min" : $"{subject.Episodes.Count}x {subject.Series.Runtime}min";
|
||||
var runtimeMessage = subject.Episodes.Count == 1 ? $"{runtime}min" : $"{subject.Episodes.Count}x {runtime}min";
|
||||
|
||||
_logger.Debug("Item: {0}, Size: {1} is smaller than minimum allowed size ({2} bytes for {3}), rejecting.", subject, subject.Release.Size, minSize, runtimeMessage);
|
||||
return Decision.Reject("{0} is smaller than minimum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), minSize.SizeSuffix(), runtimeMessage);
|
||||
|
@ -64,46 +94,31 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
{
|
||||
_logger.Debug("Max size is unlimited, skipping size check");
|
||||
}
|
||||
else if (subject.Series.Runtime == 0)
|
||||
{
|
||||
_logger.Debug("Series runtime is 0, unable to validate size until it is available, rejecting");
|
||||
return Decision.Reject("Series runtime is 0, unable to validate size until it is available");
|
||||
}
|
||||
else
|
||||
{
|
||||
var maxSize = qualityDefinition.MaxSize.Value.Megabytes();
|
||||
|
||||
//Multiply maxSize by Series.Runtime
|
||||
maxSize = maxSize * subject.Series.Runtime * subject.Episodes.Count;
|
||||
// Multiply maxSize by Series.Runtime
|
||||
maxSize = maxSize * runtime * subject.Episodes.Count;
|
||||
|
||||
if (subject.Episodes.Count == 1 && subject.Series.SeriesType == SeriesTypes.Standard)
|
||||
{
|
||||
Episode episode = subject.Episodes.First();
|
||||
List<Episode> seasonEpisodes;
|
||||
var firstEpisode = subject.Episodes.First();
|
||||
var seasonEpisodes = GetSeasonEpisodes(subject, searchCriteria);
|
||||
|
||||
var seasonSearchCriteria = searchCriteria as SeasonSearchCriteria;
|
||||
if (seasonSearchCriteria != null && !seasonSearchCriteria.Series.UseSceneNumbering && seasonSearchCriteria.Episodes.Any(v => v.Id == episode.Id))
|
||||
{
|
||||
seasonEpisodes = seasonSearchCriteria.Episodes;
|
||||
}
|
||||
else
|
||||
{
|
||||
seasonEpisodes = _episodeService.GetEpisodesBySeason(episode.SeriesId, episode.SeasonNumber);
|
||||
}
|
||||
|
||||
//Ensure that this is either the first episode
|
||||
//or is the last episode in a season that has 10 or more episodes
|
||||
if (seasonEpisodes.First().Id == episode.Id || (seasonEpisodes.Count() >= 10 && seasonEpisodes.Last().Id == episode.Id))
|
||||
// Ensure that this is either the first episode
|
||||
// or is the last episode in a season that has 10 or more episodes
|
||||
if (seasonEpisodes.First().Id == firstEpisode.Id || (seasonEpisodes.Count() >= 10 && seasonEpisodes.Last().Id == firstEpisode.Id))
|
||||
{
|
||||
_logger.Debug("Possible double episode, doubling allowed size.");
|
||||
maxSize = maxSize * 2;
|
||||
}
|
||||
}
|
||||
|
||||
//If the parsed size is greater than maxSize we don't want it
|
||||
// If the parsed size is greater than maxSize we don't want it
|
||||
if (subject.Release.Size > maxSize)
|
||||
{
|
||||
var runtimeMessage = subject.Episodes.Count == 1 ? $"{subject.Series.Runtime}min" : $"{subject.Episodes.Count}x {subject.Series.Runtime}min";
|
||||
var runtimeMessage = subject.Episodes.Count == 1 ? $"{runtime}min" : $"{subject.Episodes.Count}x {runtime}min";
|
||||
|
||||
_logger.Debug("Item: {0}, Size: {1} is greater than maximum allowed size ({2} for {3}), rejecting", subject, subject.Release.Size, maxSize, runtimeMessage);
|
||||
return Decision.Reject("{0} is larger than maximum allowed {1} (for {2})", subject.Release.Size.SizeSuffix(), maxSize.SizeSuffix(), runtimeMessage);
|
||||
|
@ -113,5 +128,17 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||
_logger.Debug("Item: {0}, meets size constraints", subject);
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
private List<Episode> GetSeasonEpisodes(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var firstEpisode = subject.Episodes.First();
|
||||
|
||||
if (searchCriteria is SeasonSearchCriteria seasonSearchCriteria && !seasonSearchCriteria.Series.UseSceneNumbering && seasonSearchCriteria.Episodes.Any(v => v.Id == firstEpisode.Id))
|
||||
{
|
||||
return seasonSearchCriteria.Episodes;
|
||||
}
|
||||
|
||||
return _episodeService.GetEpisodesBySeason(firstEpisode.SeriesId, firstEpisode.SeasonNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue