diff --git a/frontend/src/Settings/MediaManagement/MediaManagement.js b/frontend/src/Settings/MediaManagement/MediaManagement.js index de456f802..4d6247b4b 100644 --- a/frontend/src/Settings/MediaManagement/MediaManagement.js +++ b/frontend/src/Settings/MediaManagement/MediaManagement.js @@ -21,6 +21,12 @@ const rescanAfterRefreshOptions = [ { key: 'never', value: 'Never' } ]; +const downloadPropersAndRepacksOptions = [ + { key: 'preferAndUpgrade', value: 'Prefer and Upgrade' }, + { key: 'doNotUpgrade', value: 'Do not Upgrade Automatically' }, + { key: 'doNotPrefer', value: 'Do not Prefer' } +]; + const fileDateOptions = [ { key: 'none', value: 'None' }, { key: 'cinemas', value: 'In Cinemas Date' }, @@ -230,14 +236,23 @@ class MediaManagement extends Component { isAdvanced={true} size={sizes.MEDIUM} > - {translate('DownloadPropers')} + {translate('DownloadPropersAndRepacks')} diff --git a/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs b/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs index 691b7b85b..512891fcb 100644 --- a/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs +++ b/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs @@ -1,5 +1,6 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Qualities; using Radarr.Http.REST; namespace NzbDrone.Api.Config @@ -8,7 +9,7 @@ public class MediaManagementConfigResource : RestResource { public bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; } public string RecycleBin { get; set; } - public bool AutoDownloadPropers { get; set; } + public ProperDownloadTypes DownloadPropersAndRepacks { get; set; } public bool CreateEmptySeriesFolders { get; set; } public FileDateType FileDate { get; set; } public bool AutoRenameFolders { get; set; } @@ -32,7 +33,7 @@ public static MediaManagementConfigResource ToResource(IConfigService model) { AutoUnmonitorPreviouslyDownloadedEpisodes = model.AutoUnmonitorPreviouslyDownloadedMovies, RecycleBin = model.RecycleBin, - AutoDownloadPropers = model.AutoDownloadPropers, + DownloadPropersAndRepacks = model.DownloadPropersAndRepacks, CreateEmptySeriesFolders = model.CreateEmptyMovieFolders, FileDate = model.FileDate, AutoRenameFolders = model.AutoRenameFolders, diff --git a/src/NzbDrone.Api/Profiles/ProfileResource.cs b/src/NzbDrone.Api/Profiles/ProfileResource.cs index 4dde38175..a9f05cd2e 100644 --- a/src/NzbDrone.Api/Profiles/ProfileResource.cs +++ b/src/NzbDrone.Api/Profiles/ProfileResource.cs @@ -13,7 +13,6 @@ public class ProfileResource : RestResource { public string Name { get; set; } public Quality Cutoff { get; set; } - public string PreferredTags { get; set; } public List Items { get; set; } public int MinFormatScore { get; set; } public int CutoffFormatScore { get; set; } @@ -66,7 +65,6 @@ public static ProfileResource ToResource(this Profile model) Id = model.Id, Name = model.Name, - PreferredTags = model.PreferredTags != null ? string.Join(",", model.PreferredTags) : "", Cutoff = cutoff, // Flatten groups so things don't explode @@ -127,7 +125,6 @@ public static Profile ToModel(this ProfileResource resource) Name = resource.Name, Cutoff = resource.Cutoff.Id, - PreferredTags = resource.PreferredTags.Split(',').ToList(), Items = resource.Items.ConvertAll(ToModel), MinFormatScore = resource.MinFormatScore, CutoffFormatScore = resource.CutoffFormatScore, diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs index 4ee35f7ab..4de1b8066 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs @@ -6,6 +6,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Indexers; @@ -61,7 +62,6 @@ private RemoteMovie GivenRemoteMovie(QualityModel quality, int age = 0, long siz remoteMovie.Movie = Builder.CreateNew().With(m => m.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities(), - PreferredTags = new List { "DTS-HD", "SPARKS" }, FormatItems = CustomFormatsFixture.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name), MinFormatScore = 0 }) @@ -90,6 +90,20 @@ private void GivenPreferredDownloadProtocol(DownloadProtocol downloadProtocol) }); } + [Test] + public void should_put_reals_before_non_reals() + { + var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p, new Revision(version: 1, real: 0))); + var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p, new Revision(version: 1, real: 1))); + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteMovie1)); + decisions.Add(new DownloadDecision(remoteMovie2)); + + var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions); + qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Revision.Real.Should().Be(1); + } + [Test] public void should_put_propers_before_non_propers() { @@ -392,23 +406,6 @@ public void should_prefer_first_release_if_age_and_size_are_too_similar() qualifiedReports.First().RemoteMovie.Release.Should().Be(remoteMovie1.Release); } - [Test] - public void should_prefer_more_prioritized_words() - { - var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p)); - var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p)); - - remoteMovie1.Release.Title += " DTS-HD"; - remoteMovie2.Release.Title += " DTS-HD SPARKS"; - - var decisions = new List(); - decisions.Add(new DownloadDecision(remoteMovie1)); - decisions.Add(new DownloadDecision(remoteMovie2)); - - var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions); - qualifiedReports.First().RemoteMovie.Release.Should().Be(remoteMovie2.Release); - } - [Test] public void should_prefer_better_custom_format() { @@ -469,5 +466,94 @@ public void should_prefer_2_custom_formats() var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions); qualifiedReports.First().RemoteMovie.Release.Should().Be(remoteMovie2.Release); } + + [Test] + public void should_prefer_proper_over_score_when_download_propers_is_prefer_and_upgrade() + { + Mocker.GetMock() + .Setup(s => s.DownloadPropersAndRepacks) + .Returns(ProperDownloadTypes.PreferAndUpgrade); + + var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.WEBDL1080p, new Revision(1))); + var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.WEBDL1080p, new Revision(2))); + + remoteMovie1.CustomFormatScore = 10; + remoteMovie2.CustomFormatScore = 0; + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteMovie1)); + decisions.Add(new DownloadDecision(remoteMovie2)); + + var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions); + qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Revision.Version.Should().Be(2); + } + + [Test] + public void should_prefer_proper_over_score_when_download_propers_is_do_not_upgrade() + { + Mocker.GetMock() + .Setup(s => s.DownloadPropersAndRepacks) + .Returns(ProperDownloadTypes.DoNotUpgrade); + + var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.WEBDL1080p, new Revision(1))); + var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.WEBDL1080p, new Revision(2))); + + remoteMovie1.CustomFormatScore = 10; + remoteMovie2.CustomFormatScore = 0; + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteMovie1)); + decisions.Add(new DownloadDecision(remoteMovie2)); + + var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions); + qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Revision.Version.Should().Be(2); + } + + [Test] + public void should_prefer_score_over_proper_when_download_propers_is_do_not_prefer() + { + Mocker.GetMock() + .Setup(s => s.DownloadPropersAndRepacks) + .Returns(ProperDownloadTypes.DoNotPrefer); + + var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.WEBDL1080p, new Revision(1))); + var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.WEBDL1080p, new Revision(2))); + + remoteMovie1.CustomFormatScore = 10; + remoteMovie2.CustomFormatScore = 0; + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteMovie1)); + decisions.Add(new DownloadDecision(remoteMovie2)); + + var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions); + qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Quality.Should().Be(Quality.WEBDL1080p); + qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Revision.Version.Should().Be(1); + qualifiedReports.First().RemoteMovie.CustomFormatScore.Should().Be(10); + } + + [Test] + public void should_prefer_score_over_real_when_download_propers_is_do_not_prefer() + { + Mocker.GetMock() + .Setup(s => s.DownloadPropersAndRepacks) + .Returns(ProperDownloadTypes.DoNotPrefer); + + var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.WEBDL1080p, new Revision(1, 0))); + var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.WEBDL1080p, new Revision(1, 1))); + + remoteMovie1.CustomFormatScore = 10; + remoteMovie2.CustomFormatScore = 0; + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteMovie1)); + decisions.Add(new DownloadDecision(remoteMovie2)); + + var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions); + qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Quality.Should().Be(Quality.WEBDL1080p); + qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Revision.Version.Should().Be(1); + qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Revision.Real.Should().Be(0); + qualifiedReports.First().RemoteMovie.CustomFormatScore.Should().Be(10); + } } } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs index ea91d5f43..75b8894b7 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Configuration; @@ -51,11 +51,11 @@ public void Setup() CustomFormatsFixture.GivenCustomFormats(_customFormat1, _customFormat2); } - private void GivenAutoDownloadPropers(bool autoDownloadPropers) + private void GivenAutoDownloadPropers(ProperDownloadTypes type) { Mocker.GetMock() - .SetupGet(s => s.AutoDownloadPropers) - .Returns(autoDownloadPropers); + .SetupGet(s => s.DownloadPropersAndRepacks) + .Returns(type); } [Test] @@ -68,7 +68,7 @@ public void IsUpgradeTest(Quality current, List newFormats, bool expected) { - GivenAutoDownloadPropers(true); + GivenAutoDownloadPropers(ProperDownloadTypes.PreferAndUpgrade); var profile = new Profile { @@ -86,9 +86,9 @@ public void IsUpgradeTest(Quality current, } [Test] - public void should_return_false_if_proper_and_autoDownloadPropers_is_false() + public void should_return_true_if_proper_and_download_propers_is_do_not_download() { - GivenAutoDownloadPropers(false); + GivenAutoDownloadPropers(ProperDownloadTypes.DoNotUpgrade); var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }; diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs index fa401ee29..0f7ee0c1d 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs @@ -25,7 +25,7 @@ public class DelaySpecificationFixture : CoreTest { private Profile _profile; private DelayProfile _delayProfile; - private RemoteMovie _remoteEpisode; + private RemoteMovie _remoteMovie; [SetUp] public void Setup() @@ -41,7 +41,7 @@ public void Setup() .With(s => s.Profile = _profile) .Build(); - _remoteEpisode = Builder.CreateNew() + _remoteMovie = Builder.CreateNew() .With(r => r.Movie = series) .Build(); @@ -52,12 +52,10 @@ public void Setup() _profile.Cutoff = Quality.WEBDL720p.Id; - _remoteEpisode.ParsedMovieInfo = new ParsedMovieInfo(); - _remoteEpisode.Release = new ReleaseInfo(); - _remoteEpisode.Release.DownloadProtocol = DownloadProtocol.Usenet; + _remoteMovie.ParsedMovieInfo = new ParsedMovieInfo(); + _remoteMovie.Release = new ReleaseInfo(); + _remoteMovie.Release.DownloadProtocol = DownloadProtocol.Usenet; - //_remoteEpisode.Episodes = Builder.CreateListOfSize(1).Build().ToList(); - //_remoteEpisode.Episodes.First().EpisodeFileId = 0; Mocker.GetMock() .Setup(s => s.BestForTags(It.IsAny>())) .Returns(_delayProfile); @@ -70,7 +68,7 @@ public void Setup() private void GivenExistingFile(QualityModel quality) { //_remoteEpisode.Episodes.First().EpisodeFileId = 1; - _remoteEpisode.Movie.MovieFile = new MovieFile { Quality = quality }; + _remoteMovie.Movie.MovieFile = new MovieFile { Quality = quality }; } private void GivenUpgradeForExistingFile() @@ -89,12 +87,12 @@ public void should_be_true_when_user_invoked_search() [Test] public void should_be_false_when_system_invoked_search_and_release_is_younger_than_delay() { - _remoteEpisode.ParsedMovieInfo.Quality = new QualityModel(Quality.SDTV); - _remoteEpisode.Release.PublishDate = DateTime.UtcNow; + _remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.SDTV); + _remoteMovie.Release.PublishDate = DateTime.UtcNow; _delayProfile.UsenetDelay = 720; - Subject.IsSatisfiedBy(_remoteEpisode, new MovieSearchCriteria()).Accepted.Should().BeFalse(); + Subject.IsSatisfiedBy(_remoteMovie, new MovieSearchCriteria()).Accepted.Should().BeFalse(); } [Test] @@ -102,44 +100,44 @@ public void should_be_true_when_profile_does_not_have_a_delay() { _delayProfile.UsenetDelay = 0; - Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); } [Test] public void should_be_true_when_quality_is_last_allowed_in_profile() { - _remoteEpisode.ParsedMovieInfo.Quality = new QualityModel(Quality.Bluray720p); + _remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.Bluray720p); - Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); } [Test] public void should_be_true_when_release_is_older_than_delay() { - _remoteEpisode.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p); - _remoteEpisode.Release.PublishDate = DateTime.UtcNow.AddHours(-10); + _remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p); + _remoteMovie.Release.PublishDate = DateTime.UtcNow.AddHours(-10); _delayProfile.UsenetDelay = 60; - Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); } [Test] public void should_be_false_when_release_is_younger_than_delay() { - _remoteEpisode.ParsedMovieInfo.Quality = new QualityModel(Quality.SDTV); - _remoteEpisode.Release.PublishDate = DateTime.UtcNow; + _remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.SDTV); + _remoteMovie.Release.PublishDate = DateTime.UtcNow; _delayProfile.UsenetDelay = 720; - Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); + Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse(); } [Test] - public void should_be_true_when_release_is_a_proper_for_existing_episode() + public void should_be_true_when_release_is_a_proper_for_existing_movie() { - _remoteEpisode.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2)); - _remoteEpisode.Release.PublishDate = DateTime.UtcNow; + _remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2)); + _remoteMovie.Release.PublishDate = DateTime.UtcNow; GivenExistingFile(new QualityModel(Quality.HDTV720p)); GivenUpgradeForExistingFile(); @@ -150,14 +148,14 @@ public void should_be_true_when_release_is_a_proper_for_existing_episode() _delayProfile.UsenetDelay = 720; - Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); } [Test] - public void should_be_true_when_release_is_a_real_for_existing_episode() + public void should_be_true_when_release_is_a_real_for_existing_movie() { - _remoteEpisode.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(real: 1)); - _remoteEpisode.Release.PublishDate = DateTime.UtcNow; + _remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(real: 1)); + _remoteMovie.Release.PublishDate = DateTime.UtcNow; GivenExistingFile(new QualityModel(Quality.HDTV720p)); GivenUpgradeForExistingFile(); @@ -168,20 +166,20 @@ public void should_be_true_when_release_is_a_real_for_existing_episode() _delayProfile.UsenetDelay = 720; - Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); + Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeTrue(); } [Test] - public void should_be_false_when_release_is_proper_for_existing_episode_of_different_quality() + public void should_be_false_when_release_is_proper_for_existing_movie_of_different_quality() { - _remoteEpisode.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2)); - _remoteEpisode.Release.PublishDate = DateTime.UtcNow; + _remoteMovie.ParsedMovieInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2)); + _remoteMovie.Release.PublishDate = DateTime.UtcNow; GivenExistingFile(new QualityModel(Quality.SDTV)); _delayProfile.UsenetDelay = 720; - Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); + Subject.IsSatisfiedBy(_remoteMovie, null).Accepted.Should().BeFalse(); } } } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs index 21e9a5aff..7160fabef 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs @@ -48,13 +48,6 @@ private void WithFirstFileUpgradable() _firstFile.Quality = new QualityModel(Quality.SDTV); } - private void GivenAutoDownloadPropers() - { - Mocker.GetMock() - .Setup(s => s.AutoDownloadPropers) - .Returns(true); - } - [Test] public void should_return_false_when_movieFile_was_added_more_than_7_days_ago() { @@ -85,6 +78,10 @@ public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_b [Test] public void should_return_false_when_proper_but_auto_download_propers_is_false() { + Mocker.GetMock() + .Setup(s => s.DownloadPropersAndRepacks) + .Returns(ProperDownloadTypes.DoNotUpgrade); + _firstFile.Quality.Quality = Quality.DVD; _firstFile.DateAdded = DateTime.Today; @@ -94,7 +91,21 @@ public void should_return_false_when_proper_but_auto_download_propers_is_false() [Test] public void should_return_true_when_movieFile_was_added_today() { - GivenAutoDownloadPropers(); + Mocker.GetMock() + .Setup(s => s.DownloadPropersAndRepacks) + .Returns(ProperDownloadTypes.PreferAndUpgrade); + + _firstFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today; + Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue(); + } + + public void should_return_true_when_propers_are_not_preferred() + { + Mocker.GetMock() + .Setup(s => s.DownloadPropersAndRepacks) + .Returns(ProperDownloadTypes.DoNotPrefer); _firstFile.Quality.Quality = Quality.DVD; diff --git a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs index e0c5794b1..7f7728927 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs @@ -46,7 +46,7 @@ private RemoteMovie GetRemoteMovie(QualityModel quality, Movie movie = null, Dow movie = GetMovie(1); } - movie.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities(), PreferredTags = new List() }; + movie.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }; var remoteMovie = new RemoteMovie() { diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index 364925be2..e7a142da7 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -11,6 +11,7 @@ using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.MetadataSource.SkyHook.Resource; +using NzbDrone.Core.Qualities; using NzbDrone.Core.Security; namespace NzbDrone.Core.Configuration @@ -156,11 +157,11 @@ public int MinimumAge set { SetValue("MinimumAge", value); } } - public bool AutoDownloadPropers + public ProperDownloadTypes DownloadPropersAndRepacks { - get { return GetValueBoolean("AutoDownloadPropers", true); } + get { return GetValueEnum("DownloadPropersAndRepacks", ProperDownloadTypes.PreferAndUpgrade); } - set { SetValue("AutoDownloadPropers", value); } + set { SetValue("DownloadPropersAndRepacks", value); } } public bool EnableCompletedDownloadHandling diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index c95fde86d..c4c24136c 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -3,6 +3,7 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MetadataSource.SkyHook.Resource; +using NzbDrone.Core.Qualities; using NzbDrone.Core.Security; namespace NzbDrone.Core.Configuration @@ -29,7 +30,7 @@ public interface IConfigService bool AutoUnmonitorPreviouslyDownloadedMovies { get; set; } string RecycleBin { get; set; } int RecycleBinCleanupDays { get; set; } - bool AutoDownloadPropers { get; set; } + ProperDownloadTypes DownloadPropersAndRepacks { get; set; } bool CreateEmptyMovieFolders { get; set; } bool DeleteEmptyFolders { get; set; } FileDateType FileDate { get; set; } diff --git a/src/NzbDrone.Core/Datastore/Migration/183_download_propers_config.cs b/src/NzbDrone.Core/Datastore/Migration/183_download_propers_config.cs new file mode 100644 index 000000000..72312ee1d --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/183_download_propers_config.cs @@ -0,0 +1,44 @@ +using System.Data; +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(183)] + public class download_propers_config : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Execute.WithConnection(SetMetadataFileExtension); + Execute.Sql("DELETE FROM Config WHERE Key = 'autodownloadpropers'"); + Delete.Column("PreferredTags").FromTable("Profiles"); + } + + private void SetMetadataFileExtension(IDbConnection conn, IDbTransaction tran) + { + using (var cmd = conn.CreateCommand()) + { + cmd.Transaction = tran; + cmd.CommandText = "SELECT Value FROM Config WHERE Key = 'autodownloadpropers'"; + + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var value = reader.GetString(0); + var newValue = bool.Parse(value) ? "PreferAndUpgrade" : "DoNotUpgrade"; + + using (var updateCmd = conn.CreateCommand()) + { + updateCmd.Transaction = tran; + updateCmd.CommandText = "INSERT INTO Config (key, value) VALUES ('downloadpropersandrepacks', ?)"; + updateCmd.AddParameter(newValue); + + updateCmd.ExecuteNonQuery(); + } + } + } + } + } + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs index 889b9ae19..13414c2fd 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs @@ -30,7 +30,7 @@ public int Compare(DownloadDecision x, DownloadDecision y) var comparers = new List { CompareQuality, - ComparePreferredWords, + CompareCustomFormatScore, CompareProtocol, CompareIndexerFlags, ComparePeersIfTorrent, @@ -57,28 +57,18 @@ private int CompareAll(params int[] comparers) private int CompareQuality(DownloadDecision x, DownloadDecision y) { + if (_configService.DownloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer) + { + return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)); + } + return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.Movie.Profile.GetIndex(remoteMovie.ParsedMovieInfo.Quality.Quality)), - CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.CustomFormatScore), - CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Real), - CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision.Version)); + CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.ParsedMovieInfo.Quality.Revision)); } - private int ComparePreferredWords(DownloadDecision x, DownloadDecision y) + private int CompareCustomFormatScore(DownloadDecision x, DownloadDecision y) { - return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => - { - var title = remoteMovie.Release.Title; - var preferredWords = remoteMovie.Movie.Profile.PreferredTags; - - if (preferredWords == null) - { - return 0; - } - - var num = preferredWords.AsEnumerable().Count(w => title.ToLower().Contains(w.ToLower())); - - return num; - }); + return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie => remoteMovie.CustomFormatScore); } private int CompareIndexerFlags(DownloadDecision x, DownloadDecision y) diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs index e748b2d64..e6a6e9872 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs @@ -46,21 +46,6 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se var delay = delayProfile.GetProtocolDelay(subject.Release.DownloadProtocol); var isPreferredProtocol = subject.Release.DownloadProtocol == delayProfile.PreferredProtocol; - // Preferred word count - var title = subject.Release.Title; - var preferredWords = subject.Movie.Profile?.PreferredTags; - var preferredCount = 0; - - if (preferredWords == null) - { - preferredCount = 1; - _logger.Debug("Preferred words is null, setting preffered count to 1."); - } - else - { - preferredCount = preferredWords.AsEnumerable().Count(w => title.ToLower().Contains(w.ToLower())); - } - if (delay == 0) { _logger.Debug("Profile does not require a waiting period before download for {0}.", subject.Release.DownloadProtocol); @@ -71,7 +56,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se var file = subject.Movie.MovieFile; - if (isPreferredProtocol && (subject.Movie.MovieFileId != 0 && file != null) && (preferredCount > 0 || preferredWords == null)) + if (isPreferredProtocol && (subject.Movie.MovieFileId != 0 && file != null)) { var customFormats = _formatService.ParseCustomFormat(file); var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, @@ -86,7 +71,7 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (revisionUpgrade) { - _logger.Debug("New quality is a better revision for existing quality and preferred word count is {0}, skipping delay", preferredCount); + _logger.Debug("New quality is a better revision for existing quality, skipping delay"); return Decision.Accept(); } } @@ -96,9 +81,9 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se var bestQualityInProfile = profile.LastAllowedQuality(); var isBestInProfile = comparer.Compare(subject.ParsedMovieInfo.Quality.Quality, bestQualityInProfile) >= 0; - if (isBestInProfile && isPreferredProtocol && (preferredCount > 0 || preferredWords == null)) + if (isBestInProfile && isPreferredProtocol) { - _logger.Debug("Quality is highest in profile for preferred protocol and preferred word count is {0}, will not delay.", preferredCount); + _logger.Debug("Quality is highest in profile for preferred protocol, will not delay."); return Decision.Accept(); } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs index 7f22ed761..74d823849 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs @@ -3,6 +3,7 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync { @@ -29,6 +30,14 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se return Decision.Accept(); } + var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks; + + if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer) + { + _logger.Debug("Propers are not preferred, skipping check"); + return Decision.Accept(); + } + if (subject.Movie.MovieFile == null) { return Decision.Accept(); @@ -38,17 +47,17 @@ public virtual Decision IsSatisfiedBy(RemoteMovie subject, SearchCriteriaBase se if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedMovieInfo.Quality)) { + if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotUpgrade) + { + _logger.Debug("Auto downloading of propers is disabled"); + return Decision.Reject("Proper downloading is disabled"); + } + if (file.DateAdded < DateTime.Today.AddDays(-7)) { _logger.Debug("Proper for old file, rejecting: {0}", subject); return Decision.Reject("Proper for old file"); } - - if (!_configService.AutoDownloadPropers) - { - _logger.Debug("Auto downloading of propers is disabled"); - return Decision.Reject("Proper downloading is disabled"); - } } return Decision.Accept(); diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs index 30d4c5d92..64ccae70a 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/UpgradableSpecification.cs @@ -47,10 +47,9 @@ public bool IsUpgradable(Profile profile, QualityModel currentQuality, List 0) { - _logger.Debug("New item has a better quality revision"); return true; } diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 9474f4da8..c285eb27b 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -46,7 +46,6 @@ "AudioInfo": "Audio Info", "Authentication": "Authentication", "AuthenticationMethodHelpText": "Require Username and Password to access Radarr", - "AutoDownloadPropersHelpText": "Should Radarr automatically upgrade to propers when available?", "Automatic": "Automatic", "AutoRedownloadFailedHelpText": "Automatically search for and attempt to download a different release", "AutoUnmonitorPreviouslyDownloadedMoviesHelpText": "Movies deleted from disk are automatically unmonitored in Radarr", @@ -185,7 +184,10 @@ "DownloadFailedCheckDownloadClientForMoreDetails": "Download failed: check download client for more details", "DownloadFailedInterp": "Download failed: {0}", "Downloading": "Downloading", - "DownloadPropers": "Download Propers", + "DownloadPropersAndRepacks": "Propers and Repacks", + "DownloadPropersAndRepacksHelpText1": "Whether or not to automatically upgrade to Propers/Repacks", + "DownloadPropersAndRepacksHelpText2": "Use 'Do not Prefer' to sort by preferred word score over propers/repacks", + "DownloadPropersAndRepacksHelpTextWarning": "Use preferred words for automatic upgrades to propers/repacks", "DownloadWarning": "Download warning: {0}", "DownloadWarningCheckDownloadClientForMoreDetails": "Download warning: check download client for more details", "Edit": "Edit", diff --git a/src/NzbDrone.Core/Profiles/Profile.cs b/src/NzbDrone.Core/Profiles/Profile.cs index 0952af29f..9d9e07f8b 100644 --- a/src/NzbDrone.Core/Profiles/Profile.cs +++ b/src/NzbDrone.Core/Profiles/Profile.cs @@ -20,7 +20,6 @@ public Profile() public int MinFormatScore { get; set; } public int CutoffFormatScore { get; set; } public List FormatItems { get; set; } - public List PreferredTags { get; set; } public Language Language { get; set; } public bool UpgradeAllowed { get; set; } diff --git a/src/NzbDrone.Core/Qualities/ProperDownloadTypes.cs b/src/NzbDrone.Core/Qualities/ProperDownloadTypes.cs new file mode 100644 index 000000000..d61101e5a --- /dev/null +++ b/src/NzbDrone.Core/Qualities/ProperDownloadTypes.cs @@ -0,0 +1,9 @@ +namespace NzbDrone.Core.Qualities +{ + public enum ProperDownloadTypes + { + PreferAndUpgrade, + DoNotUpgrade, + DoNotPrefer + } +} diff --git a/src/Radarr.Api.V3/Config/MediaManagementConfigResource.cs b/src/Radarr.Api.V3/Config/MediaManagementConfigResource.cs index 549d077bc..987eba574 100644 --- a/src/Radarr.Api.V3/Config/MediaManagementConfigResource.cs +++ b/src/Radarr.Api.V3/Config/MediaManagementConfigResource.cs @@ -1,5 +1,6 @@ using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Qualities; using Radarr.Http.REST; namespace Radarr.Api.V3.Config @@ -9,7 +10,7 @@ public class MediaManagementConfigResource : RestResource public bool AutoUnmonitorPreviouslyDownloadedMovies { get; set; } public string RecycleBin { get; set; } public int RecycleBinCleanupDays { get; set; } - public bool AutoDownloadPropers { get; set; } + public ProperDownloadTypes DownloadPropersAndRepacks { get; set; } public bool CreateEmptyMovieFolders { get; set; } public bool DeleteEmptyFolders { get; set; } public FileDateType FileDate { get; set; } @@ -37,7 +38,7 @@ public static MediaManagementConfigResource ToResource(IConfigService model) AutoUnmonitorPreviouslyDownloadedMovies = model.AutoUnmonitorPreviouslyDownloadedMovies, RecycleBin = model.RecycleBin, RecycleBinCleanupDays = model.RecycleBinCleanupDays, - AutoDownloadPropers = model.AutoDownloadPropers, + DownloadPropersAndRepacks = model.DownloadPropersAndRepacks, CreateEmptyMovieFolders = model.CreateEmptyMovieFolders, DeleteEmptyFolders = model.DeleteEmptyFolders, FileDate = model.FileDate, diff --git a/src/Radarr.Api.V3/Profiles/Quality/QualityProfileResource.cs b/src/Radarr.Api.V3/Profiles/Quality/QualityProfileResource.cs index c8a0ff899..fc2018bfc 100644 --- a/src/Radarr.Api.V3/Profiles/Quality/QualityProfileResource.cs +++ b/src/Radarr.Api.V3/Profiles/Quality/QualityProfileResource.cs @@ -12,7 +12,6 @@ public class QualityProfileResource : RestResource public string Name { get; set; } public bool UpgradeAllowed { get; set; } public int Cutoff { get; set; } - public string PreferredTags { get; set; } public List Items { get; set; } public int MinFormatScore { get; set; } public int CutoffFormatScore { get; set; } @@ -55,7 +54,6 @@ public static QualityProfileResource ToResource(this Profile model) Name = model.Name, UpgradeAllowed = model.UpgradeAllowed, Cutoff = model.Cutoff, - PreferredTags = model.PreferredTags != null ? string.Join(",", model.PreferredTags) : "", Items = model.Items.ConvertAll(ToResource), MinFormatScore = model.MinFormatScore, CutoffFormatScore = model.CutoffFormatScore, @@ -104,7 +102,6 @@ public static Profile ToModel(this QualityProfileResource resource) Name = resource.Name, UpgradeAllowed = resource.UpgradeAllowed, Cutoff = resource.Cutoff, - PreferredTags = resource.PreferredTags.Split(',').ToList(), Items = resource.Items.ConvertAll(ToModel), MinFormatScore = resource.MinFormatScore, CutoffFormatScore = resource.CutoffFormatScore,