New: Option to not prefer repacks/propers (for use with Preferred Words)

Closes #3084
pull/3105/head
Mark McDowall 5 years ago
parent a06cbc44cd
commit 7321075631

@ -25,6 +25,12 @@ const rescanAfterRefreshOptions = [
{ key: 'never', value: 'Never' } { 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 = [ const fileDateOptions = [
{ key: 'none', value: 'None' }, { key: 'none', value: 'None' },
{ key: 'localAirDate', value: 'Local Air Date' }, { key: 'localAirDate', value: 'Local Air Date' },
@ -227,14 +233,23 @@ class MediaManagement extends Component {
isAdvanced={true} isAdvanced={true}
size={sizes.MEDIUM} size={sizes.MEDIUM}
> >
<FormLabel>Download Propers</FormLabel> <FormLabel>Propers and Repacks</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.SELECT}
name="autoDownloadPropers" name="downloadPropersAndRepacks"
helpText="Should Sonarr automatically upgrade to propers when available?" helpTexts={[
'Whether or not to automatically upgrade to Propers/Repacks',
'Use \'Do not Prefer\' to sort by preferred word score over propers/repacks'
]}
helpTextWarning={
settings.downloadPropersAndRepacks.value === 'doNotPrefer' ?
'Use preferred words for automatic upgrades to propers/repacks' :
undefined
}
values={downloadPropersAndRepacksOptions}
onChange={onInputChange} onChange={onInputChange}
{...settings.autoDownloadPropers} {...settings.downloadPropersAndRepacks}
/> />
</FormGroup> </FormGroup>

@ -1,6 +1,7 @@
using Sonarr.Http.REST; using Sonarr.Http.REST;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Api.Config namespace NzbDrone.Api.Config
{ {
@ -8,7 +9,7 @@ namespace NzbDrone.Api.Config
{ {
public bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; } public bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; }
public string RecycleBin { get; set; } public string RecycleBin { get; set; }
public bool AutoDownloadPropers { get; set; } public ProperDownloadTypes DownloadPropersAndRepacks { get; set; }
public bool CreateEmptySeriesFolders { get; set; } public bool CreateEmptySeriesFolders { get; set; }
public bool DeleteEmptyFolders { get; set; } public bool DeleteEmptyFolders { get; set; }
public FileDateType FileDate { get; set; } public FileDateType FileDate { get; set; }
@ -34,7 +35,7 @@ namespace NzbDrone.Api.Config
{ {
AutoUnmonitorPreviouslyDownloadedEpisodes = model.AutoUnmonitorPreviouslyDownloadedEpisodes, AutoUnmonitorPreviouslyDownloadedEpisodes = model.AutoUnmonitorPreviouslyDownloadedEpisodes,
RecycleBin = model.RecycleBin, RecycleBin = model.RecycleBin,
AutoDownloadPropers = model.AutoDownloadPropers, DownloadPropersAndRepacks = model.DownloadPropersAndRepacks,
CreateEmptySeriesFolders = model.CreateEmptySeriesFolders, CreateEmptySeriesFolders = model.CreateEmptySeriesFolders,
DeleteEmptyFolders = model.DeleteEmptyFolders, DeleteEmptyFolders = model.DeleteEmptyFolders,
FileDate = model.FileDate, FileDate = model.FileDate,

@ -13,6 +13,7 @@ using NUnit.Framework;
using FluentAssertions; using FluentAssertions;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages; using NzbDrone.Core.Profiles.Languages;
@ -440,7 +441,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
} }
[Test] [Test]
public void should_put_higher_quality_before_lower_allways() public void should_put_higher_quality_before_lower_always()
{ {
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV), Language.French); var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.SDTV), Language.French);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.German); var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p), Language.German);
@ -452,5 +453,87 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var qualifiedReports = Subject.PrioritizeDecisions(decisions); var qualifiedReports = Subject.PrioritizeDecisions(decisions);
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Quality.Should().Be(Quality.HDTV720p); qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Quality.Should().Be(Quality.HDTV720p);
} }
[Test]
public void should_prefer_higher_score_over_lower_score()
{
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.WEBDL1080p), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.WEBDL1080p), Language.English);
remoteEpisode1.PreferredWordScore = 10;
remoteEpisode2.PreferredWordScore = 0;
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1));
decisions.Add(new DownloadDecision(remoteEpisode2));
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
qualifiedReports.First().RemoteEpisode.PreferredWordScore.Should().Be(10);
}
[Test]
public void should_prefer_proper_over_score_when_download_propers_is_prefer_and_upgrade()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.PreferAndUpgrade);
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.WEBDL1080p, new Revision(1)), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.WEBDL1080p, new Revision(2)), Language.English);
remoteEpisode1.PreferredWordScore = 10;
remoteEpisode2.PreferredWordScore = 0;
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1));
decisions.Add(new DownloadDecision(remoteEpisode2));
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version.Should().Be(2);
}
[Test]
public void should_prefer_proper_over_score_when_download_propers_is_do_not_upgrade()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotUpgrade);
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.WEBDL1080p, new Revision(1)), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.WEBDL1080p, new Revision(2)), Language.English);
remoteEpisode1.PreferredWordScore = 10;
remoteEpisode2.PreferredWordScore = 0;
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1));
decisions.Add(new DownloadDecision(remoteEpisode2));
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version.Should().Be(2);
}
[Test]
public void should_prefer_score_over_proper_when_download_propers_is_do_not_prefer()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotPrefer);
var remoteEpisode1 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.WEBDL1080p, new Revision(1)), Language.English);
var remoteEpisode2 = GivenRemoteEpisode(new List<Episode> { GivenEpisode(1) }, new QualityModel(Quality.WEBDL1080p, new Revision(2)), Language.English);
remoteEpisode1.PreferredWordScore = 10;
remoteEpisode2.PreferredWordScore = 0;
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode1));
decisions.Add(new DownloadDecision(remoteEpisode2));
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Quality.Should().Be(Quality.WEBDL1080p);
qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version.Should().Be(1);
qualifiedReports.First().RemoteEpisode.PreferredWordScore.Should().Be(10);
}
} }
} }

