Skip to content

Commit c180c32

Browse files
markus101mynameisbogdan
authored andcommittedDec 22, 2023
New: Quality Preferred Size Setting
Co-authored-by: Qstick <qstick@gmail.com> (cherry picked from commit d08f33ae213bb5a94b6b6aa8f6f1e780a7a9835f) Fixed: Include preferred size in quality definition reset (cherry picked from commit 8e925ac76d2f46cf5fef1ea62a20ae5e85d3000e)
1 parent 6d79b5a commit c180c32

File tree

13 files changed

+239
-71
lines changed

13 files changed

+239
-71
lines changed
 

‎frontend/src/Settings/Quality/Definition/QualityDefinition.css

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
background-color: var(--sliderAccentColor);
3232
box-shadow: 0 0 0 #000;
3333

34-
&:nth-child(odd) {
34+
&:nth-child(3n+1) {
3535
background-color: #ddd;
3636
}
3737
}
@@ -56,7 +56,7 @@
5656
.kilobitsPerSecond {
5757
display: flex;
5858
justify-content: space-between;
59-
flex: 0 0 250px;
59+
flex: 0 0 400px;
6060
}
6161

6262
.sizeInput {

‎frontend/src/Settings/Quality/Definition/QualityDefinition.js

+77-18
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,39 @@ class QualityDefinition extends Component {
5050

5151
this.state = {
5252
sliderMinSize: getSliderValue(props.minSize, slider.min),
53-
sliderMaxSize: getSliderValue(props.maxSize, slider.max)
53+
sliderMaxSize: getSliderValue(props.maxSize, slider.max),
54+
sliderPreferredSize: getSliderValue(props.preferredSize, (slider.max - 3))
5455
};
5556
}
5657

5758
//
5859
// Listeners
5960

60-
onSliderChange = ([sliderMinSize, sliderMaxSize]) => {
61+
onSliderChange = ([sliderMinSize, sliderPreferredSize, sliderMaxSize]) => {
6162
this.setState({
6263
sliderMinSize,
63-
sliderMaxSize
64+
sliderMaxSize,
65+
sliderPreferredSize
6466
});
6567

6668
this.props.onSizeChange({
6769
minSize: roundNumber(Math.pow(sliderMinSize, 1.1)),
70+
preferredSize: sliderPreferredSize === (slider.max - 3) ? null : roundNumber(Math.pow(sliderPreferredSize, 1.1)),
6871
maxSize: sliderMaxSize === slider.max ? null : roundNumber(Math.pow(sliderMaxSize, 1.1))
6972
});
7073
};
7174

7275
onAfterSliderChange = () => {
7376
const {
7477
minSize,
75-
maxSize
78+
maxSize,
79+
preferredSize
7680
} = this.props;
7781

7882
this.setState({
7983
sliderMiSize: getSliderValue(minSize, slider.min),
80-
sliderMaxSize: getSliderValue(maxSize, slider.max)
84+
sliderMaxSize: getSliderValue(maxSize, slider.max),
85+
sliderPreferredSize: getSliderValue(preferredSize, (slider.max - 3)) // fix
8186
});
8287
};
8388

@@ -90,7 +95,22 @@ class QualityDefinition extends Component {
9095

9196
this.props.onSizeChange({
9297
minSize,
93-
maxSize: this.props.maxSize
98+
maxSize: this.props.maxSize,
99+
preferredSize: this.props.preferredSize
100+
});
101+
};
102+
103+
onPreferredSizeChange = ({ value }) => {
104+
const preferredSize = value === (MAX - 3) ? null : getValue(value);
105+
106+
this.setState({
107+
sliderPreferredSize: getSliderValue(preferredSize, slider.preferred)
108+
});
109+
110+
this.props.onSizeChange({
111+
minSize: this.props.minSize,
112+
maxSize: this.props.maxSize,
113+
preferredSize
94114
});
95115
};
96116

@@ -103,7 +123,8 @@ class QualityDefinition extends Component {
103123

104124
this.props.onSizeChange({
105125
minSize: this.props.minSize,
106-
maxSize
126+
maxSize,
127+
preferredSize: this.props.preferredSize
107128
});
108129
};
109130

@@ -117,20 +138,25 @@ class QualityDefinition extends Component {
117138
title,
118139
minSize,
119140
maxSize,
141+
preferredSize,
120142
advancedSettings,
121143
onTitleChange
122144
} = this.props;
123145

124146
const {
125147
sliderMinSize,
126-
sliderMaxSize
148+
sliderMaxSize,
149+
sliderPreferredSize
127150
} = this.state;
128151

129152
const minBytes = minSize * 128;
130-
const maxBytes = maxSize && maxSize * 128;
131-
132153
const minRate = `${formatBytes(minBytes, true)}/s`;
133-
const maxRate = maxBytes ? `${formatBytes(maxBytes, true)}/s` : 'Unlimited';
154+
155+
const preferredBytes = preferredSize * 128;
156+
const preferredRate = preferredBytes ? `${formatBytes(preferredBytes, true)}/s` : translate('Unlimited');
157+
158+
const maxBytes = maxSize && maxSize * 128;
159+
const maxRate = maxBytes ? `${formatBytes(maxBytes, true)}/s` : translate('Unlimited');
134160

135161
return (
136162
<div className={styles.qualityDefinition}>
@@ -151,9 +177,10 @@ class QualityDefinition extends Component {
151177
min={slider.min}
152178
max={slider.max}
153179
step={slider.step}
154-
minDistance={MIN_DISTANCE * 5}
155-
value={[sliderMinSize, sliderMaxSize]}
180+
minDistance={3}
181+
value={[sliderMinSize, sliderPreferredSize, sliderMaxSize]}
156182
withTracks={true}
183+
allowCross={false}
157184
snapDragDisabled={true}
158185
className={styles.slider}
159186
trackClassName={styles.bar}
@@ -172,7 +199,23 @@ class QualityDefinition extends Component {
172199
body={
173200
<QualityDefinitionLimits
174201
bytes={minBytes}
175-
message={translate('NoMinimumForAnyRuntime')}
202+
message={translate('NoMinimumForAnyDuration')}
203+
/>
204+
}
205+
position={tooltipPositions.BOTTOM}
206+
/>
207+
</div>
208+
209+
<div>
210+
<Popover
211+
anchor={
212+
<Label kind={kinds.SUCCESS}>{preferredRate}</Label>
213+
}
214+
title={translate('PreferredSize')}
215+
body={
216+
<QualityDefinitionLimits
217+
bytes={preferredBytes}
218+
message={translate('NoLimitForAnyDuration')}
176219
/>
177220
}
178221
position={tooltipPositions.BOTTOM}
@@ -188,7 +231,7 @@ class QualityDefinition extends Component {
188231
body={
189232
<QualityDefinitionLimits
190233
bytes={maxBytes}
191-
message={translate('NoLimitForAnyRuntime')}
234+
message={translate('NoLimitForAnyDuration')}
192235
/>
193236
}
194237
position={tooltipPositions.BOTTOM}
@@ -201,22 +244,37 @@ class QualityDefinition extends Component {
201244
advancedSettings &&
202245
<div className={styles.kilobitsPerSecond}>
203246
<div>
204-
Min
247+
{translate('Min')}
205248

206249
<NumberInput
207250
className={styles.sizeInput}
208251
name={`${id}.min`}
209252
value={minSize || MIN}
210253
min={MIN}
211-
max={maxSize ? maxSize - MIN_DISTANCE : MAX - MIN_DISTANCE}
254+
max={preferredSize ? preferredSize - 5 : MAX - 5}
212255
step={0.1}
213256
isFloat={true}
214257
onChange={this.onMinSizeChange}
215258
/>
216259
</div>
217260

218261
<div>
219-
Max
262+
{translate('Preferred')}
263+
264+
<NumberInput
265+
className={styles.sizeInput}
266+
name={`${id}.min`}
267+
value={preferredSize || MAX - 5}
268+
min={MIN}
269+
max={maxSize ? maxSize - 5 : MAX - 5}
270+
step={0.1}
271+
isFloat={true}
272+
onChange={this.onPreferredSizeChange}
273+
/>
274+
</div>
275+
276+
<div>
277+
{translate('Max')}
220278

221279
<NumberInput
222280
className={styles.sizeInput}
@@ -242,6 +300,7 @@ QualityDefinition.propTypes = {
242300
title: PropTypes.string.isRequired,
243301
minSize: PropTypes.number,
244302
maxSize: PropTypes.number,
303+
preferredSize: PropTypes.number,
245304
advancedSettings: PropTypes.bool.isRequired,
246305
onTitleChange: PropTypes.func.isRequired,
247306
onSizeChange: PropTypes.func.isRequired

‎frontend/src/Settings/Quality/Definition/QualityDefinitionConnector.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ class QualityDefinitionConnector extends Component {
2323
this.props.setQualityDefinitionValue({ id: this.props.id, name: 'title', value });
2424
};
2525

26-
onSizeChange = ({ minSize, maxSize }) => {
26+
onSizeChange = ({ minSize, maxSize, preferredSize }) => {
2727
const {
2828
id,
2929
minSize: currentMinSize,
30-
maxSize: currentMaxSize
30+
maxSize: currentMaxSize,
31+
preferredSize: currentPreferredSize
3132
} = this.props;
3233

3334
if (minSize !== currentMinSize) {
@@ -37,6 +38,10 @@ class QualityDefinitionConnector extends Component {
3738
if (maxSize !== currentMaxSize) {
3839
this.props.setQualityDefinitionValue({ id, name: 'maxSize', value: maxSize });
3940
}
41+
42+
if (preferredSize !== currentPreferredSize) {
43+
this.props.setQualityDefinitionValue({ id, name: 'preferredSize', value: preferredSize });
44+
}
4045
};
4146

4247
//
@@ -57,6 +62,7 @@ QualityDefinitionConnector.propTypes = {
5762
id: PropTypes.number.isRequired,
5863
minSize: PropTypes.number,
5964
maxSize: PropTypes.number,
65+
preferredSize: PropTypes.number,
6066
setQualityDefinitionValue: PropTypes.func.isRequired,
6167
clearPendingChanges: PropTypes.func.isRequired
6268
};

‎src/Lidarr.Api.V1/Qualities/QualityDefinitionResource.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class QualityDefinitionResource : RestResource
1515

1616
public double? MinSize { get; set; }
1717
public double? MaxSize { get; set; }
18+
public double? PreferredSize { get; set; }
1819
}
1920

2021
public static class QualityDefinitionResourceMapper
@@ -33,7 +34,8 @@ public static QualityDefinitionResource ToResource(this QualityDefinition model)
3334
Title = model.Title,
3435
Weight = model.Weight,
3536
MinSize = model.MinSize,
36-
MaxSize = model.MaxSize
37+
MaxSize = model.MaxSize,
38+
PreferredSize = model.PreferredSize
3739
};
3840
}
3941

@@ -51,7 +53,8 @@ public static QualityDefinition ToModel(this QualityDefinitionResource resource)
5153
Title = resource.Title,
5254
Weight = resource.Weight,
5355
MinSize = resource.MinSize,
54-
MaxSize = resource.MaxSize
56+
MaxSize = resource.MaxSize,
57+
PreferredSize = resource.PreferredSize
5558
};
5659
}
5760

‎src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs

+56
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,30 @@ public class PrioritizeDownloadDecisionFixture : CoreTest<DownloadDecisionPriori
2525
public void Setup()
2626
{
2727
GivenPreferredDownloadProtocol(DownloadProtocol.Usenet);
28+
29+
Mocker.GetMock<IQualityDefinitionService>()
30+
.Setup(s => s.Get(It.IsAny<Quality>()))
31+
.Returns(new QualityDefinition { PreferredSize = null });
32+
}
33+
34+
private void GivenPreferredSize(double? size)
35+
{
36+
Mocker.GetMock<IQualityDefinitionService>()
37+
.Setup(s => s.Get(It.IsAny<Quality>()))
38+
.Returns(new QualityDefinition { PreferredSize = size });
2839
}
2940

3041
private Album GivenAlbum(int id)
3142
{
43+
var release = Builder<AlbumRelease>.CreateNew()
44+
.With(e => e.AlbumId = id)
45+
.With(e => e.Monitored = true)
46+
.With(e => e.Duration = 3600000)
47+
.Build();
48+
3249
return Builder<Album>.CreateNew()
3350
.With(e => e.Id = id)
51+
.With(e => e.AlbumReleases = new List<AlbumRelease> { release })
3452
.Build();
3553
}
3654

@@ -130,6 +148,44 @@ public void should_order_by_age_then_largest_rounded_to_200mb()
130148
qualifiedReports.First().RemoteAlbum.Should().Be(remoteAlbumHdLargeYoung);
131149
}
132150

151+
[Test]
152+
public void should_order_by_closest_to_preferred_size_if_both_under()
153+
{
154+
// 1000 Kibit/Sec * 60 Min Duration = 439.5 MiB
155+
GivenPreferredSize(1000);
156+
157+
var remoteAlbumSmall = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_256), size: 120.Megabytes(), age: 1);
158+
var remoteAlbumLarge = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_256), size: 400.Megabytes(), age: 1);
159+
160+
var decisions = new List<DownloadDecision>();
161+
decisions.Add(new DownloadDecision(remoteAlbumSmall));
162+
decisions.Add(new DownloadDecision(remoteAlbumLarge));
163+
164+
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
165+
qualifiedReports.First().RemoteAlbum.Should().Be(remoteAlbumLarge);
166+
}
167+
168+
[Test]
169+
public void should_order_by_closest_to_preferred_size_if_preferred_is_in_between()
170+
{
171+
// 700 Kibit/Sec * 60 Min Duration = 307.6 MiB
172+
GivenPreferredSize(700);
173+
174+
var remoteAlbum1 = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_256), size: 100.Megabytes(), age: 1);
175+
var remoteAlbum2 = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_256), size: 200.Megabytes(), age: 1);
176+
var remoteAlbum3 = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_256), size: 300.Megabytes(), age: 1);
177+
var remoteAlbum4 = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_256), size: 500.Megabytes(), age: 1);
178+
179+
var decisions = new List<DownloadDecision>();
180+
decisions.Add(new DownloadDecision(remoteAlbum1));
181+
decisions.Add(new DownloadDecision(remoteAlbum2));
182+
decisions.Add(new DownloadDecision(remoteAlbum3));
183+
decisions.Add(new DownloadDecision(remoteAlbum4));
184+
185+
var qualifiedReports = Subject.PrioritizeDecisions(decisions);
186+
qualifiedReports.First().RemoteAlbum.Should().Be(remoteAlbum3);
187+
}
188+
133189
[Test]
134190
public void should_order_by_youngest()
135191
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using FluentMigrator;
2+
using NzbDrone.Core.Datastore.Migration.Framework;
3+
4+
namespace NzbDrone.Core.Datastore.Migration
5+
{
6+
[Migration(075)]
7+
public class quality_definition_preferred_size : NzbDroneMigrationBase
8+
{
9+
protected override void MainDbUpgrade()
10+
{
11+
Alter.Table("QualityDefinitions").AddColumn("PreferredSize").AsDouble().Nullable();
12+
13+
Execute.Sql("UPDATE \"QualityDefinitions\" SET \"PreferredSize\" = \"MaxSize\" - 5 WHERE \"MaxSize\" > 5");
14+
}
15+
}
16+
}

