From 3d3c6f327d0807e426645d6cd40e4cf4b12bc98b Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Wed, 5 May 2021 16:20:16 -0500 Subject: [PATCH] fix(sonarr): quality definition unlimited support Also added some code sharing between Radarr and Sonarr for this stuff, since they are largely similar (Radarr has preferred size but the rest is the same). --- .../RadarrQualityDataTest.cs | 86 ++--------------- .../SonarrQualityDataTest.cs | 94 +++++++++++++++++++ .../QualityDefinition/RadarrQualityData.cs | 33 +------ .../Objects/SonarrQualityDefinitionItem.cs | 2 +- .../QualityDefinition/SonarrQualityData.cs | 32 ++++++- .../SonarrQualityDefinitionUpdater.cs | 20 ++-- 6 files changed, 144 insertions(+), 123 deletions(-) create mode 100644 src/Trash.Tests/Sonarr/QualityDefinition/SonarrQualityDataTest.cs diff --git a/src/Trash.Tests/Radarr/QualityDefinition/RadarrQualityDataTest.cs b/src/Trash.Tests/Radarr/QualityDefinition/RadarrQualityDataTest.cs index 3d6b70b4..966d383b 100644 --- a/src/Trash.Tests/Radarr/QualityDefinition/RadarrQualityDataTest.cs +++ b/src/Trash.Tests/Radarr/QualityDefinition/RadarrQualityDataTest.cs @@ -1,6 +1,7 @@ using FluentAssertions; using NUnit.Framework; using Trash.Radarr.QualityDefinition; +using Trash.Sonarr.QualityDefinition; namespace Trash.Tests.Radarr.QualityDefinition { @@ -10,12 +11,12 @@ namespace Trash.Tests.Radarr.QualityDefinition { private static readonly object[] ToleranceTestValues = { - new object[] {-RadarrQualityData.Tolerance - 0.01m, true}, - new object[] {-RadarrQualityData.Tolerance, false}, - new object[] {-RadarrQualityData.Tolerance / 2, false}, - new object[] {RadarrQualityData.Tolerance / 2, false}, - new object[] {RadarrQualityData.Tolerance, false}, - new object[] {RadarrQualityData.Tolerance + 0.01m, true} + new object[] {-SonarrQualityData.Tolerance - 0.01m, true}, + new object[] {-SonarrQualityData.Tolerance, false}, + new object[] {-SonarrQualityData.Tolerance / 2, false}, + new object[] {SonarrQualityData.Tolerance / 2, false}, + new object[] {SonarrQualityData.Tolerance, false}, + new object[] {SonarrQualityData.Tolerance + 0.01m, true} }; [TestCaseSource(nameof(ToleranceTestValues))] @@ -28,24 +29,6 @@ namespace Trash.Tests.Radarr.QualityDefinition .Should().Be(expectedResult); } - [TestCaseSource(nameof(ToleranceTestValues))] - public void MaxOutsideTolerance_WithVariousTolerance_ReturnsExpectedResult(decimal offset, bool expectedResult) - { - const decimal testVal = 100; - var data = new RadarrQualityData {Max = testVal}; - data.MaxOutsideTolerance(testVal + offset) - .Should().Be(expectedResult); - } - - [TestCaseSource(nameof(ToleranceTestValues))] - public void MinOutsideTolerance_WithVariousTolerance_ReturnsExpectedResult(decimal offset, bool expectedResult) - { - const decimal testVal = 0; - var data = new RadarrQualityData {Min = testVal}; - data.MinOutsideTolerance(testVal + offset) - .Should().Be(expectedResult); - } - private static readonly object[] InterpolatedPreferredTestParams = { new[] @@ -88,30 +71,6 @@ namespace Trash.Tests.Radarr.QualityDefinition data.InterpolatedPreferred(ratio).Should().Be(expectedResult); } - [Test] - public void AnnotatedMax_OutsideThreshold_EqualsSameValueWithUnlimited() - { - const decimal testVal = RadarrQualityData.MaxUnlimitedThreshold; - var data = new RadarrQualityData {Max = testVal}; - data.AnnotatedMax.Should().Be($"{testVal} (Unlimited)"); - } - - [Test] - public void AnnotatedMax_WithinThreshold_EqualsSameStringValue() - { - const decimal testVal = RadarrQualityData.MaxUnlimitedThreshold - 1; - var data = new RadarrQualityData {Max = testVal}; - data.AnnotatedMax.Should().Be($"{testVal}"); - } - - [Test] - public void AnnotatedMin_NoThreshold_EqualsSameValue() - { - const decimal testVal = 10m; - var data = new RadarrQualityData {Max = testVal}; - data.AnnotatedMax.Should().Be($"{testVal}"); - } - [Test] public void AnnotatedPreferred_OutsideThreshold_EqualsSameValueWithUnlimited() { @@ -128,37 +87,6 @@ namespace Trash.Tests.Radarr.QualityDefinition data.AnnotatedPreferred.Should().Be($"{testVal}"); } - [Test] - public void Max_AboveThreshold_EqualsSameValue() - { - const decimal testVal = RadarrQualityData.MaxUnlimitedThreshold + 1; - var data = new RadarrQualityData {Max = testVal}; - data.Max.Should().Be(testVal); - } - - [Test] - public void MaxForApi_AboveThreshold_EqualsNull() - { - const decimal testVal = RadarrQualityData.MaxUnlimitedThreshold + 1; - var data = new RadarrQualityData {Max = testVal}; - data.MaxForApi.Should().Be(null); - } - - [Test] - public void MaxForApi_HighestWithinThreshold_EqualsSameValue() - { - const decimal testVal = RadarrQualityData.MaxUnlimitedThreshold - 0.1m; - var data = new RadarrQualityData {Max = testVal}; - data.MaxForApi.Should().Be(testVal).And.Be(data.Max); - } - - [Test] - public void MaxForApi_LowestWithinThreshold_EqualsSameValue() - { - var data = new RadarrQualityData {Max = 0}; - data.MaxForApi.Should().Be(0); - } - [Test] public void Preferred_AboveThreshold_EqualsSameValue() { diff --git a/src/Trash.Tests/Sonarr/QualityDefinition/SonarrQualityDataTest.cs b/src/Trash.Tests/Sonarr/QualityDefinition/SonarrQualityDataTest.cs new file mode 100644 index 00000000..63cb4a57 --- /dev/null +++ b/src/Trash.Tests/Sonarr/QualityDefinition/SonarrQualityDataTest.cs @@ -0,0 +1,94 @@ +using FluentAssertions; +using NUnit.Framework; +using Trash.Sonarr.QualityDefinition; + +namespace Trash.Tests.Sonarr.QualityDefinition +{ + [TestFixture] + [Parallelizable(ParallelScope.All)] + public class SonarrQualityDataTest + { + private static readonly object[] ToleranceTestValues = + { + new object[] {-SonarrQualityData.Tolerance - 0.01m, true}, + new object[] {-SonarrQualityData.Tolerance, false}, + new object[] {-SonarrQualityData.Tolerance / 2, false}, + new object[] {SonarrQualityData.Tolerance / 2, false}, + new object[] {SonarrQualityData.Tolerance, false}, + new object[] {SonarrQualityData.Tolerance + 0.01m, true} + }; + + [TestCaseSource(nameof(ToleranceTestValues))] + public void MaxOutsideTolerance_WithVariousTolerance_ReturnsExpectedResult(decimal offset, bool expectedResult) + { + const decimal testVal = 100; + var data = new SonarrQualityData {Max = testVal}; + data.MaxOutsideTolerance(testVal + offset) + .Should().Be(expectedResult); + } + + [TestCaseSource(nameof(ToleranceTestValues))] + public void MinOutsideTolerance_WithVariousTolerance_ReturnsExpectedResult(decimal offset, bool expectedResult) + { + const decimal testVal = 0; + var data = new SonarrQualityData {Min = testVal}; + data.MinOutsideTolerance(testVal + offset) + .Should().Be(expectedResult); + } + + [Test] + public void AnnotatedMax_OutsideThreshold_EqualsSameValueWithUnlimited() + { + const decimal testVal = SonarrQualityData.MaxUnlimitedThreshold; + var data = new SonarrQualityData {Max = testVal}; + data.AnnotatedMax.Should().Be($"{testVal} (Unlimited)"); + } + + [Test] + public void AnnotatedMax_WithinThreshold_EqualsSameStringValue() + { + const decimal testVal = SonarrQualityData.MaxUnlimitedThreshold - 1; + var data = new SonarrQualityData {Max = testVal}; + data.AnnotatedMax.Should().Be($"{testVal}"); + } + + [Test] + public void AnnotatedMin_NoThreshold_EqualsSameValue() + { + const decimal testVal = 10m; + var data = new SonarrQualityData {Max = testVal}; + data.AnnotatedMax.Should().Be($"{testVal}"); + } + + [Test] + public void Max_AboveThreshold_EqualsSameValue() + { + const decimal testVal = SonarrQualityData.MaxUnlimitedThreshold + 1; + var data = new SonarrQualityData {Max = testVal}; + data.Max.Should().Be(testVal); + } + + [Test] + public void MaxForApi_AboveThreshold_EqualsNull() + { + const decimal testVal = SonarrQualityData.MaxUnlimitedThreshold + 1; + var data = new SonarrQualityData {Max = testVal}; + data.MaxForApi.Should().Be(null); + } + + [Test] + public void MaxForApi_HighestWithinThreshold_EqualsSameValue() + { + const decimal testVal = SonarrQualityData.MaxUnlimitedThreshold - 0.1m; + var data = new SonarrQualityData {Max = testVal}; + data.MaxForApi.Should().Be(testVal).And.Be(data.Max); + } + + [Test] + public void MaxForApi_LowestWithinThreshold_EqualsSameValue() + { + var data = new SonarrQualityData {Max = 0}; + data.MaxForApi.Should().Be(0); + } + } +} diff --git a/src/Trash/Radarr/QualityDefinition/RadarrQualityData.cs b/src/Trash/Radarr/QualityDefinition/RadarrQualityData.cs index 6fa1d075..c3dcbbe6 100644 --- a/src/Trash/Radarr/QualityDefinition/RadarrQualityData.cs +++ b/src/Trash/Radarr/QualityDefinition/RadarrQualityData.cs @@ -1,26 +1,14 @@ using System; -using System.Globalization; -using System.Text; +using Trash.Sonarr.QualityDefinition; namespace Trash.Radarr.QualityDefinition { - public class RadarrQualityData + public class RadarrQualityData : SonarrQualityData { - public const decimal Tolerance = 0.1m; - public const decimal MaxUnlimitedThreshold = 400; public const decimal PreferredUnlimitedThreshold = 395; - public string Name { get; set; } = ""; - public decimal Min { get; set; } - public decimal Max { get; set; } public decimal Preferred { get; set; } - - public decimal? MaxForApi => Max < MaxUnlimitedThreshold ? Max : null; - public decimal MinForApi => Min; public decimal? PreferredForApi => Preferred < PreferredUnlimitedThreshold ? Preferred : null; - - public string AnnotatedMin => Min.ToString(CultureInfo.InvariantCulture); - public string AnnotatedMax => AnnotatedValue(Max, MaxUnlimitedThreshold); public string AnnotatedPreferred => AnnotatedValue(Preferred, PreferredUnlimitedThreshold); public decimal InterpolatedPreferred(decimal ratio) @@ -29,23 +17,6 @@ namespace Trash.Radarr.QualityDefinition return Math.Round(Min + (cappedMax - Min) * ratio, 1); } - private static string AnnotatedValue(decimal value, decimal threshold) - { - var builder = new StringBuilder(value.ToString(CultureInfo.InvariantCulture)); - if (value >= threshold) - { - builder.Append(" (Unlimited)"); - } - - return builder.ToString(); - } - - public bool MinOutsideTolerance(decimal other) => - Math.Abs(other - Min) > Tolerance; - - public bool MaxOutsideTolerance(decimal? other) => - Math.Abs((other ?? MaxUnlimitedThreshold) - Max) > Tolerance; - public bool PreferredOutsideTolerance(decimal? other) => Math.Abs((other ?? PreferredUnlimitedThreshold) - Preferred) > Tolerance; } diff --git a/src/Trash/Sonarr/Api/Objects/SonarrQualityDefinitionItem.cs b/src/Trash/Sonarr/Api/Objects/SonarrQualityDefinitionItem.cs index e2153651..c25f970c 100644 --- a/src/Trash/Sonarr/Api/Objects/SonarrQualityDefinitionItem.cs +++ b/src/Trash/Sonarr/Api/Objects/SonarrQualityDefinitionItem.cs @@ -19,6 +19,6 @@ namespace Trash.Sonarr.Api.Objects public string Title { get; set; } = ""; public int Weight { get; set; } public decimal MinSize { get; set; } - public decimal MaxSize { get; set; } + public decimal? MaxSize { get; set; } } } diff --git a/src/Trash/Sonarr/QualityDefinition/SonarrQualityData.cs b/src/Trash/Sonarr/QualityDefinition/SonarrQualityData.cs index 4fe7a8d1..84fa3a05 100644 --- a/src/Trash/Sonarr/QualityDefinition/SonarrQualityData.cs +++ b/src/Trash/Sonarr/QualityDefinition/SonarrQualityData.cs @@ -1,9 +1,39 @@ -namespace Trash.Sonarr.QualityDefinition +using System; +using System.Globalization; +using System.Text; + +namespace Trash.Sonarr.QualityDefinition { public class SonarrQualityData { + public const decimal Tolerance = 0.1m; + public const decimal MaxUnlimitedThreshold = 400; + public string Name { get; set; } = ""; public decimal Min { get; set; } public decimal Max { get; set; } + + public decimal? MaxForApi => Max < MaxUnlimitedThreshold ? Max : null; + public decimal MinForApi => Min; + + public string AnnotatedMin => Min.ToString(CultureInfo.InvariantCulture); + public string AnnotatedMax => AnnotatedValue(Max, MaxUnlimitedThreshold); + + protected static string AnnotatedValue(decimal value, decimal threshold) + { + var builder = new StringBuilder(value.ToString(CultureInfo.InvariantCulture)); + if (value >= threshold) + { + builder.Append(" (Unlimited)"); + } + + return builder.ToString(); + } + + public bool MinOutsideTolerance(decimal other) => + Math.Abs(other - Min) > Tolerance; + + public bool MaxOutsideTolerance(decimal? other) => + Math.Abs((other ?? MaxUnlimitedThreshold) - Max) > Tolerance; } } diff --git a/src/Trash/Sonarr/QualityDefinition/SonarrQualityDefinitionUpdater.cs b/src/Trash/Sonarr/QualityDefinition/SonarrQualityDefinitionUpdater.cs index 5f634b4c..33ae52f6 100644 --- a/src/Trash/Sonarr/QualityDefinition/SonarrQualityDefinitionUpdater.cs +++ b/src/Trash/Sonarr/QualityDefinition/SonarrQualityDefinitionUpdater.cs @@ -68,13 +68,13 @@ namespace Trash.Sonarr.QualityDefinition private static void PrintQualityPreview(IEnumerable quality) { Console.WriteLine(""); - const string format = "{0,-20} {1,-10} {2,-10}"; + const string format = "{0,-20} {1,-10} {2,-15}"; Console.WriteLine(format, "Quality", "Min", "Max"); Console.WriteLine(format, "-------", "---", "---"); foreach (var q in quality) { - Console.WriteLine(format, q.Name, q.Min, q.Max); + Console.WriteLine(format, q.Name, q.AnnotatedMin, q.AnnotatedMax); } Console.WriteLine(""); @@ -116,13 +116,10 @@ namespace Trash.Sonarr.QualityDefinition { static bool QualityIsDifferent(SonarrQualityDefinitionItem a, SonarrQualityData b) { - const decimal tolerance = 0.1m; - return - Math.Abs(a.MaxSize - b.Max) > tolerance || - Math.Abs(a.MinSize - b.Min) > tolerance; + return b.MinOutsideTolerance(a.MinSize) || + b.MaxOutsideTolerance(a.MaxSize); } - // var newQuality = serverQuality.Where(q => guideQuality.Any(gq => gq.Name == q.Quality.Name)); var newQuality = new List(); foreach (var qualityData in guideQuality) { @@ -139,12 +136,13 @@ namespace Trash.Sonarr.QualityDefinition } // Not using the original list again, so it's OK to modify the definition reftype objects in-place. - entry.MinSize = qualityData.Min; - entry.MaxSize = qualityData.Max; + entry.MinSize = qualityData.MinForApi; + entry.MaxSize = qualityData.MaxForApi; newQuality.Add(entry); - Log.Debug("Setting Quality [Name: {Name}] [Min: {Min}] [Max: {Max}]", - entry.Quality?.Name, entry.MinSize, entry.MaxSize); + Log.Debug("Setting Quality " + + "[Name: {Name}] [Source: {Source}] [Min: {Min}] [Max: {Max}]", + entry.Quality?.Name, entry.Quality?.Source, entry.MinSize, entry.MaxSize); } await _api.UpdateQualityDefinition(newQuality);