@ -11,7 +11,6 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -61,13 +60,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_firstFile.Quality = new QualityModel(Quality.SDTV); _firstFile.Quality = new QualityModel(Quality.SDTV);
} }
private void GivenAutoDownloadPropers()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.AutoDownloadPropers)
.Returns(true);
}
[Test] [Test]
public void should_return_false_when_episodeFile_was_added_more_than_7_days_ago() public void should_return_false_when_episodeFile_was_added_more_than_7_days_ago()
{ {
@ -118,6 +110,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
[Test] [Test]
public void should_return_false_when_proper_but_auto_download_propers_is_false() public void should_return_false_when_proper_but_auto_download_propers_is_false()
{ {
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotUpgrade);
_firstFile.Quality.Quality = Quality.DVD; _firstFile.Quality.Quality = Quality.DVD;
_firstFile.DateAdded = DateTime.Today; _firstFile.DateAdded = DateTime.Today;
@ -127,7 +123,22 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
[Test] [Test]
public void should_return_true_when_episodeFile_was_added_today() public void should_return_true_when_episodeFile_was_added_today()
{ {
GivenAutoDownloadPropers(); Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.PreferAndUpgrade);
_firstFile.Quality.Quality = Quality.DVD;
_firstFile.DateAdded = DateTime.Today;
Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_when_propers_are_not_preferred()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.DownloadPropersAndRepacks)
.Returns(ProperDownloadTypes.DoNotPrefer);
_firstFile.Quality.Quality = Quality.DVD; _firstFile.Quality.Quality = Quality.DVD;

@ -38,17 +38,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private static readonly int NoPreferredWordScore = 0; private static readonly int NoPreferredWordScore = 0;
private void GivenAutoDownloadPropers(bool autoDownloadPropers) private void GivenAutoDownloadPropers(ProperDownloadTypes type)
{ {
Mocker.GetMock<IConfigService>() Mocker.GetMock<IConfigService>()
.SetupGet(s => s.AutoDownloadPropers) .SetupGet(s => s.DownloadPropersAndRepacks)
.Returns(autoDownloadPropers); .Returns(type);
} }
[Test, TestCaseSource(nameof(IsUpgradeTestCases))] [Test, TestCaseSource(nameof(IsUpgradeTestCases))]
public void IsUpgradeTest(Quality current, int currentVersion, Quality newQuality, int newVersion, Quality cutoff, bool expected) public void IsUpgradeTest(Quality current, int currentVersion, Quality newQuality, int newVersion, Quality cutoff, bool expected)
{ {
GivenAutoDownloadPropers(true); GivenAutoDownloadPropers(ProperDownloadTypes.PreferAndUpgrade);
var profile = new QualityProfile var profile = new QualityProfile
@ -79,7 +79,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test, TestCaseSource("IsUpgradeTestCasesLanguages")] [Test, TestCaseSource("IsUpgradeTestCasesLanguages")]
public void IsUpgradeTestLanguage(Quality current, int currentVersion, Language currentLanguage, Quality newQuality, int newVersion, Language newLanguage, Quality cutoff, Language languageCutoff, bool expected) public void IsUpgradeTestLanguage(Quality current, int currentVersion, Language currentLanguage, Quality newQuality, int newVersion, Language newLanguage, Quality cutoff, Language languageCutoff, bool expected)
{ {
GivenAutoDownloadPropers(true); GivenAutoDownloadPropers(ProperDownloadTypes.PreferAndUpgrade);
var profile = new QualityProfile var profile = new QualityProfile
{ {
@ -108,9 +108,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
} }
[Test] [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 QualityProfile var profile = new QualityProfile
{ {
@ -127,11 +127,40 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.IsUpgradable( Subject.IsUpgradable(
profile, profile,
langProfile, langProfile,
new QualityModel(Quality.DVD, new Revision(version: 2)), new QualityModel(Quality.DVD, new Revision(version: 1)),
Language.English, Language.English,
NoPreferredWordScore, NoPreferredWordScore,
new QualityModel(Quality.DVD, new Revision(version: 2)),
Language.English,
NoPreferredWordScore)
.Should().BeTrue();
}
[Test]
public void should_return_false_if_proper_and_autoDownloadPropers_is_do_not_prefer()
{
GivenAutoDownloadPropers(ProperDownloadTypes.DoNotPrefer);
var profile = new QualityProfile
{
Items = Qualities.QualityFixture.GetDefaultQualities(),
};
var langProfile = new LanguageProfile
{
Languages = LanguageFixture.GetDefaultLanguages(),
Cutoff = Language.English
};
Subject.IsUpgradable(
profile,
langProfile,
new QualityModel(Quality.DVD, new Revision(version: 1)), new QualityModel(Quality.DVD, new Revision(version: 1)),
Language.English, Language.English,
NoPreferredWordScore,
new QualityModel(Quality.DVD, new Revision(version: 2)),
Language.English,
NoPreferredWordScore) NoPreferredWordScore)
.Should().BeFalse(); .Should().BeFalse();
} }

@ -9,6 +9,7 @@ using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Common.Http.Proxy; using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Security; using NzbDrone.Core.Security;
namespace NzbDrone.Core.Configuration namespace NzbDrone.Core.Configuration
@ -113,11 +114,11 @@ namespace NzbDrone.Core.Configuration
set { SetValue("MinimumAge", value); } 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 public bool EnableCompletedDownloadHandling

@ -2,6 +2,7 @@ using System.Collections.Generic;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Common.Http.Proxy; using NzbDrone.Common.Http.Proxy;
using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Security; using NzbDrone.Core.Security;
namespace NzbDrone.Core.Configuration namespace NzbDrone.Core.Configuration
@ -26,7 +27,7 @@ namespace NzbDrone.Core.Configuration
//Media Management //Media Management
bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; } bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; }
string RecycleBin { get; set; } string RecycleBin { get; set; }
bool AutoDownloadPropers { get; set; } ProperDownloadTypes DownloadPropersAndRepacks { get; set; }
bool CreateEmptySeriesFolders { get; set; } bool CreateEmptySeriesFolders { get; set; }
bool DeleteEmptyFolders { get; set; } bool DeleteEmptyFolders { get; set; }
FileDateType FileDate { get; set; } FileDateType FileDate { get; set; }

@ -0,0 +1,43 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(131)]
public class download_propers_config : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(SetMetadataFileExtension);
Execute.Sql("DELETE FROM Config WHERE Key = 'autodownloadpropers'");
}
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();
}
}
}
}
}
}
}