‎src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs

+24-3
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ public class DownloadDecisionComparer : IComparer<DownloadDecision>
1313
{
1414
private readonly IConfigService _configService;
1515
private readonly IDelayProfileService _delayProfileService;
16+
private readonly IQualityDefinitionService _qualityDefinitionService;
1617

1718
public delegate int CompareDelegate(DownloadDecision x, DownloadDecision y);
1819
public delegate int CompareDelegate<TSubject, TValue>(DownloadDecision x, DownloadDecision y);
1920

20-
public DownloadDecisionComparer(IConfigService configService, IDelayProfileService delayProfileService)
21+
public DownloadDecisionComparer(IConfigService configService, IDelayProfileService delayProfileService, IQualityDefinitionService qualityDefinitionService)
2122
{
2223
_configService = configService;
2324
_delayProfileService = delayProfileService;
25+
_qualityDefinitionService = qualityDefinitionService;
2426
}
2527

2628
public int Compare(DownloadDecision x, DownloadDecision y)
@@ -166,8 +168,27 @@ private int CompareAgeIfUsenet(DownloadDecision x, DownloadDecision y)
166168

167169
private int CompareSize(DownloadDecision x, DownloadDecision y)
168170
{
169-
// TODO: Is smaller better? Smaller for usenet could mean no par2 files.
170-
return CompareBy(x.RemoteAlbum, y.RemoteAlbum, remoteAlbum => remoteAlbum.Release.Size.Round(200.Megabytes()));
171+
var sizeCompare = CompareBy(x.RemoteAlbum, y.RemoteAlbum, remoteAlbum =>
172+
{
173+
var preferredSize = _qualityDefinitionService.Get(remoteAlbum.ParsedAlbumInfo.Quality.Quality).PreferredSize;
174+
175+
var releaseDuration = remoteAlbum.Albums.Select(a => a.AlbumReleases.Value.Where(r => r.Monitored || a.AnyReleaseOk).Select(r => r.Duration).MaxOrDefault()).Sum() / 1000;
176+
177+
// If no value for preferred it means unlimited so fallback to sort largest is best
178+
if (preferredSize.HasValue && releaseDuration > 0)
179+
{
180+
var preferredAlbumSize = releaseDuration * preferredSize.Value.Kilobits();
181+
182+
// Calculate closest to the preferred size
183+
return Math.Abs((remoteAlbum.Release.Size - preferredAlbumSize).Round(100.Megabytes())) * (-1);
184+
}
185+
else
186+
{
187+
return remoteAlbum.Release.Size.Round(100.Megabytes());
188+
}
189+
});
190+
191+
return sizeCompare;
171192
}
172193
}
173194
}

