diff --git a/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js b/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js index 8960cd2b7..2f821ccff 100644 --- a/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js +++ b/frontend/src/Settings/Profiles/Delay/EditDelayProfileModalContent.js @@ -39,6 +39,7 @@ function EditDelayProfileModalContent(props) { enableTorrent, usenetDelay, torrentDelay, + bypassIfHighestQuality, tags } = item; @@ -107,6 +108,20 @@ function EditDelayProfileModalContent(props) { } + { + + Bypass if Highest Quality + + + + } + { id === 1 ? diff --git a/src/NzbDrone.Api/Profiles/Delay/DelayProfileResource.cs b/src/NzbDrone.Api/Profiles/Delay/DelayProfileResource.cs index 53b7c4f19..569f972d7 100644 --- a/src/NzbDrone.Api/Profiles/Delay/DelayProfileResource.cs +++ b/src/NzbDrone.Api/Profiles/Delay/DelayProfileResource.cs @@ -13,6 +13,7 @@ namespace NzbDrone.Api.Profiles.Delay public DownloadProtocol PreferredProtocol { get; set; } public int UsenetDelay { get; set; } public int TorrentDelay { get; set; } + public bool BypassIfHighestQuality { get; set; } public int Order { get; set; } public HashSet Tags { get; set; } } @@ -32,6 +33,7 @@ namespace NzbDrone.Api.Profiles.Delay PreferredProtocol = model.PreferredProtocol, UsenetDelay = model.UsenetDelay, TorrentDelay = model.TorrentDelay, + BypassIfHighestQuality = model.BypassIfHighestQuality, Order = model.Order, Tags = new HashSet(model.Tags) }; @@ -50,6 +52,7 @@ namespace NzbDrone.Api.Profiles.Delay PreferredProtocol = resource.PreferredProtocol, UsenetDelay = resource.UsenetDelay, TorrentDelay = resource.TorrentDelay, + BypassIfHighestQuality = resource.BypassIfHighestQuality, Order = resource.Order, Tags = new HashSet(resource.Tags) }; diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs index 006b2412c..deb2d8ba2 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs @@ -124,7 +124,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync } [Test] - public void should_be_true_when_quality_and_language_is_last_allowed_in_profile() + public void should_be_false_when_quality_and_language_is_last_allowed_in_profile_and_bypass_disabled() { _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray720p); _remoteEpisode.ParsedEpisodeInfo.Language = Language.French; @@ -132,6 +132,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); } + [Test] + public void should_be_true_when_quality_and_language_is_last_allowed_in_profile_and_bypass_enabled() + { + _delayProfile.BypassIfHighestQuality = true; + + _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray720p); + _remoteEpisode.ParsedEpisodeInfo.Language = Language.French; + + Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + } + [Test] public void should_be_true_when_release_is_older_than_delay() { diff --git a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs index cff5872cb..64823598c 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs @@ -218,5 +218,39 @@ namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests Mocker.GetMock() .Verify(v => v.FindByTitle(It.IsAny()), Times.Never()); } + + [Test] + public void should_handle_anime_underrange() + { + var title = "[DroneRaws] Phantom - The Final Countdown - 04 [1080p].mkv"; + + var parsedEpisodeInfo = Parser.Parser.ParseTitle(title); + + Mocker.GetMock() + .Setup(s => s.FindSceneMapping(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(new SceneMapping + { + Title = "Phantom: The Final Countdown", + ParseTerm = "phantomthefinalcountdown", + SceneSeasonNumber = 4, + SeasonNumber = -1, + TvdbId = 100 + }); + + Mocker.GetMock() + .Setup(s => s.GetSceneSeasonNumber(It.IsAny(), It.IsAny())) + .Returns(4); + + Mocker.GetMock() + .Setup(s => s.FindByTvdbId(It.IsAny())) + .Returns(new Series + { + Id = 1, + TvdbId = 100, + Title = "Phantom" + }); + + var remoteEpisode = Subject.Map(parsedEpisodeInfo, 0, 0, null); + } } } diff --git a/src/NzbDrone.Core/Datastore/Migration/156_add_bypass_to_delay_profile.cs b/src/NzbDrone.Core/Datastore/Migration/156_add_bypass_to_delay_profile.cs new file mode 100644 index 000000000..c53fb895e --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/156_add_bypass_to_delay_profile.cs @@ -0,0 +1,17 @@ +using FluentMigrator; +using Newtonsoft.Json.Linq; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(156)] + public class add_bypass_to_delay_profile : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("DelayProfiles").AddColumn("BypassIfHighestQuality").AsBoolean().WithDefaultValue(false); + // Set to true for existing Delay Profiles to keep behavior the same. + Execute.Sql("UPDATE DelayProfiles SET BypassIfHighestQuality = 1;"); + } + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs index 6979f5042..8819ba686 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs @@ -67,14 +67,17 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync } // If quality meets or exceeds the best allowed quality in the profile accept it immediately - var bestQualityInProfile = qualityProfile.LastAllowedQuality(); - var isBestInProfile = qualityComparer.Compare(subject.ParsedEpisodeInfo.Quality.Quality, bestQualityInProfile) >= 0; - var isBestInProfileLanguage = languageComparer.Compare(subject.ParsedEpisodeInfo.Language, languageProfile.LastAllowedLanguage()) >= 0; - - if (isBestInProfile && isBestInProfileLanguage && isPreferredProtocol) + if (delayProfile.BypassIfHighestQuality) { - _logger.Debug("Quality and language is highest in profile for preferred protocol, will not delay"); - return Decision.Accept(); + var bestQualityInProfile = qualityProfile.LastAllowedQuality(); + var isBestInProfile = qualityComparer.Compare(subject.ParsedEpisodeInfo.Quality.Quality, bestQualityInProfile) >= 0; + var isBestInProfileLanguage = languageComparer.Compare(subject.ParsedEpisodeInfo.Language, languageProfile.LastAllowedLanguage()) >= 0; + + if (isBestInProfile && isBestInProfileLanguage && isPreferredProtocol) + { + _logger.Debug("Quality and language is highest in profile for preferred protocol, will not delay"); + return Decision.Accept(); + } } var episodeIds = subject.Episodes.Select(e => e.Id); diff --git a/src/NzbDrone.Core/Profiles/Delay/DelayProfile.cs b/src/NzbDrone.Core/Profiles/Delay/DelayProfile.cs index ef20bb6a5..81100d6f5 100644 --- a/src/NzbDrone.Core/Profiles/Delay/DelayProfile.cs +++ b/src/NzbDrone.Core/Profiles/Delay/DelayProfile.cs @@ -12,6 +12,7 @@ namespace NzbDrone.Core.Profiles.Delay public int UsenetDelay { get; set; } public int TorrentDelay { get; set; } public int Order { get; set; } + public bool BypassIfHighestQuality { get; set; } public HashSet Tags { get; set; } public DelayProfile() diff --git a/src/Sonarr.Api.V3/Profiles/Delay/DelayProfileResource.cs b/src/Sonarr.Api.V3/Profiles/Delay/DelayProfileResource.cs index 4ce6ec189..fb83debe5 100644 --- a/src/Sonarr.Api.V3/Profiles/Delay/DelayProfileResource.cs +++ b/src/Sonarr.Api.V3/Profiles/Delay/DelayProfileResource.cs @@ -13,6 +13,7 @@ namespace Sonarr.Api.V3.Profiles.Delay public DownloadProtocol PreferredProtocol { get; set; } public int UsenetDelay { get; set; } public int TorrentDelay { get; set; } + public bool BypassIfHighestQuality { get; set; } public int Order { get; set; } public HashSet Tags { get; set; } } @@ -32,6 +33,7 @@ namespace Sonarr.Api.V3.Profiles.Delay PreferredProtocol = model.PreferredProtocol, UsenetDelay = model.UsenetDelay, TorrentDelay = model.TorrentDelay, + BypassIfHighestQuality = model.BypassIfHighestQuality, Order = model.Order, Tags = new HashSet(model.Tags) }; @@ -50,6 +52,7 @@ namespace Sonarr.Api.V3.Profiles.Delay PreferredProtocol = resource.PreferredProtocol, UsenetDelay = resource.UsenetDelay, TorrentDelay = resource.TorrentDelay, + BypassIfHighestQuality = resource.BypassIfHighestQuality, Order = resource.Order, Tags = new HashSet(resource.Tags) };