@ -1,21 +1,26 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Delay; using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.DecisionEngine namespace NzbDrone.Core.DecisionEngine
{ {
public class DownloadDecisionComparer : IComparer<DownloadDecision> public class DownloadDecisionComparer : IComparer<DownloadDecision>
{ {
private readonly IConfigService _configService;
private readonly IDelayProfileService _delayProfileService; private readonly IDelayProfileService _delayProfileService;
public delegate int CompareDelegate(DownloadDecision x, DownloadDecision y); public delegate int CompareDelegate(DownloadDecision x, DownloadDecision y);
public delegate int CompareDelegate<TSubject, TValue>(DownloadDecision x, DownloadDecision y); public delegate int CompareDelegate<TSubject, TValue>(DownloadDecision x, DownloadDecision y);
public DownloadDecisionComparer(IDelayProfileService delayProfileService) public DownloadDecisionComparer(IConfigService configService, IDelayProfileService delayProfileService)
{ {
_configService = configService;
_delayProfileService = delayProfileService; _delayProfileService = delayProfileService;
} }
@ -59,6 +64,12 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareQuality(DownloadDecision x, DownloadDecision y) private int CompareQuality(DownloadDecision x, DownloadDecision y)
{ {
if (_configService.DownloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer)
{
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.QualityProfile.Value.GetIndex(remoteEpisode.ParsedEpisodeInfo.Quality.Quality)),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real));
}
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.QualityProfile.Value.GetIndex(remoteEpisode.ParsedEpisodeInfo.Quality.Quality)), return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.QualityProfile.Value.GetIndex(remoteEpisode.ParsedEpisodeInfo.Quality.Quality)),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real), CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version)); CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version));

