From 4d8bcd12e33592eab36647dc8e40b0cdcaf97fe8 Mon Sep 17 00:00:00 2001 From: Qstick Date: Thu, 21 Mar 2019 18:02:16 -0400 Subject: [PATCH] Fixed: Cutoff Specification not Respecting Profile Order (#660) * Fixed: Cutoff Specification not Repsecting Profile Order * Fixed: Incorrect wording in UpgradeAllowed logging * Fixed: Change Logic to update if upgrade for any, downgrade for none. * Fixed: Removed Double Preferred Word Logic * New: Add Test Cases to Disk Upgrade Spec * Fixed: Cleanup UpgradableSpecification * Add ConcatToString extension and fix logging * Fixed: Enum Naming, Commas --- .../Extensions/IEnumerableExtensions.cs | 10 + .../CutoffSpecificationFixture.cs | 42 +-- .../RssSync/DelaySpecificationFixture.cs | 2 +- .../UpgradeAllowedSpecificationFixture.cs | 293 ++++++++++++++++++ .../UpgradeDiskSpecificationFixture.cs | 28 +- .../UpgradeSpecificationFixture.cs | 13 +- .../NzbDrone.Core.Test.csproj | 1 + .../LanguageUpgradableSpecification.cs | 75 ----- .../Specifications/CutoffSpecification.cs | 28 +- .../Specifications/QueueSpecification.cs | 41 ++- .../RssSync/DelaySpecification.cs | 8 +- .../RssSync/HistorySpecification.cs | 11 +- .../Specifications/UpgradableSpecification.cs | 132 ++++++-- .../UpgradeAllowedSpecification.cs | 76 +++++ .../UpgradeDiskSpecification.cs | 10 +- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + 16 files changed, 602 insertions(+), 169 deletions(-) create mode 100644 src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeAllowedSpecificationFixture.cs delete mode 100644 src/NzbDrone.Core/DecisionEngine/LanguageUpgradableSpecification.cs create mode 100644 src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs diff --git a/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs b/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs index 751e3dfca..85a288aee 100644 --- a/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs +++ b/src/NzbDrone.Common/Extensions/IEnumerableExtensions.cs @@ -137,5 +137,15 @@ namespace NzbDrone.Common.Extensions { return source.Select(predicate).ToList(); } + + public static string ConcatToString(this IEnumerable source, string separator = ", ") + { + return string.Join(separator, source.Select(x => x.ToString())); + } + + public static string ConcatToString(this IEnumerable source, Func predicate, string separator = ", ") + { + return string.Join(separator, source.Select(predicate)); + } } } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs index 1aeaa7da6..a9caa32a6 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs @@ -7,6 +7,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Languages; using NzbDrone.Core.Profiles.Languages; using NzbDrone.Core.Test.Languages; +using System.Collections.Generic; namespace NzbDrone.Core.Test.DecisionEngineTests { @@ -30,7 +31,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Languages = LanguageFixture.GetDefaultLanguages(Language.English), Cutoff = Language.English }, - new QualityModel(Quality.MP3_192, new Revision(version: 2)), Language.English, NoPreferredWordScore).Should().BeTrue(); + new List { new QualityModel(Quality.MP3_192, new Revision(version: 2)) }, + new List { Language.English }, NoPreferredWordScore).Should().BeTrue(); } [Test] @@ -47,7 +49,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Languages = LanguageFixture.GetDefaultLanguages(Language.English), Cutoff = Language.English }, - new QualityModel(Quality.MP3_256, new Revision(version: 2)), Language.English, NoPreferredWordScore).Should().BeFalse(); + new List { new QualityModel(Quality.MP3_256, new Revision(version: 2)) }, + new List { Language.English }, NoPreferredWordScore).Should().BeFalse(); } [Test] @@ -65,7 +68,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Languages = LanguageFixture.GetDefaultLanguages(Language.English), Cutoff = Language.English }, - new QualityModel(Quality.MP3_320, new Revision(version: 2)), Language.English, NoPreferredWordScore).Should().BeFalse(); + new List { new QualityModel(Quality.MP3_320, new Revision(version: 2)) }, + new List { Language.English }, NoPreferredWordScore).Should().BeFalse(); } [Test] @@ -83,8 +87,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Languages = LanguageFixture.GetDefaultLanguages(Language.English), Cutoff = Language.English }, - new QualityModel(Quality.MP3_320, new Revision(version: 1)), - Language.English, + new List { new QualityModel(Quality.MP3_320, new Revision(version: 1)) }, + new List { Language.English }, NoPreferredWordScore, new QualityModel(Quality.MP3_320, new Revision(version: 2))).Should().BeTrue(); @@ -105,7 +109,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Languages = LanguageFixture.GetDefaultLanguages(Language.English), Cutoff = Language.English }, - new QualityModel(Quality.MP3_320, new Revision(version: 2)), Language.English, + new List { new QualityModel(Quality.MP3_320, new Revision(version: 2)) }, + new List { Language.English }, NoPreferredWordScore, new QualityModel(Quality.FLAC, new Revision(version: 2))).Should().BeFalse(); } @@ -128,8 +133,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.CutoffNotMet(_profile, _langProfile, - new QualityModel(Quality.MP3_320, new Revision(version: 2)), - Language.English, + new List { new QualityModel(Quality.MP3_320, new Revision(version: 2)) }, + new List { Language.English }, NoPreferredWordScore, new QualityModel(Quality.FLAC, new Revision(version: 2))).Should().BeTrue(); } @@ -153,8 +158,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.CutoffNotMet( _profile, _langProfile, - new QualityModel(Quality.MP3_320, new Revision(version: 2)), - Language.Spanish, + new List { new QualityModel(Quality.MP3_320, new Revision(version: 2)) }, + new List { Language.Spanish }, NoPreferredWordScore, new QualityModel(Quality.FLAC, new Revision(version: 2))).Should().BeFalse(); } @@ -178,8 +183,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.CutoffNotMet( _profile, _langProfile, - new QualityModel(Quality.MP3_320, new Revision(version: 2)), - Language.French, + new List { new QualityModel(Quality.MP3_320, new Revision(version: 2)) }, + new List { Language.French }, NoPreferredWordScore, new QualityModel(Quality.FLAC, new Revision(version: 2))).Should().BeFalse(); } @@ -203,8 +208,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.CutoffNotMet( _profile, _langProfile, - new QualityModel(Quality.MP3_256, new Revision(version: 2)), - Language.French, + new List { new QualityModel(Quality.MP3_256, new Revision(version: 2)) }, + new List { Language.French }, NoPreferredWordScore, new QualityModel(Quality.FLAC, new Revision(version: 2))).Should().BeTrue(); } @@ -228,8 +233,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.CutoffNotMet( _profile, _langProfile, - new QualityModel(Quality.MP3_256, new Revision(version: 2)), - Language.French, NoPreferredWordScore).Should().BeTrue(); + new List { new QualityModel(Quality.MP3_256, new Revision(version: 2)) }, + new List { Language.French }, + NoPreferredWordScore).Should().BeTrue(); } [Test] @@ -250,8 +256,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.CutoffNotMet( _profile, _langProfile, - new QualityModel(Quality.MP3_320, new Revision(version: 2)), - Language.Spanish, + new List { new QualityModel(Quality.MP3_320, new Revision(version: 2)) }, + new List { Language.Spanish }, NoPreferredWordScore, new QualityModel(Quality.FLAC, new Revision(version: 2)), 10).Should().BeTrue(); diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs index 206e5a241..9a614ba9b 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs @@ -96,7 +96,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync private void GivenUpgradeForExistingFile() { Mocker.GetMock() - .Setup(s => s.IsUpgradable(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(s => s.IsUpgradable(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(true); } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeAllowedSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeAllowedSpecificationFixture.cs new file mode 100644 index 000000000..258c7ae75 --- /dev/null +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeAllowedSpecificationFixture.cs @@ -0,0 +1,293 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Profiles.Qualities; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.DecisionEngine.Specifications; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Languages; +using NzbDrone.Core.Profiles.Languages; +using NzbDrone.Core.Test.Languages; +using System.Collections.Generic; + +namespace NzbDrone.Core.Test.DecisionEngineTests +{ + [TestFixture] + public class UpgradeAllowedSpecificationFixture : CoreTest + { + [Test] + public void should_return_false_when_quality_are_the_same_language_is_better_and_upgrade_allowed_is_false_for_language_profile() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English, Language.French), + Cutoff = Language.French, + UpgradeAllowed = false + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.MP3_320), + Language.French + ).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_quality_is_better_languages_are_the_same_and_upgrade_allowed_is_false_for_quality_profile() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = false + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English), + Cutoff = Language.English, + UpgradeAllowed = true + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.FLAC), + Language.English + ).Should().BeFalse(); + } + + [Test] + public void should_return_true_for_language_upgrade_when_upgrading_is_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English, Language.French), + Cutoff = Language.French, + UpgradeAllowed = true + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.MP3_320), + Language.French + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_same_language_when_upgrading_is_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English, Language.French), + Cutoff = Language.French, + UpgradeAllowed = true + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.MP3_320), + Language.English + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_same_language_when_upgrading_is_not_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English, Language.French), + Cutoff = Language.French, + UpgradeAllowed = false + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.French }, + new QualityModel(Quality.MP3_320), + Language.English + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_lower_language_when_upgrading_is_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English, Language.French), + Cutoff = Language.French, + UpgradeAllowed = true + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.French }, + new QualityModel(Quality.MP3_320), + Language.English + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_lower_language_when_upgrading_is_not_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English, Language.French), + Cutoff = Language.French, + UpgradeAllowed = false + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.French }, + new QualityModel(Quality.MP3_320), + Language.English + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_quality_upgrade_when_upgrading_is_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English), + Cutoff = Language.English, + UpgradeAllowed = true + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.FLAC), + Language.English + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_same_quality_when_upgrading_is_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English), + Cutoff = Language.English, + UpgradeAllowed = true + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.MP3_320), + Language.English + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_same_quality_when_upgrading_is_not_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = false + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English), + Cutoff = Language.English, + UpgradeAllowed = true + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.MP3_320), + Language.English + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_lower_quality_when_upgrading_is_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = true + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English), + Cutoff = Language.English, + UpgradeAllowed = true + }, + new List { new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.MP3_256), + Language.English + ).Should().BeTrue(); + } + + [Test] + public void should_return_true_for_lower_quality_when_upgrading_is_not_allowed() + { + Subject.IsUpgradeAllowed( + new QualityProfile + { + Cutoff = Quality.FLAC.Id, + Items = Qualities.QualityFixture.GetDefaultQualities(), + UpgradeAllowed = false + }, + new LanguageProfile + { + Languages = LanguageFixture.GetDefaultLanguages(Language.English), + Cutoff = Language.English, + UpgradeAllowed = true + }, + new List{ new QualityModel(Quality.MP3_320) }, + new List { Language.English }, + new QualityModel(Quality.MP3_256), + Language.English + ).Should().BeTrue(); + } + } +} diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs index 26828524a..60b0b44a9 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs @@ -34,8 +34,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _firstFile = new TrackFile { Quality = new QualityModel(Quality.FLAC, new Revision(version: 2)), DateAdded = DateTime.Now, Language = Language.English }; _secondFile = new TrackFile { Quality = new QualityModel(Quality.FLAC, new Revision(version: 2)), DateAdded = DateTime.Now, Language = Language.English }; - var singleEpisodeList = new List { new Album {}}; - var doubleEpisodeList = new List { new Album {}, new Album {}, new Album {} }; + var singleAlbumList = new List { new Album {}}; + var doubleAlbumList = new List { new Album {}, new Album {}, new Album {} }; var languages = Languages.LanguageFixture.GetDefaultLanguages(Language.English, Language.Spanish); @@ -66,14 +66,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Artist = fakeArtist, ParsedAlbumInfo = new ParsedAlbumInfo { Quality = new QualityModel(Quality.MP3_256, new Revision(version: 2)), Language = Language.English }, - Albums = doubleEpisodeList + Albums = doubleAlbumList }; _parseResultSingle = new RemoteAlbum { Artist = fakeArtist, ParsedAlbumInfo = new ParsedAlbumInfo { Quality = new QualityModel(Quality.MP3_256, new Revision(version: 2)), Language = Language.English }, - Albums = singleEpisodeList + Albums = singleAlbumList }; } @@ -127,9 +127,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests } [Test] - public void should_be_upgradable_if_album_is_upgradable() + public void should_be_upgradable_if_all_files_are_upgradable() { WithFirstFileUpgradable(); + WithSecondFileUpgradable(); Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue(); } @@ -137,6 +138,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_not_be_upgradable_if_qualities_are_the_same() { _firstFile.Quality = new QualityModel(Quality.MP3_320); + _secondFile.Quality = new QualityModel(Quality.MP3_320); _parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_320); Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); } @@ -146,5 +148,21 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); } + + [Test] + public void should_be_true_if_some_tracks_are_upgradable_and_none_are_downgrades() + { + WithFirstFileUpgradable(); + _parseResultSingle.ParsedAlbumInfo.Quality = _secondFile.Quality; + Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_be_false_if_some_tracks_are_upgradable_and_some_are_downgrades() + { + WithFirstFileUpgradable(); + _parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_320); + Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); + } } } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeSpecificationFixture.cs index 2e243bac3..9b0141a22 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeSpecificationFixture.cs @@ -8,6 +8,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Languages; using NzbDrone.Core.Profiles.Languages; using NzbDrone.Core.Test.Languages; +using System.Collections.Generic; namespace NzbDrone.Core.Test.DecisionEngineTests { @@ -65,8 +66,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.IsUpgradable( profile, langProfile, - new QualityModel(current, new Revision(version: currentVersion)), - Language.English, + new List { new QualityModel(current, new Revision(version: currentVersion)) }, + new List { Language.English }, NoPreferredWordScore, new QualityModel(newQuality, new Revision(version: newVersion)), Language.English, @@ -96,8 +97,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.IsUpgradable( profile, langProfile, - new QualityModel(current, new Revision(version: currentVersion)), - currentLanguage, + new List { new QualityModel(current, new Revision(version: currentVersion)) }, + new List { currentLanguage }, NoPreferredWordScore, new QualityModel(newQuality, new Revision(version: newVersion)), newLanguage, @@ -125,8 +126,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.IsUpgradable( profile, langProfile, - new QualityModel(Quality.MP3_256, new Revision(version: 2)), - Language.English, + new List { new QualityModel(Quality.MP3_256, new Revision(version: 2)) }, + new List { Language.English }, NoPreferredWordScore, new QualityModel(Quality.MP3_256, new Revision(version: 1)), Language.English, diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 4231188f7..d76b32a14 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -147,6 +147,7 @@ + diff --git a/src/NzbDrone.Core/DecisionEngine/LanguageUpgradableSpecification.cs b/src/NzbDrone.Core/DecisionEngine/LanguageUpgradableSpecification.cs deleted file mode 100644 index 4f150279b..000000000 --- a/src/NzbDrone.Core/DecisionEngine/LanguageUpgradableSpecification.cs +++ /dev/null @@ -1,75 +0,0 @@ -using NLog; -using NzbDrone.Core.Languages; -using NzbDrone.Core.Profiles; -using NzbDrone.Core.Qualities; - -namespace NzbDrone.Core.DecisionEngine -{ - public interface ILanguageUpgradableSpecification - { - bool IsUpgradable(Profile profile, LanguageModel currentLanguage, LanguageModel newLanguage = null); - bool CutoffNotMet(Profile profile, LanguageModel currentLanguage, LanguageModel newLanguage = null); - bool IsRevisionUpgrade(LanguageModel currentLanguage, LanguageModel newLanguage); - } - - public class LanguageUpgradableSpecification : ILanguageUpgradableSpecification - { - private readonly Logger _logger; - - public LanguageUpgradableSpecification(Logger logger) - { - _logger = logger; - } - - public bool IsUpgradable(Profile profile, LanguageModel currentLanguage, LanguageModel newLanguage = null) - { - if (newLanguage != null) - { - int compare = new LanguageModelComparer(profile).Compare(newLanguage, currentLanguage); - if (compare <= 0) - { - _logger.Debug("existing item has better or equal language. skipping"); - return false; - } - - if (IsRevisionUpgrade(currentLanguage, newLanguage)) - { - return true; - } - } - - return true; - } - - public bool CutoffNotMet(Profile profile, LanguageModel currentLanguage, LanguageModel newLanguage = null) - { - int compare = new LanguageModelComparer(profile).Compare(currentLanguage.Language, profile.Languages.Find(v => v.Allowed == true).Language); - - if (compare >= 0) - { - if (newLanguage != null && IsRevisionUpgrade(currentLanguage, newLanguage)) - { - return true; - } - - _logger.Debug("Existing item meets cut-off. skipping."); - return false; - } - - return true; - } - - public bool IsRevisionUpgrade(LanguageModel currentLanguage, LanguageModel newLanguage) - { - int compare = newLanguage.Revision.CompareTo(currentLanguage.Revision); - - if (currentLanguage.Language == newLanguage.Language && compare > 0) - { - _logger.Debug("New language is a better revision for existing quality"); - return true; - } - - return false; - } - } -} diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs index 2332538d9..321252e2d 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs @@ -7,6 +7,7 @@ using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Music; using NzbDrone.Common.Cache; using NzbDrone.Core.Profiles.Releases; +using NzbDrone.Common.Extensions; namespace NzbDrone.Core.DecisionEngine.Specifications { @@ -39,8 +40,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria) { - - var profile = subject.Artist.QualityProfile.Value; + var qualityProfile = subject.Artist.QualityProfile.Value; + var languageProfile = subject.Artist.LanguageProfile.Value; foreach (var album in subject.Albums) { @@ -50,23 +51,26 @@ namespace NzbDrone.Core.DecisionEngine.Specifications if (!tracksMissing && trackFiles.Any()) { - var lowestQuality = trackFiles.Select(c => c.Quality).OrderBy(c => c.Quality.Id).First(); + // Get a distinct list of all current track qualities and languages for a given album + var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList(); + var currentLanguages = trackFiles.Select(c => c.Language).Distinct().ToList(); - _logger.Debug("Comparing file quality and language with report. Existing file is {0}", lowestQuality.Quality); + _logger.Debug("Comparing file quality and language with report. Existing files contain {0} : {1}", currentQualities.ConcatToString(), currentLanguages.ConcatToString()); - if (!_upgradableSpecification.CutoffNotMet(profile, - subject.Artist.LanguageProfile, - lowestQuality, - trackFiles[0].Language, + if (!_upgradableSpecification.CutoffNotMet(qualityProfile, + languageProfile, + currentQualities, + currentLanguages, _preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName()), subject.ParsedAlbumInfo.Quality, subject.PreferredWordScore)) { - _logger.Debug("Cutoff already met, rejecting."); - var qualityCutoffIndex = profile.GetIndex(profile.Cutoff); - var qualityCutoff = profile.Items[qualityCutoffIndex.Index]; + _logger.Debug("Cutoff already met by existing files, rejecting."); - return Decision.Reject("Existing file meets cutoff: {0} - {1}", qualityCutoff, subject.Artist.LanguageProfile.Value.Cutoff); + var qualityCutoffIndex = qualityProfile.GetIndex(qualityProfile.Cutoff); + var qualityCutoff = qualityProfile.Items[qualityCutoffIndex.Index]; + + return Decision.Reject("Existing files meets cutoff: {0} - {1}", qualityCutoff, languageProfile.Cutoff); } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs index b137ca527..3745bec50 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs @@ -1,8 +1,11 @@ +using System.Collections.Generic; using System.Linq; using NLog; using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Profiles.Releases; +using NzbDrone.Core.Qualities; using NzbDrone.Core.Queue; namespace NzbDrone.Core.DecisionEngine.Specifications @@ -41,34 +44,48 @@ namespace NzbDrone.Core.DecisionEngine.Specifications foreach (var queueItem in matchingAlbum) { var remoteAlbum = queueItem.RemoteAlbum; - + var qualityProfile = subject.Artist.QualityProfile.Value; + var languageProfile = subject.Artist.LanguageProfile.Value; + _logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0} - {1}", remoteAlbum.ParsedAlbumInfo.Quality, remoteAlbum.ParsedAlbumInfo.Language); var queuedItemPreferredWordScore = _preferredWordServiceCalculator.Calculate(subject.Artist, queueItem.Title); - if (!_upgradableSpecification.CutoffNotMet(subject.Artist.QualityProfile, - subject.Artist.LanguageProfile, - remoteAlbum.ParsedAlbumInfo.Quality, - remoteAlbum.ParsedAlbumInfo.Language, + if (!_upgradableSpecification.CutoffNotMet(qualityProfile, + languageProfile, + new List { remoteAlbum.ParsedAlbumInfo.Quality }, + new List { remoteAlbum.ParsedAlbumInfo.Language }, queuedItemPreferredWordScore, subject.ParsedAlbumInfo.Quality, subject.PreferredWordScore)) { - return Decision.Reject("Quality for release in queue already meets cutoff: {0}", remoteAlbum.ParsedAlbumInfo.Quality); + return Decision.Reject("Release in queue already meets cutoff: {0}", remoteAlbum.ParsedAlbumInfo.Quality); } - _logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0} - {1}", remoteAlbum.ParsedAlbumInfo.Quality, remoteAlbum.ParsedAlbumInfo.Language); + _logger.Debug("Checking if release is higher quality than queued release. Queued: {0} - {1}", remoteAlbum.ParsedAlbumInfo.Quality, remoteAlbum.ParsedAlbumInfo.Language); - if (!_upgradableSpecification.IsUpgradable(subject.Artist.QualityProfile, - subject.Artist.LanguageProfile, - remoteAlbum.ParsedAlbumInfo.Quality, - remoteAlbum.ParsedAlbumInfo.Language, + if (!_upgradableSpecification.IsUpgradable(qualityProfile, + languageProfile, + new List { remoteAlbum.ParsedAlbumInfo.Quality }, + new List { remoteAlbum.ParsedAlbumInfo.Language }, queuedItemPreferredWordScore, subject.ParsedAlbumInfo.Quality, subject.ParsedAlbumInfo.Language, subject.PreferredWordScore)) { - return Decision.Reject("Quality for release in queue is of equal or higher preference: {0} - {1}", remoteAlbum.ParsedAlbumInfo.Quality, remoteAlbum.ParsedAlbumInfo.Language); + return Decision.Reject("Release in queue is of equal or higher preference: {0} - {1}", remoteAlbum.ParsedAlbumInfo.Quality, remoteAlbum.ParsedAlbumInfo.Language); + } + + _logger.Debug("Checking if profiles allow upgrading. Queued: {0} - {1}", remoteAlbum.ParsedAlbumInfo.Quality, remoteAlbum.ParsedAlbumInfo.Language); + + if (!_upgradableSpecification.IsUpgradeAllowed(qualityProfile, + languageProfile, + new List { remoteAlbum.ParsedAlbumInfo.Quality }, + new List { remoteAlbum.ParsedAlbumInfo.Language }, + subject.ParsedAlbumInfo.Quality, + subject.ParsedAlbumInfo.Language)) + { + return Decision.Reject("Another release is queued and the Quality or Language profile does not allow upgrades"); } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs index 2997b8658..05eb5c962 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs @@ -69,11 +69,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync if (trackFiles.Any()) { - var lowestQuality = trackFiles.Select(c => c.Quality).OrderBy(c => c.Quality.Id).First(); + var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList(); + var currentLanguages = trackFiles.Select(c => c.Language).Distinct().ToList(); + var upgradable = _upgradableSpecification.IsUpgradable(qualityProfile, languageProfile, - lowestQuality, - trackFiles[0].Language, + currentQualities, + currentLanguages, _preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName()), subject.ParsedAlbumInfo.Quality, subject.ParsedAlbumInfo.Language, diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs index cded91514..3a0638d15 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/HistorySpecification.cs @@ -1,11 +1,14 @@ using System; +using System.Collections.Generic; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; using NzbDrone.Core.History; using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Languages; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Profiles.Releases; +using NzbDrone.Core.Qualities; namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { @@ -59,8 +62,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync var cutoffUnmet = _upgradableSpecification.CutoffNotMet( subject.Artist.QualityProfile, subject.Artist.LanguageProfile, - mostRecent.Quality, - mostRecent.Language, + new List { mostRecent.Quality }, + new List { mostRecent.Language }, preferredWordScore, subject.ParsedAlbumInfo.Quality, subject.PreferredWordScore); @@ -68,8 +71,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync var upgradeable = _upgradableSpecification.IsUpgradable( subject.Artist.QualityProfile, subject.Artist.LanguageProfile, - mostRecent.Quality, - mostRecent.Language, + new List { mostRecent.Quality }, + new List { mostRecent.Language }, preferredWordScore, subject.ParsedAlbumInfo.Quality, subject.ParsedAlbumInfo.Language, diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs index 66ca3d710..6867486e2 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs @@ -3,16 +3,18 @@ using NzbDrone.Core.Languages; using NzbDrone.Core.Profiles.Languages; using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Qualities; +using System.Collections.Generic; namespace NzbDrone.Core.DecisionEngine.Specifications { public interface IUpgradableSpecification { - bool IsUpgradable(QualityProfile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, int currentScore, QualityModel newQuality, Language newLanguage, int newScore); + bool IsUpgradable(QualityProfile profile, LanguageProfile languageProfile, List currentQualities, List currentLanguages, int currentScore, QualityModel newQuality, Language newLanguage, int newScore); bool QualityCutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null); bool LanguageCutoffNotMet(LanguageProfile languageProfile, Language currentLanguage); - bool CutoffNotMet(QualityProfile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, int currentScore, QualityModel newQuality = null, int newScore = 0); + bool CutoffNotMet(QualityProfile profile, LanguageProfile languageProfile, List currentQualities, List currentLanguages, int currentScore, QualityModel newQuality = null, int newScore = 0); bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality); + bool IsUpgradeAllowed(QualityProfile qualityProfile, LanguageProfile languageProfile, List currentQualities, List currentLanguages, QualityModel newQuality, Language newLanguage); } public class UpgradableSpecification : IUpgradableSpecification @@ -24,32 +26,60 @@ namespace NzbDrone.Core.DecisionEngine.Specifications _logger = logger; } - private bool IsLanguageUpgradable(LanguageProfile profile, Language currentLanguage, Language newLanguage = null) + private ProfileComparisonResult IsLanguageUpgradable(LanguageProfile profile, List currentLanguages, Language newLanguage = null) { if (newLanguage != null) { - var compare = new LanguageComparer(profile).Compare(newLanguage, currentLanguage); - if (compare <= 0) + var totalCompare = 0; + + foreach (var language in currentLanguages) { - return false; + var compare = new LanguageComparer(profile).Compare(newLanguage, language); + + totalCompare += compare; + + // Not upgradable if new language is a downgrade for any current lanaguge + if (compare < 0) + { + return ProfileComparisonResult.Downgrade; + } + } + + // Not upgradable if new language is equal to all current languages + if (totalCompare == 0) + { + return ProfileComparisonResult.Equal; } } - return true; + return ProfileComparisonResult.Upgrade; } - private bool IsQualityUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) + private ProfileComparisonResult IsQualityUpgradable(QualityProfile profile, List currentQualities, QualityModel newQuality = null) { if (newQuality != null) { - var compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality); + var totalCompare = 0; - if (compare <= 0) + foreach (var quality in currentQualities) { - _logger.Debug("Existing item has better quality, skipping"); - return false; + var compare = new QualityModelComparer(profile).Compare(newQuality, quality); + + totalCompare += compare; + + if (compare < 0) + { + // Not upgradable if new quality is a downgrade for any current quality + return ProfileComparisonResult.Downgrade; + } + } + + // Not upgradable if new quality is equal to all current qualities + if (totalCompare == 0) { + return ProfileComparisonResult.Equal; } } - return true; + + return ProfileComparisonResult.Upgrade; } private bool IsPreferredWordUpgradable(int currentScore, int newScore) @@ -57,25 +87,30 @@ namespace NzbDrone.Core.DecisionEngine.Specifications return newScore > currentScore; } - public bool IsUpgradable(QualityProfile qualityProfile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, int currentScore, QualityModel newQuality, Language newLanguage, int newScore) + public bool IsUpgradable(QualityProfile qualityProfile, LanguageProfile languageProfile, List currentQualities, List currentLanguages, int currentScore, QualityModel newQuality, Language newLanguage, int newScore) { - if (IsQualityUpgradable(qualityProfile, currentQuality, newQuality) && qualityProfile.UpgradeAllowed) + + var qualityUpgrade = IsQualityUpgradable(qualityProfile, currentQualities, newQuality); + + if (qualityUpgrade == ProfileComparisonResult.Upgrade) { return true; } - if (new QualityModelComparer(qualityProfile).Compare(newQuality, currentQuality) < 0) + if (qualityUpgrade == ProfileComparisonResult.Downgrade) { _logger.Debug("Existing item has better quality, skipping"); return false; } - if (IsLanguageUpgradable(languageProfile, currentLanguage, newLanguage) && languageProfile.UpgradeAllowed) + var languageUpgrade = IsLanguageUpgradable(languageProfile, currentLanguages, newLanguage); + + if (languageUpgrade == ProfileComparisonResult.Upgrade) { return true; } - if (new LanguageComparer(languageProfile).Compare(newLanguage, currentLanguage) < 0) + if (languageUpgrade == ProfileComparisonResult.Downgrade) { _logger.Debug("Existing item has better language, skipping"); return false; @@ -87,12 +122,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications return false; } - if (!IsPreferredWordUpgradable(currentScore, newScore)) - { - _logger.Debug("Existing item has a better preferred word score, skipping"); - return false; - } - return true; } @@ -120,18 +149,24 @@ namespace NzbDrone.Core.DecisionEngine.Specifications return languageCompare < 0; } - public bool CutoffNotMet(QualityProfile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, int currentScore, QualityModel newQuality = null, int newScore = 0) + public bool CutoffNotMet(QualityProfile profile, LanguageProfile languageProfile, List currentQualities, List currentLanguages, int currentScore, QualityModel newQuality = null, int newScore = 0) { // If we can upgrade the language (it is not the cutoff) then the quality doesn't // matter as we can always get same quality with prefered language. - if (LanguageCutoffNotMet(languageProfile, currentLanguage)) + foreach (var language in currentLanguages) { - return true; + if (LanguageCutoffNotMet(languageProfile, language)) + { + return true; + } } - if (QualityCutoffNotMet(profile, currentQuality, newQuality)) + foreach (var quality in currentQualities) { - return true; + if (QualityCutoffNotMet(profile, quality, newQuality)) + { + return true; + } } if (IsPreferredWordUpgradable(currentScore, newScore)) @@ -157,5 +192,44 @@ namespace NzbDrone.Core.DecisionEngine.Specifications return false; } + + public bool IsUpgradeAllowed(QualityProfile qualityProfile, LanguageProfile languageProfile, List currentQualities, List currentLanguages, QualityModel newQuality, Language newLanguage) + { + var isQualityUpgrade = IsQualityUpgradable(qualityProfile, currentQualities, newQuality); + var isLanguageUpgrade = IsLanguageUpgradable(languageProfile, currentLanguages, newLanguage); + + return CheckUpgradeAllowed(qualityProfile, languageProfile, isQualityUpgrade, isLanguageUpgrade); + } + + private bool CheckUpgradeAllowed (QualityProfile qualityProfile, LanguageProfile languageProfile, ProfileComparisonResult isQualityUpgrade, ProfileComparisonResult isLanguageUpgrade) + { + if (isQualityUpgrade == ProfileComparisonResult.Upgrade && qualityProfile.UpgradeAllowed || + isLanguageUpgrade == ProfileComparisonResult.Upgrade && languageProfile.UpgradeAllowed) + { + _logger.Debug("At least one profile allows upgrading"); + return true; + } + + if (isQualityUpgrade == ProfileComparisonResult.Upgrade && !qualityProfile.UpgradeAllowed) + { + _logger.Debug("Quality profile does not allow upgrades, skipping"); + return false; + } + + if (isLanguageUpgrade == ProfileComparisonResult.Upgrade && !languageProfile.UpgradeAllowed) + { + _logger.Debug("Language profile does not allow upgrades, skipping"); + return false; + } + + return true; + } + + private enum ProfileComparisonResult + { + Downgrade = -1, + Equal = 0, + Upgrade = 1 + } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs new file mode 100644 index 000000000..30296ed6d --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeAllowedSpecification.cs @@ -0,0 +1,76 @@ +using System; +using System.Linq; +using NLog; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Music; +using NzbDrone.Common.Cache; +using NzbDrone.Core.Profiles.Releases; +using NzbDrone.Common.Extensions; + +namespace NzbDrone.Core.DecisionEngine.Specifications +{ + public class UpgradeAllowedSpecification : IDecisionEngineSpecification + { + private readonly UpgradableSpecification _upgradableSpecification; + private readonly IMediaFileService _mediaFileService; + private readonly ITrackService _trackService; + private readonly Logger _logger; + private readonly ICached _missingFilesCache; + + public UpgradeAllowedSpecification(UpgradableSpecification upgradableSpecification, + Logger logger, + ICacheManager cacheManager, + IMediaFileService mediaFileService, + ITrackService trackService) + { + _upgradableSpecification = upgradableSpecification; + _mediaFileService = mediaFileService; + _trackService = trackService; + _missingFilesCache = cacheManager.GetCache(GetType()); + _logger = logger; + } + + public SpecificationPriority Priority => SpecificationPriority.Default; + public RejectionType Type => RejectionType.Permanent; + + public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria) + { + var qualityProfile = subject.Artist.QualityProfile.Value; + var languageProfile = subject.Artist.LanguageProfile.Value; + + foreach (var album in subject.Albums) + { + var tracksMissing = _missingFilesCache.Get(album.Id.ToString(), () => _trackService.TracksWithoutFiles(album.Id).Any(), + TimeSpan.FromSeconds(30)); + + var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id); + + if (!tracksMissing && trackFiles.Any()) + { + // Get a distinct list of all current track qualities and languages for a given album + var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList(); + var currentLanguages = trackFiles.Select(c => c.Language).Distinct().ToList(); + + _logger.Debug("Comparing file quality and language with report. Existing files contain {0} : {1}", currentQualities.ConcatToString(), currentLanguages.ConcatToString()); + + if (!_upgradableSpecification.IsUpgradeAllowed(qualityProfile, + languageProfile, + currentQualities, + currentLanguages, + subject.ParsedAlbumInfo.Quality, + subject.ParsedAlbumInfo.Language)) + { + _logger.Debug("Upgrading is not allowed by the quality or language profile"); + + return Decision.Reject("Existing files and the Quality or Language profile does not allow upgrades"); + } + + } + } + + return Decision.Accept(); + } + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs index 51fb19c88..0f0835db9 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs @@ -7,6 +7,7 @@ using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Music; using NzbDrone.Common.Cache; using NzbDrone.Core.Profiles.Releases; +using NzbDrone.Common.Extensions; namespace NzbDrone.Core.DecisionEngine.Specifications { @@ -48,18 +49,19 @@ namespace NzbDrone.Core.DecisionEngine.Specifications if (!tracksMissing && trackFiles.Any()) { - var lowestQuality = trackFiles.Select(c => c.Quality).OrderBy(c => c.Quality.Id).First(); + var currentQualities = trackFiles.Select(c => c.Quality).Distinct().ToList(); + var currentLanguages = trackFiles.Select(c => c.Language).Distinct().ToList(); if (!_upgradableSpecification.IsUpgradable(subject.Artist.QualityProfile, subject.Artist.LanguageProfile, - lowestQuality, - trackFiles[0].Language, + currentQualities, + currentLanguages, _preferredWordServiceCalculator.Calculate(subject.Artist, trackFiles[0].GetSceneOrFileName()), subject.ParsedAlbumInfo.Quality, subject.ParsedAlbumInfo.Language, subject.PreferredWordScore)) { - return Decision.Reject("Existing file on disk is of equal or higher preference: {0} - {1}", lowestQuality, trackFiles[0].Language); + return Decision.Reject("Existing files on disk is of equal or higher preference: {0} - {1}", currentQualities.ConcatToString(), currentLanguages.ConcatToString()); } } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 3881bcb2d..b80d60df1 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -258,6 +258,7 @@ +