‎src/NzbDrone.Core/DecisionEngine/DownloadDecisionPriorizationService.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using NzbDrone.Core.Configuration;
44
using NzbDrone.Core.Profiles.Delay;
5+
using NzbDrone.Core.Qualities;
56

67
namespace NzbDrone.Core.DecisionEngine
78
{
@@ -14,19 +15,21 @@ public class DownloadDecisionPriorizationService : IPrioritizeDownloadDecision
1415
{
1516
private readonly IConfigService _configService;
1617
private readonly IDelayProfileService _delayProfileService;
18+
private readonly IQualityDefinitionService _qualityDefinitionService;
1719

18-
public DownloadDecisionPriorizationService(IConfigService configService, IDelayProfileService delayProfileService)
20+
public DownloadDecisionPriorizationService(IConfigService configService, IDelayProfileService delayProfileService, IQualityDefinitionService qualityDefinitionService)
1921
{
2022
_configService = configService;
2123
_delayProfileService = delayProfileService;
24+
_qualityDefinitionService = qualityDefinitionService;
2225
}
2326

2427
public List<DownloadDecision> PrioritizeDecisions(List<DownloadDecision> decisions)
2528
{
2629
return decisions.Where(c => c.RemoteAlbum.DownloadAllowed)
2730
.GroupBy(c => c.RemoteAlbum.Artist.Id, (artistId, downloadDecisions) =>
2831
{
29-
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_configService, _delayProfileService));
32+
return downloadDecisions.OrderByDescending(decision => decision, new DownloadDecisionComparer(_configService, _delayProfileService, _qualityDefinitionService));
3033
})
3134
.SelectMany(c => c)
3235
.Union(decisions.Where(c => !c.RemoteAlbum.DownloadAllowed))

‎src/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCrit
4949
var minSize = qualityDefinition.MinSize.Value.Kilobits();
5050

5151
// Multiply minSize by smallest release duration
52-
minSize = minSize * minReleaseDuration;
52+
minSize *= minReleaseDuration;
5353

5454
// If the parsed size is smaller than minSize we don't want it
5555
if (subject.Release.Size < minSize)
@@ -75,7 +75,7 @@ public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCrit
7575
var maxSize = qualityDefinition.MaxSize.Value.Kilobits();
7676

7777
// Multiply maxSize by Album.Duration
78-
maxSize = maxSize * maxReleaseDuration;
78+
maxSize *= maxReleaseDuration;
7979

8080
// If the parsed size is greater than maxSize we don't want it
8181
if (subject.Release.Size > maxSize)

‎src/NzbDrone.Core/Localization/Core/en.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -680,9 +680,9 @@
680680
"NoImportListsFound": "No import lists found",
681681
"NoIndexersFound": "No indexers found",
682682
"NoLeaveIt": "No, Leave It",
683-
"NoLimitForAnyRuntime": "No limit for any runtime",
683+
"NoLimitForAnyDuration": "No limit for any duration",
684684
"NoLogFiles": "No log files",
685-
"NoMinimumForAnyRuntime": "No minimum for any runtime",
685+
"NoMinimumForAnyDuration": "No minimum for any duration",
686686
"NoMissingItems": "No missing items",
687687
"NoResultsFound": "No results found",
688688
"NoTagsHaveBeenAddedYet": "No tags have been added yet",
@@ -754,6 +754,7 @@
754754
"PreferTorrent": "Prefer Torrent",
755755
"PreferUsenet": "Prefer Usenet",
756756
"PreferredProtocol": "Preferred Protocol",
757+
"PreferredSize": "Preferred Size",
757758
"Presets": "Presets",
758759
"PreviewRename": "Preview Rename",
759760
"PreviewRetag": "Preview Retag",
@@ -1116,6 +1117,7 @@
11161117
"UnableToLoadTheCalendar": "Unable to load the calendar",
11171118
"UnableToLoadUISettings": "Unable to load UI settings",
11181119
"Ungroup": "Ungroup",
1120+
"Unlimited": "Unlimited",
11191121
"UnmappedFiles": "Unmapped Files",
11201122
"UnmappedFilesOnly": "Unmapped Files Only",
11211123
"Unmonitored": "Unmonitored",

‎src/NzbDrone.Core/Qualities/Quality.cs

+38-38
Original file line numberDiff line numberDiff line change
@@ -161,44 +161,44 @@ static Quality()
161161

162162
DefaultQualityDefinitions = new HashSet<QualityDefinition>
163163
{
164-
new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 350, GroupWeight = 1 },
165-
new QualityDefinition(Quality.MP3_008) { Weight = 2, MinSize = 0, MaxSize = 10, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
166-
new QualityDefinition(Quality.MP3_016) { Weight = 3, MinSize = 0, MaxSize = 20, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
167-
new QualityDefinition(Quality.MP3_024) { Weight = 4, MinSize = 0, MaxSize = 30, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
168-
new QualityDefinition(Quality.MP3_032) { Weight = 5, MinSize = 0, MaxSize = 40, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
169-
new QualityDefinition(Quality.MP3_040) { Weight = 6, MinSize = 0, MaxSize = 45, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
170-
new QualityDefinition(Quality.MP3_048) { Weight = 7, MinSize = 0, MaxSize = 55, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
171-
new QualityDefinition(Quality.MP3_056) { Weight = 8, MinSize = 0, MaxSize = 65, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
172-
new QualityDefinition(Quality.MP3_064) { Weight = 9, MinSize = 0, MaxSize = 75, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
173-
new QualityDefinition(Quality.MP3_080) { Weight = 10, MinSize = 0, MaxSize = 95, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
174-
new QualityDefinition(Quality.MP3_096) { Weight = 11, MinSize = 0, MaxSize = 110, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
175-
new QualityDefinition(Quality.MP3_112) { Weight = 12, MinSize = 0, MaxSize = 125, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
176-
new QualityDefinition(Quality.MP3_128) { Weight = 13, MinSize = 0, MaxSize = 140, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
177-
new QualityDefinition(Quality.VORBIS_Q5) { Weight = 14, MinSize = 0, MaxSize = 175, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
178-
new QualityDefinition(Quality.MP3_160) { Weight = 14, MinSize = 0, MaxSize = 175, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
179-
new QualityDefinition(Quality.MP3_192) { Weight = 15, MinSize = 0, MaxSize = 210, GroupName = "Low Quality Lossy", GroupWeight = 4 },
180-
new QualityDefinition(Quality.VORBIS_Q6) { Weight = 15, MinSize = 0, MaxSize = 210, GroupName = "Low Quality Lossy", GroupWeight = 4 },
181-
new QualityDefinition(Quality.AAC_192) { Weight = 15, MinSize = 0, MaxSize = 210, GroupName = "Low Quality Lossy", GroupWeight = 4 },
182-
new QualityDefinition(Quality.WMA) { Weight = 15, MinSize = 0, MaxSize = 350, GroupName = "Low Quality Lossy", GroupWeight = 4 },
183-
new QualityDefinition(Quality.MP3_224) { Weight = 16, MinSize = 0, MaxSize = 245, GroupName = "Low Quality Lossy", GroupWeight = 4 },
184-
new QualityDefinition(Quality.VORBIS_Q7) { Weight = 17, MinSize = 0, MaxSize = 245, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
185-
new QualityDefinition(Quality.MP3_VBR_V2) { Weight = 18, MinSize = 0, MaxSize = 280, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
186-
new QualityDefinition(Quality.MP3_256) { Weight = 18, MinSize = 0, MaxSize = 280, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
187-
new QualityDefinition(Quality.VORBIS_Q8) { Weight = 18, MinSize = 0, MaxSize = 280, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
188-
new QualityDefinition(Quality.AAC_256) { Weight = 18, MinSize = 0, MaxSize = 280, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
189-
new QualityDefinition(Quality.MP3_VBR) { Weight = 19, MinSize = 0, MaxSize = 350, GroupName = "High Quality Lossy", GroupWeight = 6 },
190-
new QualityDefinition(Quality.AAC_VBR) { Weight = 19, MinSize = 0, MaxSize = 350, GroupName = "High Quality Lossy", GroupWeight = 6 },
191-
new QualityDefinition(Quality.MP3_320) { Weight = 20, MinSize = 0, MaxSize = 350, GroupName = "High Quality Lossy", GroupWeight = 6 },
192-
new QualityDefinition(Quality.VORBIS_Q9) { Weight = 20, MinSize = 0, MaxSize = 350, GroupName = "High Quality Lossy", GroupWeight = 6 },
193-
new QualityDefinition(Quality.AAC_320) { Weight = 20, MinSize = 0, MaxSize = 350, GroupName = "High Quality Lossy", GroupWeight = 6 },
194-
new QualityDefinition(Quality.VORBIS_Q10) { Weight = 21, MinSize = 0, MaxSize = 550, GroupName = "High Quality Lossy", GroupWeight = 6 },
195-
new QualityDefinition(Quality.FLAC) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
196-
new QualityDefinition(Quality.ALAC) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
197-
new QualityDefinition(Quality.APE) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
198-
new QualityDefinition(Quality.WAVPACK) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
199-
new QualityDefinition(Quality.FLAC_24) { Weight = 23, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
200-
new QualityDefinition(Quality.ALAC_24) { Weight = 23, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
201-
new QualityDefinition(Quality.WAV) { Weight = 24, MinSize = 0, MaxSize = null, GroupWeight = 8 }
164+
new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 350, PreferredSize = 195, GroupWeight = 1 },
165+
new QualityDefinition(Quality.MP3_008) { Weight = 2, MinSize = 0, MaxSize = 10, PreferredSize = 5, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
166+
new QualityDefinition(Quality.MP3_016) { Weight = 3, MinSize = 0, MaxSize = 20, PreferredSize = 15, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
167+
new QualityDefinition(Quality.MP3_024) { Weight = 4, MinSize = 0, MaxSize = 30, PreferredSize = 25, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
168+
new QualityDefinition(Quality.MP3_032) { Weight = 5, MinSize = 0, MaxSize = 40, PreferredSize = 35, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
169+
new QualityDefinition(Quality.MP3_040) { Weight = 6, MinSize = 0, MaxSize = 45, PreferredSize = 40, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
170+
new QualityDefinition(Quality.MP3_048) { Weight = 7, MinSize = 0, MaxSize = 55, PreferredSize = 50, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
171+
new QualityDefinition(Quality.MP3_056) { Weight = 8, MinSize = 0, MaxSize = 65, PreferredSize = 60, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
172+
new QualityDefinition(Quality.MP3_064) { Weight = 9, MinSize = 0, MaxSize = 75, PreferredSize = 70, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
173+
new QualityDefinition(Quality.MP3_080) { Weight = 10, MinSize = 0, MaxSize = 95, PreferredSize = 90, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
174+
new QualityDefinition(Quality.MP3_096) { Weight = 11, MinSize = 0, MaxSize = 110, PreferredSize = 95, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
175+
new QualityDefinition(Quality.MP3_112) { Weight = 12, MinSize = 0, MaxSize = 125, PreferredSize = 95, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
176+
new QualityDefinition(Quality.MP3_128) { Weight = 13, MinSize = 0, MaxSize = 140, PreferredSize = 95, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
177+
new QualityDefinition(Quality.VORBIS_Q5) { Weight = 14, MinSize = 0, MaxSize = 175, PreferredSize = 95, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
178+
new QualityDefinition(Quality.MP3_160) { Weight = 14, MinSize = 0, MaxSize = 175, PreferredSize = 95, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
179+
new QualityDefinition(Quality.MP3_192) { Weight = 15, MinSize = 0, MaxSize = 210, PreferredSize = 95, GroupName = "Low Quality Lossy", GroupWeight = 4 },
180+
new QualityDefinition(Quality.VORBIS_Q6) { Weight = 15, MinSize = 0, MaxSize = 210, PreferredSize = 95, GroupName = "Low Quality Lossy", GroupWeight = 4 },
181+
new QualityDefinition(Quality.AAC_192) { Weight = 15, MinSize = 0, MaxSize = 210, PreferredSize = 95, GroupName = "Low Quality Lossy", GroupWeight = 4 },
182+
new QualityDefinition(Quality.WMA) { Weight = 15, MinSize = 0, MaxSize = 350, PreferredSize = 195, GroupName = "Low Quality Lossy", GroupWeight = 4 },
183+
new QualityDefinition(Quality.MP3_224) { Weight = 16, MinSize = 0, MaxSize = 245, PreferredSize = 95, GroupName = "Low Quality Lossy", GroupWeight = 4 },
184+
new QualityDefinition(Quality.VORBIS_Q7) { Weight = 17, MinSize = 0, MaxSize = 245, PreferredSize = 95, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
185+
new QualityDefinition(Quality.MP3_VBR_V2) { Weight = 18, MinSize = 0, MaxSize = 280, PreferredSize = 95, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
186+
new QualityDefinition(Quality.MP3_256) { Weight = 18, MinSize = 0, MaxSize = 280, PreferredSize = 95, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
187+
new QualityDefinition(Quality.VORBIS_Q8) { Weight = 18, MinSize = 0, MaxSize = 280, PreferredSize = 95, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
188+
new QualityDefinition(Quality.AAC_256) { Weight = 18, MinSize = 0, MaxSize = 280, PreferredSize = 95, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
189+
new QualityDefinition(Quality.MP3_VBR) { Weight = 19, MinSize = 0, MaxSize = 350, PreferredSize = 195, GroupName = "High Quality Lossy", GroupWeight = 6 },
190+
new QualityDefinition(Quality.AAC_VBR) { Weight = 19, MinSize = 0, MaxSize = 350, PreferredSize = 195, GroupName = "High Quality Lossy", GroupWeight = 6 },
191+
new QualityDefinition(Quality.MP3_320) { Weight = 20, MinSize = 0, MaxSize = 350, PreferredSize = 195, GroupName = "High Quality Lossy", GroupWeight = 6 },
192+
new QualityDefinition(Quality.VORBIS_Q9) { Weight = 20, MinSize = 0, MaxSize = 350, PreferredSize = 195, GroupName = "High Quality Lossy", GroupWeight = 6 },
193+
new QualityDefinition(Quality.AAC_320) { Weight = 20, MinSize = 0, MaxSize = 350, PreferredSize = 195, GroupName = "High Quality Lossy", GroupWeight = 6 },
194+
new QualityDefinition(Quality.VORBIS_Q10) { Weight = 21, MinSize = 0, MaxSize = 550, PreferredSize = 295, GroupName = "High Quality Lossy", GroupWeight = 6 },
195+
new QualityDefinition(Quality.FLAC) { Weight = 22, MinSize = 0, MaxSize = null, PreferredSize = 895, GroupName = "Lossless", GroupWeight = 7 },
196+
new QualityDefinition(Quality.ALAC) { Weight = 22, MinSize = 0, MaxSize = null, PreferredSize = 895, GroupName = "Lossless", GroupWeight = 7 },
197+
new QualityDefinition(Quality.APE) { Weight = 22, MinSize = 0, MaxSize = null, PreferredSize = 895, GroupName = "Lossless", GroupWeight = 7 },
198+
new QualityDefinition(Quality.WAVPACK) { Weight = 22, MinSize = 0, MaxSize = null, PreferredSize = 895, GroupName = "Lossless", GroupWeight = 7 },
199+
new QualityDefinition(Quality.FLAC_24) { Weight = 23, MinSize = 0, MaxSize = null, PreferredSize = 895, GroupName = "Lossless", GroupWeight = 7 },
200+
new QualityDefinition(Quality.ALAC_24) { Weight = 23, MinSize = 0, MaxSize = null, PreferredSize = 895, GroupName = "Lossless", GroupWeight = 7 },
201+
new QualityDefinition(Quality.WAV) { Weight = 24, MinSize = 0, MaxSize = null, PreferredSize = 895, GroupWeight = 8 }
202202
};
203203
}
204204

‎src/NzbDrone.Core/Qualities/QualityDefinition.cs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class QualityDefinition : ModelBase
1414

1515
public double? MinSize { get; set; }
1616
public double? MaxSize { get; set; }
17+
public double? PreferredSize { get; set; }
1718

1819
public QualityDefinition()
1920
{

‎src/NzbDrone.Core/Qualities/QualityDefinitionService.cs

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public void Execute(ResetQualityDefinitionsCommand message)
121121

122122
existing.MinSize = definition.MinSize;
123123
existing.MaxSize = definition.MaxSize;
124+
existing.PreferredSize = definition.PreferredSize;
124125
existing.Title = message.ResetTitles ? definition.Title : existing.Title;
125126

126127
updateList.Add(existing);

0 commit comments

Comments
 (0)
Please sign in to comment.