@ -1,7 +1,7 @@
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Profiles.Delay; using NzbDrone.Core.Profiles.Delay;
using NzbDrone.Core.Languages;
namespace NzbDrone.Core.DecisionEngine namespace NzbDrone.Core.DecisionEngine
{ {
@ -12,10 +12,12 @@ namespace NzbDrone.Core.DecisionEngine
public class DownloadDecisionPriorizationService : IPrioritizeDownloadDecision public class DownloadDecisionPriorizationService : IPrioritizeDownloadDecision
{ {
private readonly IConfigService _configService;
private readonly IDelayProfileService _delayProfileService; private readonly IDelayProfileService _delayProfileService;
public DownloadDecisionPriorizationService(IDelayProfileService delayProfileService) public DownloadDecisionPriorizationService(IConfigService configService, IDelayProfileService delayProfileService)
{ {
_configService = configService;
_delayProfileService = delayProfileService; _delayProfileService = delayProfileService;
} }
@ -24,7 +26,7 @@ namespace NzbDrone.Core.DecisionEngine
return decisions.Where(c => c.RemoteEpisode.Series != null) return decisions.Where(c => c.RemoteEpisode.Series != null)
.GroupBy(c => c.RemoteEpisode.Series.Id, (seriesId, downloadDecisions) => .GroupBy(c => c.RemoteEpisode.Series.Id, (seriesId, downloadDecisions) =>
{ {
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_delayProfileService)); return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_configService, _delayProfileService));
}) })
.SelectMany(c => c) .SelectMany(c => c)
.Union(decisions.Where(c => c.RemoteEpisode.Series == null)) .Union(decisions.Where(c => c.RemoteEpisode.Series == null))

