diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.css b/frontend/src/Settings/Quality/Definition/QualityDefinition.css
index ef4deb180..e81febc1e 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinition.css
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.css
@@ -31,7 +31,7 @@
background-color: $sliderAccentColor;
box-shadow: 0 0 0 #000;
- &:nth-child(odd) {
+ &:nth-child(3n+1) {
background-color: #ddd;
}
}
@@ -56,7 +56,7 @@
.megabytesPerMinute {
display: flex;
justify-content: space-between;
- flex: 0 0 250px;
+ flex: 0 0 400px;
}
.sizeInput {
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinition.js b/frontend/src/Settings/Quality/Definition/QualityDefinition.js
index 53e1718f1..172804947 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinition.js
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinition.js
@@ -48,21 +48,24 @@ class QualityDefinition extends Component {
this.state = {
sliderMinSize: getSliderValue(props.minSize, slider.min),
- sliderMaxSize: getSliderValue(props.maxSize, slider.max)
+ sliderMaxSize: getSliderValue(props.maxSize, slider.max),
+ sliderPreferredSize: getSliderValue(props.preferredSize, (slider.max - 3))
};
}
//
// Listeners
- onSliderChange = ([sliderMinSize, sliderMaxSize]) => {
+ onSliderChange = ([sliderMinSize, sliderPreferredSize, sliderMaxSize]) => {
this.setState({
sliderMinSize,
- sliderMaxSize
+ sliderMaxSize,
+ sliderPreferredSize
});
this.props.onSizeChange({
minSize: roundNumber(Math.pow(sliderMinSize, 1.1)),
+ preferredSize: sliderPreferredSize === (slider.max - 3) ? null : roundNumber(Math.pow(sliderPreferredSize, 1.1)),
maxSize: sliderMaxSize === slider.max ? null : roundNumber(Math.pow(sliderMaxSize, 1.1))
});
}
@@ -70,12 +73,14 @@ class QualityDefinition extends Component {
onAfterSliderChange = () => {
const {
minSize,
- maxSize
+ maxSize,
+ preferredSize
} = this.props;
this.setState({
sliderMiSize: getSliderValue(minSize, slider.min),
- sliderMaxSize: getSliderValue(maxSize, slider.max)
+ sliderMaxSize: getSliderValue(maxSize, slider.max),
+ sliderPreferredSize: getSliderValue(preferredSize, (slider.max - 3)) // fix
});
}
@@ -88,7 +93,22 @@ class QualityDefinition extends Component {
this.props.onSizeChange({
minSize,
- maxSize: this.props.maxSize
+ maxSize: this.props.maxSize,
+ preferredSize: this.props.preferredSize
+ });
+ }
+
+ onPreferredSizeChange = ({ value }) => {
+ const preferredSize = value === (MAX - 3) ? null : getValue(value);
+
+ this.setState({
+ sliderPreferredSize: getSliderValue(preferredSize, slider.preferred)
+ });
+
+ this.props.onSizeChange({
+ minSize: this.props.minSize,
+ maxSize: this.props.maxSize,
+ preferredSize
});
}
@@ -101,7 +121,8 @@ class QualityDefinition extends Component {
this.props.onSizeChange({
minSize: this.props.minSize,
- maxSize
+ maxSize,
+ preferredSize: this.props.preferredSize
});
}
@@ -115,18 +136,23 @@ class QualityDefinition extends Component {
title,
minSize,
maxSize,
+ preferredSize,
advancedSettings,
onTitleChange
} = this.props;
const {
sliderMinSize,
- sliderMaxSize
+ sliderMaxSize,
+ sliderPreferredSize
} = this.state;
const minBytes = minSize * 1024 * 1024;
const minSixty = `${formatBytes(minBytes * 60)}/h`;
+ const preferredBytes = preferredSize * 1024 * 1024;
+ const preferredSixty = preferredBytes ? `${formatBytes(preferredBytes * 60)}/h` : 'Unlimited';
+
const maxBytes = maxSize && maxSize * 1024 * 1024;
const maxSixty = maxBytes ? `${formatBytes(maxBytes * 60)}/h` : 'Unlimited';
@@ -149,9 +175,10 @@ class QualityDefinition extends Component {
min={slider.min}
max={slider.max}
step={slider.step}
- minDistance={10}
- value={[sliderMinSize, sliderMaxSize]}
+ minDistance={3}
+ value={[sliderMinSize, sliderPreferredSize, sliderMaxSize]}
withTracks={true}
+ allowCross={false}
snapDragDisabled={true}
className={styles.slider}
trackClassName={styles.bar}
@@ -177,6 +204,22 @@ class QualityDefinition extends Component {
/>
+
+
{preferredSixty}
+ }
+ title="Preferred Size"
+ body={
+
+ }
+ position={tooltipPositions.BOTTOM}
+ />
+
+
+
+ Preferred
+
+
+
+
Max
@@ -220,7 +278,7 @@ class QualityDefinition extends Component {
className={styles.sizeInput}
name={`${id}.min`}
value={maxSize || MAX}
- min={minSize + 10}
+ min={minSize + 5}
max={MAX}
step={0.1}
isFloat={true}
@@ -240,6 +298,7 @@ QualityDefinition.propTypes = {
title: PropTypes.string.isRequired,
minSize: PropTypes.number,
maxSize: PropTypes.number,
+ preferredSize: PropTypes.number,
advancedSettings: PropTypes.bool.isRequired,
onTitleChange: PropTypes.func.isRequired,
onSizeChange: PropTypes.func.isRequired
diff --git a/frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js b/frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js
index a76c9440f..5f23d2765 100644
--- a/frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js
+++ b/frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js
@@ -23,11 +23,12 @@ class QualityDefinitionConnector extends Component {
this.props.setQualityDefinitionValue({ id: this.props.id, name: 'title', value });
}
- onSizeChange = ({ minSize, maxSize }) => {
+ onSizeChange = ({ minSize, maxSize, preferredSize }) => {
const {
id,
minSize: currentMinSize,
- maxSize: currentMaxSize
+ maxSize: currentMaxSize,
+ preferredSize: currentPreferredSize
} = this.props;
if (minSize !== currentMinSize) {
@@ -37,6 +38,10 @@ class QualityDefinitionConnector extends Component {
if (maxSize !== currentMaxSize) {
this.props.setQualityDefinitionValue({ id, name: 'maxSize', value: maxSize });
}
+
+ if (preferredSize !== currentPreferredSize) {
+ this.props.setQualityDefinitionValue({ id, name: 'preferredSize', value: preferredSize });
+ }
}
//
@@ -57,6 +62,7 @@ QualityDefinitionConnector.propTypes = {
id: PropTypes.number.isRequired,
minSize: PropTypes.number,
maxSize: PropTypes.number,
+ preferredSize: PropTypes.number,
setQualityDefinitionValue: PropTypes.func.isRequired,
clearPendingChanges: PropTypes.func.isRequired
};
diff --git a/package.json b/package.json
index 390156e06..a67691435 100644
--- a/package.json
+++ b/package.json
@@ -105,7 +105,7 @@
"react-popper": "1.3.7",
"react-redux": "7.1.3",
"react-router-dom": "5.1.2",
- "react-slider": "1.0.1",
+ "react-slider": "1.0.3",
"react-tabs": "3.0.0",
"react-text-truncate": "0.15.0",
"react-virtualized": "9.21.1",
diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs
index de120fd99..4ee35f7ab 100644
--- a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs
+++ b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
@@ -37,9 +37,20 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_customFormat2 = new CustomFormat("My Format 2", new LanguageSpecification { Value = (int)Language.French }) { Id = 2 };
CustomFormatsFixture.GivenCustomFormats(_customFormat1, _customFormat2);
+
+ Mocker.GetMock()
+ .Setup(s => s.Get(It.IsAny()))
+ .Returns(new QualityDefinition { PreferredSize = null });
}
- private RemoteMovie GivenRemoteMovie(QualityModel quality, int age = 0, long size = 0, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet)
+ private void GivenPreferredSize(double? size)
+ {
+ Mocker.GetMock()
+ .Setup(s => s.Get(It.IsAny()))
+ .Returns(new QualityDefinition { PreferredSize = size });
+ }
+
+ private RemoteMovie GivenRemoteMovie(QualityModel quality, int age = 0, long size = 0, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet, int runtime = 150)
{
var remoteMovie = new RemoteMovie();
remoteMovie.ParsedMovieInfo = new ParsedMovieInfo();
@@ -54,7 +65,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
FormatItems = CustomFormatsFixture.GetSampleFormatItems(_customFormat1.Name, _customFormat2.Name),
MinFormatScore = 0
})
- .With(m => m.Title = "A Movie").Build();
+ .With(m => m.Title = "A Movie")
+ .With(m => m.Runtime = runtime).Build();
remoteMovie.Release = new ReleaseInfo();
remoteMovie.Release.PublishDate = DateTime.Now.AddDays(-age);
@@ -81,12 +93,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_put_propers_before_non_propers()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p, new Revision(version: 1)));
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p, new Revision(version: 2)));
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p, new Revision(version: 1)));
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p, new Revision(version: 2)));
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ 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);
@@ -95,12 +107,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_put_higher_quality_before_lower()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.SDTV));
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.SDTV));
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
qualifiedReports.First().RemoteMovie.ParsedMovieInfo.Quality.Quality.Should().Be(Quality.HDTV720p);
@@ -109,33 +121,105 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_order_by_age_then_largest_rounded_to_200mb()
{
- var remoteEpisodeSd = GivenRemoteMovie(new QualityModel(Quality.SDTV), size: 100.Megabytes(), age: 1);
- var remoteEpisodeHdSmallOld = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 1200.Megabytes(), age: 1000);
- var remoteEpisodeSmallYoung = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 1250.Megabytes(), age: 10);
- var remoteEpisodeHdLargeYoung = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 3000.Megabytes(), age: 1);
+ var remoteMovieSd = GivenRemoteMovie(new QualityModel(Quality.SDTV), size: 100.Megabytes(), age: 1);
+ var remoteMovieHdSmallOld = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 1200.Megabytes(), age: 1000);
+ var remoteMovieSmallYoung = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 1250.Megabytes(), age: 10);
+ var remoteMovieHdLargeYoung = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 3000.Megabytes(), age: 1);
+
+ var decisions = new List();
+ decisions.Add(new DownloadDecision(remoteMovieSd));
+ decisions.Add(new DownloadDecision(remoteMovieHdSmallOld));
+ decisions.Add(new DownloadDecision(remoteMovieSmallYoung));
+ decisions.Add(new DownloadDecision(remoteMovieHdLargeYoung));
+
+ var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
+ qualifiedReports.First().RemoteMovie.Should().Be(remoteMovieHdLargeYoung);
+ }
+
+ [Test]
+ public void should_order_by_closest_to_preferred_size_if_both_over()
+ {
+ // 2 MB/Min * 150 Min Runtime = 300 MB
+ GivenPreferredSize(2);
+
+ var remoteMovieSmall = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 400.Megabytes(), age: 1);
+ var remoteMovieLarge = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 15000.Megabytes(), age: 1);
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisodeSd));
- decisions.Add(new DownloadDecision(remoteEpisodeHdSmallOld));
- decisions.Add(new DownloadDecision(remoteEpisodeSmallYoung));
- decisions.Add(new DownloadDecision(remoteEpisodeHdLargeYoung));
+ decisions.Add(new DownloadDecision(remoteMovieSmall));
+ decisions.Add(new DownloadDecision(remoteMovieLarge));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
- qualifiedReports.First().RemoteMovie.Should().Be(remoteEpisodeHdLargeYoung);
+ qualifiedReports.First().RemoteMovie.Should().Be(remoteMovieSmall);
+ }
+
+ [Test]
+ public void should_order_by_largest_to_if_zero_runtime()
+ {
+ // 2 MB/Min * 150 Min Runtime = 300 MB
+ GivenPreferredSize(2);
+
+ var remoteMovieSmall = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 400.Megabytes(), age: 1, runtime: 0);
+ var remoteMovieLarge = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 15000.Megabytes(), age: 1, runtime: 0);
+
+ var decisions = new List();
+ decisions.Add(new DownloadDecision(remoteMovieSmall));
+ decisions.Add(new DownloadDecision(remoteMovieLarge));
+
+ var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
+ qualifiedReports.First().RemoteMovie.Should().Be(remoteMovieLarge);
+ }
+
+ [Test]
+ public void should_order_by_closest_to_preferred_size_if_both_under()
+ {
+ // 390 MB/Min * 150 Min Runtime = 58,500 MB
+ GivenPreferredSize(390);
+
+ var remoteMovieSmall = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 100.Megabytes(), age: 1);
+ var remoteMovieLarge = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 15000.Megabytes(), age: 1);
+
+ var decisions = new List();
+ decisions.Add(new DownloadDecision(remoteMovieSmall));
+ decisions.Add(new DownloadDecision(remoteMovieLarge));
+
+ var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
+ qualifiedReports.First().RemoteMovie.Should().Be(remoteMovieLarge);
+ }
+
+ [Test]
+ public void should_order_by_closest_to_preferred_size_if_preferred_is_in_between()
+ {
+ // 46 MB/Min * 150 Min Runtime = 6900 MB
+ GivenPreferredSize(46);
+
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 100.Megabytes(), age: 1);
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 500.Megabytes(), age: 1);
+ var remoteMovie3 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 7000.Megabytes(), age: 1);
+ var remoteMovie4 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), size: 15000.Megabytes(), age: 1);
+
+ var decisions = new List();
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
+ decisions.Add(new DownloadDecision(remoteMovie3));
+ decisions.Add(new DownloadDecision(remoteMovie4));
+
+ var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
+ qualifiedReports.First().RemoteMovie.Should().Be(remoteMovie3);
}
[Test]
public void should_order_by_youngest()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), age: 10);
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), age: 5);
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), age: 10);
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), age: 5);
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
- qualifiedReports.First().RemoteMovie.Should().Be(remoteEpisode2);
+ qualifiedReports.First().RemoteMovie.Should().Be(remoteMovie2);
}
[Test]
@@ -143,12 +227,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
GivenPreferredDownloadProtocol(DownloadProtocol.Usenet);
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Torrent);
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Usenet);
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Torrent);
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Usenet);
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
qualifiedReports.First().RemoteMovie.Release.DownloadProtocol.Should().Be(DownloadProtocol.Usenet);
@@ -159,12 +243,12 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
GivenPreferredDownloadProtocol(DownloadProtocol.Torrent);
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Torrent);
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Usenet);
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Torrent);
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p), downloadProtocol: DownloadProtocol.Usenet);
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
qualifiedReports.First().RemoteMovie.Release.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
@@ -173,8 +257,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_prefer_releases_with_more_seeders()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now;
@@ -185,14 +269,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var torrentInfo2 = torrentInfo1.JsonClone();
torrentInfo2.Seeders = 100;
- remoteEpisode1.Release = torrentInfo1;
- remoteEpisode1.Release.Title = "A Movie 1998";
- remoteEpisode2.Release = torrentInfo2;
- remoteEpisode2.Release.Title = "A Movie 1998";
+ remoteMovie1.Release = torrentInfo1;
+ remoteMovie1.Release.Title = "A Movie 1998";
+ remoteMovie2.Release = torrentInfo2;
+ remoteMovie2.Release.Title = "A Movie 1998";
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
((TorrentInfo)qualifiedReports.First().RemoteMovie.Release).Seeders.Should().Be(torrentInfo2.Seeders);
@@ -201,8 +285,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_prefer_releases_with_more_peers_given_equal_number_of_seeds()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now;
@@ -214,14 +298,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var torrentInfo2 = torrentInfo1.JsonClone();
torrentInfo2.Peers = 100;
- remoteEpisode1.Release = torrentInfo1;
- remoteEpisode1.Release.Title = "A Movie 1998";
- remoteEpisode2.Release = torrentInfo2;
- remoteEpisode2.Release.Title = "A Movie 1998";
+ remoteMovie1.Release = torrentInfo1;
+ remoteMovie1.Release.Title = "A Movie 1998";
+ remoteMovie2.Release = torrentInfo2;
+ remoteMovie2.Release.Title = "A Movie 1998";
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
((TorrentInfo)qualifiedReports.First().RemoteMovie.Release).Peers.Should().Be(torrentInfo2.Peers);
@@ -230,8 +314,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_prefer_releases_with_more_peers_no_seeds()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now;
@@ -244,14 +328,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
torrentInfo2.Seeders = 0;
torrentInfo2.Peers = 100;
- remoteEpisode1.Release = torrentInfo1;
- remoteEpisode1.Release.Title = "A Movie 1998";
- remoteEpisode2.Release = torrentInfo2;
- remoteEpisode2.Release.Title = "A Movie 1998";
+ remoteMovie1.Release = torrentInfo1;
+ remoteMovie1.Release.Title = "A Movie 1998";
+ remoteMovie2.Release = torrentInfo2;
+ remoteMovie2.Release.Title = "A Movie 1998";
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
((TorrentInfo)qualifiedReports.First().RemoteMovie.Release).Peers.Should().Be(torrentInfo2.Peers);
@@ -260,8 +344,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_prefer_first_release_if_peers_and_size_are_too_similar()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
var torrentInfo1 = new TorrentInfo();
torrentInfo1.PublishDate = DateTime.Now;
@@ -275,14 +359,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
torrentInfo2.Peers = 10;
torrentInfo1.Size = 250.Megabytes();
- remoteEpisode1.Release = torrentInfo1;
- remoteEpisode1.Release.Title = "A Movie 1998";
- remoteEpisode2.Release = torrentInfo2;
- remoteEpisode2.Release.Title = "A Movie 1998";
+ remoteMovie1.Release = torrentInfo1;
+ remoteMovie1.Release.Title = "A Movie 1998";
+ remoteMovie2.Release = torrentInfo2;
+ remoteMovie2.Release.Title = "A Movie 1998";
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
((TorrentInfo)qualifiedReports.First().RemoteMovie.Release).Should().Be(torrentInfo1);
@@ -291,38 +375,38 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_prefer_first_release_if_age_and_size_are_too_similar()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
- remoteEpisode1.Release.PublishDate = DateTime.UtcNow.AddDays(-100);
- remoteEpisode1.Release.Size = 200.Megabytes();
+ remoteMovie1.Release.PublishDate = DateTime.UtcNow.AddDays(-100);
+ remoteMovie1.Release.Size = 200.Megabytes();
- remoteEpisode2.Release.PublishDate = DateTime.UtcNow.AddDays(-150);
- remoteEpisode2.Release.Size = 250.Megabytes();
+ remoteMovie2.Release.PublishDate = DateTime.UtcNow.AddDays(-150);
+ remoteMovie2.Release.Size = 250.Megabytes();
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
- qualifiedReports.First().RemoteMovie.Release.Should().Be(remoteEpisode1.Release);
+ qualifiedReports.First().RemoteMovie.Release.Should().Be(remoteMovie1.Release);
}
[Test]
public void should_prefer_more_prioritized_words()
{
- var remoteEpisode1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
- var remoteEpisode2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie1 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
+ var remoteMovie2 = GivenRemoteMovie(new QualityModel(Quality.HDTV720p));
- remoteEpisode1.Release.Title += " DTS-HD";
- remoteEpisode2.Release.Title += " DTS-HD SPARKS";
+ remoteMovie1.Release.Title += " DTS-HD";
+ remoteMovie2.Release.Title += " DTS-HD SPARKS";
var decisions = new List();
- decisions.Add(new DownloadDecision(remoteEpisode1));
- decisions.Add(new DownloadDecision(remoteEpisode2));
+ decisions.Add(new DownloadDecision(remoteMovie1));
+ decisions.Add(new DownloadDecision(remoteMovie2));
var qualifiedReports = Subject.PrioritizeDecisionsForMovies(decisions);
- qualifiedReports.First().RemoteMovie.Release.Should().Be(remoteEpisode2.Release);
+ qualifiedReports.First().RemoteMovie.Release.Should().Be(remoteMovie2.Release);
}
[Test]
diff --git a/src/NzbDrone.Core/Datastore/Migration/171_quality_definition_preferred_size.cs b/src/NzbDrone.Core/Datastore/Migration/171_quality_definition_preferred_size.cs
new file mode 100644
index 000000000..f9ac4b7aa
--- /dev/null
+++ b/src/NzbDrone.Core/Datastore/Migration/171_quality_definition_preferred_size.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.Data;
+using Dapper;
+using FluentMigrator;
+using NzbDrone.Core.Datastore.Migration.Framework;
+
+namespace NzbDrone.Core.Datastore.Migration
+{
+ [Migration(171)]
+ public class quality_definition_preferred_size : NzbDroneMigrationBase
+ {
+ protected override void MainDbUpgrade()
+ {
+ Alter.Table("QualityDefinitions").AddColumn("PreferredSize").AsDouble().Nullable();
+
+ Execute.WithConnection(UpdateQualityDefinitions);
+ }
+
+ private void UpdateQualityDefinitions(IDbConnection conn, IDbTransaction tran)
+ {
+ var existing = conn.Query("SELECT Id, MaxSize FROM QualityDefinitions");
+
+ var updated = new List();
+
+ foreach (var row in existing)
+ {
+ var maxSize = row.MaxSize;
+ var preferredSize = maxSize;
+
+ if (maxSize.HasValue && maxSize.Value > 5)
+ {
+ preferredSize = maxSize.Value - 5;
+ }
+
+ updated.Add(new QualityDefinition171
+ {
+ Id = row.Id,
+ PreferredSize = preferredSize
+ });
+ }
+
+ var updateSql = "UPDATE QualityDefinitions SET PreferredSize = @PreferredSize WHERE Id = @Id";
+ conn.Execute(updateSql, updated, transaction: tran);
+ }
+
+ private class QualityDefinition170 : ModelBase
+ {
+ public int? MaxSize { get; set; }
+ }
+
+ private class QualityDefinition171 : ModelBase
+ {
+ public int? PreferredSize { get; set; }
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs
index cb83fd9bc..653fc4c1b 100644
--- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs
+++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs
@@ -5,6 +5,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles.Delay;
+using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine
{
@@ -12,14 +13,16 @@ namespace NzbDrone.Core.DecisionEngine
{
private readonly IConfigService _configService;
private readonly IDelayProfileService _delayProfileService;
+ private readonly IQualityDefinitionService _qualityDefinitionService;
public delegate int CompareDelegate(DownloadDecision x, DownloadDecision y);
public delegate int CompareDelegate(DownloadDecision x, DownloadDecision y);
- public DownloadDecisionComparer(IConfigService configService, IDelayProfileService delayProfileService)
+ public DownloadDecisionComparer(IConfigService configService, IDelayProfileService delayProfileService, IQualityDefinitionService qualityDefinitionService)
{
_configService = configService;
_delayProfileService = delayProfileService;
+ _qualityDefinitionService = qualityDefinitionService;
}
public int Compare(DownloadDecision x, DownloadDecision y)
@@ -164,8 +167,25 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareSize(DownloadDecision x, DownloadDecision y)
{
- // TODO: Is smaller better? Smaller for usenet could mean no par2 files.
- return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes()));
+ var sizeCompare = CompareBy(x.RemoteMovie, y.RemoteMovie, remoteMovie =>
+ {
+ var preferredSize = _qualityDefinitionService.Get(remoteMovie.ParsedMovieInfo.Quality.Quality).PreferredSize;
+
+ // If no value for preferred it means unlimited so fallback to sort largest is best
+ if (preferredSize.HasValue && remoteMovie.Movie.Runtime > 0)
+ {
+ var preferredMovieSize = remoteMovie.Movie.Runtime * preferredSize.Value.Megabytes();
+
+ // Calculate closest to the preferred size
+ return Math.Abs((remoteMovie.Release.Size - preferredMovieSize).Round(200.Megabytes())) * (-1);
+ }
+ else
+ {
+ return remoteMovie.Release.Size.Round(200.Megabytes());
+ }
+ });
+
+ return sizeCompare;
}
private int ScoreFlags(IndexerFlags flags)
diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionPriorizationService.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionPriorizationService.cs
index 163ff7a9f..e84e20f51 100644
--- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionPriorizationService.cs
+++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionPriorizationService.cs
@@ -3,6 +3,7 @@ using System.Linq;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Profiles.Delay;
+using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine
{
@@ -15,11 +16,13 @@ namespace NzbDrone.Core.DecisionEngine
{
private readonly IConfigService _configService;
private readonly IDelayProfileService _delayProfileService;
+ private readonly IQualityDefinitionService _qualityDefinitionService;
- public DownloadDecisionPriorizationService(IConfigService configService, IDelayProfileService delayProfileService)
+ public DownloadDecisionPriorizationService(IConfigService configService, IDelayProfileService delayProfileService, IQualityDefinitionService qualityDefinitionService)
{
_configService = configService;
_delayProfileService = delayProfileService;
+ _qualityDefinitionService = qualityDefinitionService;
}
public List PrioritizeDecisionsForMovies(List decisions)
@@ -27,7 +30,7 @@ namespace NzbDrone.Core.DecisionEngine
return decisions.Where(c => c.RemoteMovie.MappingResult == MappingResultType.Success || c.RemoteMovie.MappingResult == MappingResultType.SuccessLenientMapping)
.GroupBy(c => c.RemoteMovie.Movie.Id, (movieId, downloadDecisions) =>
{
- return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_configService, _delayProfileService));
+ return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_configService, _delayProfileService, _qualityDefinitionService));
})
.SelectMany(c => c)
.Union(decisions.Where(c => c.RemoteMovie.MappingResult != MappingResultType.Success || c.RemoteMovie.MappingResult != MappingResultType.SuccessLenientMapping))
diff --git a/src/NzbDrone.Core/Qualities/Quality.cs b/src/NzbDrone.Core/Qualities/Quality.cs
index 4a15f1bec..2bb812a9e 100644
--- a/src/NzbDrone.Core/Qualities/Quality.cs
+++ b/src/NzbDrone.Core/Qualities/Quality.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Datastore;
@@ -167,41 +167,41 @@ namespace NzbDrone.Core.Qualities
#if !LIBRARY
DefaultQualityDefinitions = new HashSet
{
- new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.WORKPRINT) { Weight = 2, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.CAM) { Weight = 3, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.TELESYNC) { Weight = 4, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.TELECINE) { Weight = 5, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.REGIONAL) { Weight = 6, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.DVDSCR) { Weight = 7, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.SDTV) { Weight = 8, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.DVD) { Weight = 9, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.DVDR) { Weight = 10, MinSize = 0, MaxSize = 100 },
-
- new QualityDefinition(Quality.WEBDL480p) { Weight = 11, MinSize = 0, MaxSize = 100, GroupName = "WEB 480p" },
- new QualityDefinition(Quality.WEBRip480p) { Weight = 11, MinSize = 0, MaxSize = 100, GroupName = "WEB 480p" },
- new QualityDefinition(Quality.Bluray480p) { Weight = 12, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.Bluray576p) { Weight = 13, MinSize = 0, MaxSize = 100 },
-
- new QualityDefinition(Quality.HDTV720p) { Weight = 14, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.WEBDL720p) { Weight = 15, MinSize = 0, MaxSize = 100, GroupName = "WEB 720p" },
- new QualityDefinition(Quality.WEBRip720p) { Weight = 15, MinSize = 0, MaxSize = 100, GroupName = "WEB 720p" },
- new QualityDefinition(Quality.Bluray720p) { Weight = 16, MinSize = 0, MaxSize = 100 },
-
- new QualityDefinition(Quality.HDTV1080p) { Weight = 17, MinSize = 0, MaxSize = 100 },
- new QualityDefinition(Quality.WEBDL1080p) { Weight = 18, MinSize = 0, MaxSize = 100, GroupName = "WEB 1080p" },
- new QualityDefinition(Quality.WEBRip1080p) { Weight = 18, MinSize = 0, MaxSize = 100, GroupName = "WEB 1080p" },
- new QualityDefinition(Quality.Bluray1080p) { Weight = 19, MinSize = 0, MaxSize = null },
- new QualityDefinition(Quality.Remux1080p) { Weight = 20, MinSize = 0, MaxSize = null },
-
- new QualityDefinition(Quality.HDTV2160p) { Weight = 21, MinSize = 0, MaxSize = null },
- new QualityDefinition(Quality.WEBDL2160p) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "WEB 2160p" },
- new QualityDefinition(Quality.WEBRip2160p) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "WEB 2160p" },
- new QualityDefinition(Quality.Bluray2160p) { Weight = 23, MinSize = 0, MaxSize = null },
- new QualityDefinition(Quality.Remux2160p) { Weight = 24, MinSize = 0, MaxSize = null },
-
- new QualityDefinition(Quality.BRDISK) { Weight = 25, MinSize = 0, MaxSize = null },
- new QualityDefinition(Quality.RAWHD) { Weight = 26, MinSize = 0, MaxSize = null }
+ new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.WORKPRINT) { Weight = 2, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.CAM) { Weight = 3, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.TELESYNC) { Weight = 4, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.TELECINE) { Weight = 5, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.REGIONAL) { Weight = 6, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.DVDSCR) { Weight = 7, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.SDTV) { Weight = 8, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.DVD) { Weight = 9, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.DVDR) { Weight = 10, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+
+ new QualityDefinition(Quality.WEBDL480p) { Weight = 11, MinSize = 0, MaxSize = 100, PreferredSize = 95, GroupName = "WEB 480p" },
+ new QualityDefinition(Quality.WEBRip480p) { Weight = 11, MinSize = 0, MaxSize = 100, PreferredSize = 95, GroupName = "WEB 480p" },
+ new QualityDefinition(Quality.Bluray480p) { Weight = 12, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.Bluray576p) { Weight = 13, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+
+ new QualityDefinition(Quality.HDTV720p) { Weight = 14, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.WEBDL720p) { Weight = 15, MinSize = 0, MaxSize = 100, PreferredSize = 95, GroupName = "WEB 720p" },
+ new QualityDefinition(Quality.WEBRip720p) { Weight = 15, MinSize = 0, MaxSize = 100, PreferredSize = 95, GroupName = "WEB 720p" },
+ new QualityDefinition(Quality.Bluray720p) { Weight = 16, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+
+ new QualityDefinition(Quality.HDTV1080p) { Weight = 17, MinSize = 0, MaxSize = 100, PreferredSize = 95 },
+ new QualityDefinition(Quality.WEBDL1080p) { Weight = 18, MinSize = 0, MaxSize = 100, PreferredSize = 95, GroupName = "WEB 1080p" },
+ new QualityDefinition(Quality.WEBRip1080p) { Weight = 18, MinSize = 0, MaxSize = 100, PreferredSize = 95, GroupName = "WEB 1080p" },
+ new QualityDefinition(Quality.Bluray1080p) { Weight = 19, MinSize = 0, MaxSize = null, PreferredSize = null },
+ new QualityDefinition(Quality.Remux1080p) { Weight = 20, MinSize = 0, MaxSize = null, PreferredSize = null },
+
+ new QualityDefinition(Quality.HDTV2160p) { Weight = 21, MinSize = 0, MaxSize = null, PreferredSize = null },
+ new QualityDefinition(Quality.WEBDL2160p) { Weight = 22, MinSize = 0, MaxSize = null, PreferredSize = null, GroupName = "WEB 2160p" },
+ new QualityDefinition(Quality.WEBRip2160p) { Weight = 22, MinSize = 0, MaxSize = null, PreferredSize = null, GroupName = "WEB 2160p" },
+ new QualityDefinition(Quality.Bluray2160p) { Weight = 23, MinSize = 0, MaxSize = null, PreferredSize = null },
+ new QualityDefinition(Quality.Remux2160p) { Weight = 24, MinSize = 0, MaxSize = null, PreferredSize = null },
+
+ new QualityDefinition(Quality.BRDISK) { Weight = 25, MinSize = 0, MaxSize = null, PreferredSize = null },
+ new QualityDefinition(Quality.RAWHD) { Weight = 26, MinSize = 0, MaxSize = null, PreferredSize = null }
};
#endif
}
diff --git a/src/NzbDrone.Core/Qualities/QualityDefinition.cs b/src/NzbDrone.Core/Qualities/QualityDefinition.cs
index 74fb21793..ce6307eac 100644
--- a/src/NzbDrone.Core/Qualities/QualityDefinition.cs
+++ b/src/NzbDrone.Core/Qualities/QualityDefinition.cs
@@ -12,6 +12,7 @@ namespace NzbDrone.Core.Qualities
public double? MinSize { get; set; }
public double? MaxSize { get; set; }
+ public double? PreferredSize { get; set; }
public QualityDefinition()
{
diff --git a/src/Radarr.Api.V3/Qualities/QualityDefinitionResource.cs b/src/Radarr.Api.V3/Qualities/QualityDefinitionResource.cs
index eb0c0ec18..1522fe563 100644
--- a/src/Radarr.Api.V3/Qualities/QualityDefinitionResource.cs
+++ b/src/Radarr.Api.V3/Qualities/QualityDefinitionResource.cs
@@ -15,6 +15,7 @@ namespace Radarr.Api.V3.Qualities
public double? MinSize { get; set; }
public double? MaxSize { get; set; }
+ public double? PreferredSize { get; set; }
}
public static class QualityDefinitionResourceMapper
@@ -38,6 +39,7 @@ namespace Radarr.Api.V3.Qualities
MinSize = model.MinSize,
MaxSize = model.MaxSize,
+ PreferredSize = model.PreferredSize
};
}
@@ -60,6 +62,7 @@ namespace Radarr.Api.V3.Qualities
MinSize = resource.MinSize,
MaxSize = resource.MaxSize,
+ PreferredSize = resource.PreferredSize
};
}
diff --git a/yarn.lock b/yarn.lock
index 7e0f1afaf..347a65ce6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7250,10 +7250,10 @@ react-side-effect@^1.0.2:
dependencies:
shallowequal "^1.0.1"
-react-slider@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/react-slider/-/react-slider-1.0.1.tgz#4cb212d31c35d804805e31f02ce37771e95d5d45"
- integrity sha512-SI2anLzeKlFxnntoM93VXrf3ll9uL/TYjIJB6PsQVp4mDVt2VfWyGtMoUvK/ir/PnjuirNtF1pU3cG9XCezfBA==
+react-slider@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/react-slider/-/react-slider-1.0.3.tgz#89bd9cf0bb87f07ce27d8724efa584d9c6e6dca8"
+ integrity sha512-6HSkzHeyPthoCuQseZ16KQePdj0VloHllXXXmtO1733wGhFa2v1kKjIuGpfHAgWshqB3F7Xn2s5FxTu3xvFXbg==
react-tabs@3.0.0:
version "3.0.0"