@ -4,6 +4,7 @@ using NLog;
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
{ {
@ -30,20 +31,28 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
return Decision.Accept(); return Decision.Accept();
} }
var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks;
if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer)
{
_logger.Debug("Propers are not preferred, skipping check");
return Decision.Accept();
}
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{ {
if (_upgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality)) if (_upgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality))
{ {
if (file.DateAdded < DateTime.Today.AddDays(-7)) if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotUpgrade)
{ {
_logger.Debug("Proper for old file, rejecting: {0}", subject); _logger.Debug("Auto downloading of propers is disabled");
return Decision.Reject("Proper for old file"); return Decision.Reject("Proper downloading is disabled");
} }
if (!_configService.AutoDownloadPropers) if (file.DateAdded < DateTime.Today.AddDays(-7))
{ {
_logger.Debug("Auto downloading of propers is disabled"); _logger.Debug("Proper for old file, rejecting: {0}", subject);
return Decision.Reject("Proper downloading is disabled"); return Decision.Reject("Proper for old file");
} }
} }
} }

@ -1,4 +1,5 @@
using NLog; using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Languages; using NzbDrone.Core.Profiles.Languages;
using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Profiles.Qualities;
@ -18,65 +19,52 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public class UpgradableSpecification : IUpgradableSpecification public class UpgradableSpecification : IUpgradableSpecification
{ {
private readonly IConfigService _configService;
private readonly Logger _logger; private readonly Logger _logger;
public UpgradableSpecification(Logger logger) public UpgradableSpecification(IConfigService configService, Logger logger)
{ {
_configService = configService;
_logger = logger; _logger = logger;
} }
private bool IsLanguageUpgradable(LanguageProfile profile, Language currentLanguage, Language newLanguage = null) private bool IsPreferredWordUpgradable(int currentScore, int newScore)
{
if (newLanguage != null)
{
var compare = new LanguageComparer(profile).Compare(newLanguage, currentLanguage);
if (compare <= 0)
{ {
return false; return newScore > currentScore;
}
}
return true;
} }
private bool IsQualityUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) public bool IsUpgradable(QualityProfile qualityProfile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, int currentScore, QualityModel newQuality, Language newLanguage, int newScore)
{
if (newQuality != null)
{ {
var compare = new QualityModelComparer(profile).Compare(newQuality, currentQuality); var qualityComparer = new QualityModelComparer(qualityProfile);
var qualityCompare = qualityComparer.Compare(newQuality?.Quality, currentQuality.Quality);
if (compare <= 0) if (qualityCompare > 0)
{ {
_logger.Debug("Existing item has better quality, skipping");
return false;
}
}
return true; return true;
} }
private bool IsPreferredWordUpgradable(int currentScore, int newScore) if (qualityCompare < 0)
{ {
return newScore > currentScore; _logger.Debug("Existing item has better quality, skipping");
return false;
} }
public bool IsUpgradable(QualityProfile qualityProfile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, int currentScore, QualityModel newQuality, Language newLanguage, int newScore) // Accept unless the user doesn't want to prefer propers, optionally they can
{ // use preferred words to prefer propers/repacks over non-propers/repacks.
if (IsQualityUpgradable(qualityProfile, currentQuality, newQuality)) if (_configService.DownloadPropersAndRepacks != ProperDownloadTypes.DoNotPrefer &&
newQuality?.Revision.CompareTo(currentQuality.Revision) > 0)
{ {
return true; return true;
} }
if (new QualityModelComparer(qualityProfile).Compare(newQuality, currentQuality) < 0) var languageCompare = new LanguageComparer(languageProfile).Compare(newLanguage, currentLanguage);
{
_logger.Debug("Existing item has better quality, skipping");
return false;
}
if (IsLanguageUpgradable(languageProfile, currentLanguage, newLanguage)) if (languageCompare > 0)
{ {
return true; return true;
} }
if (new LanguageComparer(languageProfile).Compare(newLanguage, currentLanguage) < 0) if (languageCompare < 0)
{ {
_logger.Debug("Existing item has better language, skipping"); _logger.Debug("Existing item has better language, skipping");
return false; return false;

@ -136,6 +136,7 @@
<Compile Include="Configuration\InvalidConfigFileException.cs" /> <Compile Include="Configuration\InvalidConfigFileException.cs" />
<Compile Include="Configuration\RescanAfterRefreshType.cs" /> <Compile Include="Configuration\RescanAfterRefreshType.cs" />
<Compile Include="Configuration\ResetApiKeyCommand.cs" /> <Compile Include="Configuration\ResetApiKeyCommand.cs" />
<Compile Include="Datastore\Migration\131_download_propers_config.cs" />
<Compile Include="Datastore\Migration\130_episode_last_searched_time.cs" /> <Compile Include="Datastore\Migration\130_episode_last_searched_time.cs" />
<Compile Include="Datastore\Migration\129_add_relative_original_path_to_episode_file.cs" /> <Compile Include="Datastore\Migration\129_add_relative_original_path_to_episode_file.cs" />
<Compile Include="Datastore\Migration\128_rename_quality_profiles_add_upgrade_allowed.cs" /> <Compile Include="Datastore\Migration\128_rename_quality_profiles_add_upgrade_allowed.cs" />
@ -1034,6 +1035,7 @@
<Compile Include="Profiles\Releases\TermMatchers\ITermMatcher.cs" /> <Compile Include="Profiles\Releases\TermMatchers\ITermMatcher.cs" />
<Compile Include="Profiles\Releases\TermMatchers\RegexTermMatcher.cs" /> <Compile Include="Profiles\Releases\TermMatchers\RegexTermMatcher.cs" />
<Compile Include="ProgressMessaging\ProgressMessageContext.cs" /> <Compile Include="ProgressMessaging\ProgressMessageContext.cs" />
<Compile Include="Qualities\ProperDownloadTypes.cs" />
<Compile Include="Qualities\QualityDetectionSource.cs" /> <Compile Include="Qualities\QualityDetectionSource.cs" />
<Compile Include="Qualities\QualityFinder.cs" /> <Compile Include="Qualities\QualityFinder.cs" />
<Compile Include="Qualities\QualitySource.cs" /> <Compile Include="Qualities\QualitySource.cs" />

@ -0,0 +1,9 @@
namespace NzbDrone.Core.Qualities
{
public enum ProperDownloadTypes
{
PreferAndUpgrade,
DoNotUpgrade,
DoNotPrefer
}
}

@ -1,6 +1,7 @@
using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration;
using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Qualities;
using Sonarr.Http.REST; using Sonarr.Http.REST;
namespace Sonarr.Api.V3.Config namespace Sonarr.Api.V3.Config
@ -9,7 +10,7 @@ namespace Sonarr.Api.V3.Config
{ {
public bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; } public bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; }
public string RecycleBin { get; set; } public string RecycleBin { get; set; }
public bool AutoDownloadPropers { get; set; } public ProperDownloadTypes DownloadPropersAndRepacks { get; set; }
public bool CreateEmptySeriesFolders { get; set; } public bool CreateEmptySeriesFolders { get; set; }
public bool DeleteEmptyFolders { get; set; } public bool DeleteEmptyFolders { get; set; }
public FileDateType FileDate { get; set; } public FileDateType FileDate { get; set; }
@ -37,7 +38,7 @@ namespace Sonarr.Api.V3.Config
{ {
AutoUnmonitorPreviouslyDownloadedEpisodes = model.AutoUnmonitorPreviouslyDownloadedEpisodes, AutoUnmonitorPreviouslyDownloadedEpisodes = model.AutoUnmonitorPreviouslyDownloadedEpisodes,
RecycleBin = model.RecycleBin, RecycleBin = model.RecycleBin,
AutoDownloadPropers = model.AutoDownloadPropers, DownloadPropersAndRepacks = model.DownloadPropersAndRepacks,
CreateEmptySeriesFolders = model.CreateEmptySeriesFolders, CreateEmptySeriesFolders = model.CreateEmptySeriesFolders,
DeleteEmptyFolders = model.DeleteEmptyFolders, DeleteEmptyFolders = model.DeleteEmptyFolders,
FileDate = model.FileDate, FileDate = model.FileDate,

Loading…
Cancel
Save