diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e58e2de..2aeb0044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Quality definition data is now pulled from JSON files. + ## [2.3.1] - 2022-08-20 ### Changed diff --git a/src/Recyclarr/Command/SonarrCommand.cs b/src/Recyclarr/Command/SonarrCommand.cs index ac2756a8..72947282 100644 --- a/src/Recyclarr/Command/SonarrCommand.cs +++ b/src/Recyclarr/Command/SonarrCommand.cs @@ -68,7 +68,7 @@ public class SonarrCommand : ServiceCommand await profileUpdaterFactory().Process(Preview, config); } - if (config.QualityDefinition.HasValue) + if (!string.IsNullOrEmpty(config.QualityDefinition)) { await qualityUpdaterFactory().Process(Preview, config); } diff --git a/src/TrashLib.Tests/Radarr/QualityDefinition/RadarrQualityDataTest.cs b/src/TrashLib.Tests/Radarr/QualityDefinition/RadarrQualityDataTest.cs index 3ddda70f..0989ed29 100644 --- a/src/TrashLib.Tests/Radarr/QualityDefinition/RadarrQualityDataTest.cs +++ b/src/TrashLib.Tests/Radarr/QualityDefinition/RadarrQualityDataTest.cs @@ -14,17 +14,17 @@ public class RadarrQualityDataTest new object?[] {100m, 101m, true}, new object?[] {100m, 98m, true}, new object?[] {100m, null, true}, - new object?[] {RadarrQualityData.PreferredUnlimitedThreshold, null, false}, - new object?[] {RadarrQualityData.PreferredUnlimitedThreshold - 1, null, true}, + new object?[] {RadarrQualityItem.PreferredUnlimitedThreshold, null, false}, + new object?[] {RadarrQualityItem.PreferredUnlimitedThreshold - 1, null, true}, new object?[] - {RadarrQualityData.PreferredUnlimitedThreshold, RadarrQualityData.PreferredUnlimitedThreshold, true} + {RadarrQualityItem.PreferredUnlimitedThreshold, RadarrQualityItem.PreferredUnlimitedThreshold, true} }; [TestCaseSource(nameof(PreferredTestValues))] public void PreferredDifferent_WithVariousValues_ReturnsExpectedResult(decimal guideValue, decimal? radarrValue, bool isDifferent) { - var data = new RadarrQualityData {Preferred = guideValue}; + var data = new RadarrQualityItem("", 0, 0, guideValue); data.IsPreferredDifferent(radarrValue) .Should().Be(isDifferent); } @@ -35,19 +35,19 @@ public class RadarrQualityDataTest { 400m, 1.0m, - RadarrQualityData.PreferredUnlimitedThreshold + RadarrQualityItem.PreferredUnlimitedThreshold }, new[] { - RadarrQualityData.PreferredUnlimitedThreshold, + RadarrQualityItem.PreferredUnlimitedThreshold, 1.0m, - RadarrQualityData.PreferredUnlimitedThreshold + RadarrQualityItem.PreferredUnlimitedThreshold }, new[] { - RadarrQualityData.PreferredUnlimitedThreshold - 1m, + RadarrQualityItem.PreferredUnlimitedThreshold - 1m, 1.0m, - RadarrQualityData.PreferredUnlimitedThreshold - 1m + RadarrQualityItem.PreferredUnlimitedThreshold - 1m }, new[] { @@ -67,54 +67,54 @@ public class RadarrQualityDataTest public void InterpolatedPreferred_VariousValues_ExpectedResults(decimal max, decimal ratio, decimal expectedResult) { - var data = new RadarrQualityData {Min = 0, Max = max}; + var data = new RadarrQualityItem("", 0, max, 0); data.InterpolatedPreferred(ratio).Should().Be(expectedResult); } [Test] public void AnnotatedPreferred_OutsideThreshold_EqualsSameValueWithUnlimited() { - const decimal testVal = RadarrQualityData.PreferredUnlimitedThreshold; - var data = new RadarrQualityData {Preferred = testVal}; + const decimal testVal = RadarrQualityItem.PreferredUnlimitedThreshold; + var data = new RadarrQualityItem("", 0, 0, testVal); data.AnnotatedPreferred.Should().Be($"{testVal} (Unlimited)"); } [Test] public void AnnotatedPreferred_WithinThreshold_EqualsSameStringValue() { - const decimal testVal = RadarrQualityData.PreferredUnlimitedThreshold - 1; - var data = new RadarrQualityData {Preferred = testVal}; + const decimal testVal = RadarrQualityItem.PreferredUnlimitedThreshold - 1; + var data = new RadarrQualityItem("", 0, 0, testVal); data.AnnotatedPreferred.Should().Be($"{testVal}"); } [Test] public void Preferred_AboveThreshold_EqualsSameValue() { - const decimal testVal = RadarrQualityData.PreferredUnlimitedThreshold + 1; - var data = new RadarrQualityData {Preferred = testVal}; + const decimal testVal = RadarrQualityItem.PreferredUnlimitedThreshold + 1; + var data = new RadarrQualityItem("", 0, 0, testVal); data.Preferred.Should().Be(testVal); } [Test] public void PreferredForApi_AboveThreshold_EqualsNull() { - const decimal testVal = RadarrQualityData.PreferredUnlimitedThreshold + 1; - var data = new RadarrQualityData {Preferred = testVal}; + const decimal testVal = RadarrQualityItem.PreferredUnlimitedThreshold + 1; + var data = new RadarrQualityItem("", 0, 0, testVal); data.PreferredForApi.Should().Be(null); } [Test] public void PreferredForApi_HighestWithinThreshold_EqualsSameValue() { - const decimal testVal = RadarrQualityData.PreferredUnlimitedThreshold - 0.1m; - var data = new RadarrQualityData {Preferred = testVal}; + const decimal testVal = RadarrQualityItem.PreferredUnlimitedThreshold - 0.1m; + var data = new RadarrQualityItem("", 0, 0, testVal); data.PreferredForApi.Should().Be(testVal).And.Be(data.Preferred); } [Test] public void PreferredForApi_LowestWithinThreshold_EqualsSameValue() { - var data = new RadarrQualityData {Preferred = 0}; + var data = new RadarrQualityItem("", 0, 0, 0); data.PreferredForApi.Should().Be(0); } } diff --git a/src/TrashLib.Tests/Radarr/RadarrConfigurationTest.cs b/src/TrashLib.Tests/Radarr/RadarrConfigurationTest.cs index 1f887f46..61b3ce22 100644 --- a/src/TrashLib.Tests/Radarr/RadarrConfigurationTest.cs +++ b/src/TrashLib.Tests/Radarr/RadarrConfigurationTest.cs @@ -6,7 +6,6 @@ using NUnit.Framework; using TrashLib.Config; using TrashLib.Services.Radarr; using TrashLib.Services.Radarr.Config; -using TrashLib.Services.Radarr.QualityDefinition; namespace TrashLib.Tests.Radarr; @@ -95,7 +94,7 @@ public class RadarrConfigurationTest }, QualityDefinition = new QualityDefinitionConfig { - Type = RadarrQualityDefinitionType.Movie + Type = "movie" } }; diff --git a/src/TrashLib.Tests/Sonarr/QualityDefinition/SonarrQualityDataTest.cs b/src/TrashLib.Tests/Sonarr/QualityDefinition/SonarrQualityDataTest.cs index b69226a4..c108ebb3 100644 --- a/src/TrashLib.Tests/Sonarr/QualityDefinition/SonarrQualityDataTest.cs +++ b/src/TrashLib.Tests/Sonarr/QualityDefinition/SonarrQualityDataTest.cs @@ -1,6 +1,6 @@ using FluentAssertions; using NUnit.Framework; -using TrashLib.Services.Sonarr.QualityDefinition; +using TrashLib.Services.Common.QualityDefinition; namespace TrashLib.Tests.Sonarr.QualityDefinition; @@ -14,9 +14,9 @@ public class SonarrQualityDataTest new object?[] {100m, 101m, true}, new object?[] {100m, 98m, true}, new object?[] {100m, null, true}, - new object?[] {SonarrQualityData.MaxUnlimitedThreshold, null, false}, - new object?[] {SonarrQualityData.MaxUnlimitedThreshold - 1, null, true}, - new object?[] {SonarrQualityData.MaxUnlimitedThreshold, SonarrQualityData.MaxUnlimitedThreshold, true} + new object?[] {QualityItem.MaxUnlimitedThreshold, null, false}, + new object?[] {QualityItem.MaxUnlimitedThreshold - 1, null, true}, + new object?[] {QualityItem.MaxUnlimitedThreshold, QualityItem.MaxUnlimitedThreshold, true} }; private static readonly object[] MinTestValues = @@ -30,7 +30,7 @@ public class SonarrQualityDataTest public void MaxDifferent_WithVariousValues_ReturnsExpectedResult(decimal guideValue, decimal? radarrValue, bool isDifferent) { - var data = new SonarrQualityData {Max = guideValue}; + var data = new QualityItem("", 0, guideValue); data.IsMaxDifferent(radarrValue) .Should().Be(isDifferent); } @@ -39,7 +39,7 @@ public class SonarrQualityDataTest public void MinDifferent_WithVariousValues_ReturnsExpectedResult(decimal guideValue, decimal radarrValue, bool isDifferent) { - var data = new SonarrQualityData {Min = guideValue}; + var data = new QualityItem("", guideValue, 0); data.IsMinDifferent(radarrValue) .Should().Be(isDifferent); } @@ -47,16 +47,16 @@ public class SonarrQualityDataTest [Test] public void AnnotatedMax_OutsideThreshold_EqualsSameValueWithUnlimited() { - const decimal testVal = SonarrQualityData.MaxUnlimitedThreshold; - var data = new SonarrQualityData {Max = testVal}; + const decimal testVal = QualityItem.MaxUnlimitedThreshold; + var data = new QualityItem("", 0, 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}; + const decimal testVal = QualityItem.MaxUnlimitedThreshold - 1; + var data = new QualityItem("", 0, testVal); data.AnnotatedMax.Should().Be($"{testVal}"); } @@ -64,38 +64,38 @@ public class SonarrQualityDataTest public void AnnotatedMin_NoThreshold_EqualsSameValue() { const decimal testVal = 10m; - var data = new SonarrQualityData {Max = testVal}; + var data = new QualityItem("", 0, testVal); data.AnnotatedMax.Should().Be($"{testVal}"); } [Test] public void Max_AboveThreshold_EqualsSameValue() { - const decimal testVal = SonarrQualityData.MaxUnlimitedThreshold + 1; - var data = new SonarrQualityData {Max = testVal}; + const decimal testVal = QualityItem.MaxUnlimitedThreshold + 1; + var data = new QualityItem("", 0, testVal); data.Max.Should().Be(testVal); } [Test] public void MaxForApi_AboveThreshold_EqualsNull() { - const decimal testVal = SonarrQualityData.MaxUnlimitedThreshold + 1; - var data = new SonarrQualityData {Max = testVal}; + const decimal testVal = QualityItem.MaxUnlimitedThreshold + 1; + var data = new QualityItem("", 0, 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}; + const decimal testVal = QualityItem.MaxUnlimitedThreshold - 0.1m; + var data = new QualityItem("", 0, testVal); data.MaxForApi.Should().Be(testVal).And.Be(data.Max); } [Test] public void MaxForApi_LowestWithinThreshold_EqualsSameValue() { - var data = new SonarrQualityData {Max = 0}; + var data = new QualityItem("", 0, 0); data.MaxForApi.Should().Be(0); } } diff --git a/src/TrashLib/Repo/IRepoPaths.cs b/src/TrashLib/Repo/IRepoPaths.cs index dcb0bc6f..b5711881 100644 --- a/src/TrashLib/Repo/IRepoPaths.cs +++ b/src/TrashLib/Repo/IRepoPaths.cs @@ -6,4 +6,6 @@ public interface IRepoPaths { IReadOnlyCollection RadarrCustomFormatPaths { get; } IReadOnlyCollection SonarrReleaseProfilePaths { get; } + IReadOnlyCollection SonarrQualityPaths { get; } + IReadOnlyCollection RadarrQualityPaths { get; } } diff --git a/src/TrashLib/Repo/RepoMetadata.cs b/src/TrashLib/Repo/RepoMetadata.cs index 8bd2eb5e..d6e14c26 100644 --- a/src/TrashLib/Repo/RepoMetadata.cs +++ b/src/TrashLib/Repo/RepoMetadata.cs @@ -1,11 +1,13 @@ namespace TrashLib.Repo; public record RadarrMetadata( - IReadOnlyCollection CustomFormats + IReadOnlyCollection CustomFormats, + IReadOnlyCollection Qualities ); public record SonarrMetadata( - IReadOnlyCollection ReleaseProfiles + IReadOnlyCollection ReleaseProfiles, + IReadOnlyCollection Qualities ); public record JsonPaths( diff --git a/src/TrashLib/Repo/RepoPaths.cs b/src/TrashLib/Repo/RepoPaths.cs index 7ffddf24..d61a8357 100644 --- a/src/TrashLib/Repo/RepoPaths.cs +++ b/src/TrashLib/Repo/RepoPaths.cs @@ -4,5 +4,7 @@ namespace TrashLib.Repo; public record RepoPaths( IReadOnlyCollection RadarrCustomFormatPaths, - IReadOnlyCollection SonarrReleaseProfilePaths + IReadOnlyCollection SonarrReleaseProfilePaths, + IReadOnlyCollection RadarrQualityPaths, + IReadOnlyCollection SonarrQualityPaths ) : IRepoPaths; diff --git a/src/TrashLib/Repo/RepoPathsFactory.cs b/src/TrashLib/Repo/RepoPathsFactory.cs index 703c6bb8..bf395e5f 100644 --- a/src/TrashLib/Repo/RepoPathsFactory.cs +++ b/src/TrashLib/Repo/RepoPathsFactory.cs @@ -27,6 +27,9 @@ public class RepoPathsFactory : IRepoPathsFactory var metadata = _metadata.Value; return new RepoPaths( ToDirectoryInfoList(metadata.JsonPaths.Radarr.CustomFormats), - ToDirectoryInfoList(metadata.JsonPaths.Sonarr.ReleaseProfiles)); + ToDirectoryInfoList(metadata.JsonPaths.Sonarr.ReleaseProfiles), + ToDirectoryInfoList(metadata.JsonPaths.Radarr.Qualities), + ToDirectoryInfoList(metadata.JsonPaths.Sonarr.Qualities) + ); } } diff --git a/src/TrashLib/Services/Common/QualityDefinition/QualityGuideParser.cs b/src/TrashLib/Services/Common/QualityDefinition/QualityGuideParser.cs new file mode 100644 index 00000000..d9d4908e --- /dev/null +++ b/src/TrashLib/Services/Common/QualityDefinition/QualityGuideParser.cs @@ -0,0 +1,46 @@ +using System.IO.Abstractions; +using Common.Extensions; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Serilog; + +namespace TrashLib.Services.Common.QualityDefinition; + +internal class QualityGuideParser where T : class +{ + private readonly ILogger _log; + + public QualityGuideParser(ILogger log) + { + _log = log; + } + + public ICollection GetQualities(IEnumerable jsonDirectories) + { + return jsonDirectories + .SelectMany(x => x.GetFiles("*.json")) + .Select(ParseQuality) + .NotNull() + .ToList(); + } + + private T? ParseQuality(IFileInfo jsonFile) + { + var serializer = JsonSerializer.Create(new JsonSerializerSettings + { + ContractResolver = new DefaultContractResolver + { + NamingStrategy = new SnakeCaseNamingStrategy() + } + }); + + using var json = new JsonTextReader(jsonFile.OpenText()); + var quality = serializer.Deserialize(json); + if (quality is null) + { + _log.Debug("Failed to parse quality definition JSON file: {Filename}", jsonFile.FullName); + } + + return quality; + } +} diff --git a/src/TrashLib/Services/Common/QualityDefinition/QualityItem.cs b/src/TrashLib/Services/Common/QualityDefinition/QualityItem.cs new file mode 100644 index 00000000..55ac1136 --- /dev/null +++ b/src/TrashLib/Services/Common/QualityDefinition/QualityItem.cs @@ -0,0 +1,46 @@ +using System.Globalization; +using System.Text; + +namespace TrashLib.Services.Common.QualityDefinition; + +public class QualityItem +{ + public QualityItem(string quality, decimal min, decimal max) + { + Quality = quality; + Min = min; + Max = max; + } + + public const decimal MaxUnlimitedThreshold = 400; + + public string Quality { get; } + public decimal Min { get; } + public decimal Max { get; } + + 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 IsMinDifferent(decimal serviceValue) => serviceValue != Min; + + public bool IsMaxDifferent(decimal? serviceValue) + { + return serviceValue == null + ? MaxUnlimitedThreshold != Max + : serviceValue != Max || MaxUnlimitedThreshold == Max; + } +} diff --git a/src/TrashLib/Services/Radarr/Config/RadarrConfiguration.cs b/src/TrashLib/Services/Radarr/Config/RadarrConfiguration.cs index 699c012a..1ca36a93 100644 --- a/src/TrashLib/Services/Radarr/Config/RadarrConfiguration.cs +++ b/src/TrashLib/Services/Radarr/Config/RadarrConfiguration.cs @@ -1,6 +1,5 @@ using JetBrains.Annotations; using TrashLib.Config.Services; -using TrashLib.Services.Radarr.QualityDefinition; namespace TrashLib.Services.Radarr.Config; @@ -31,9 +30,6 @@ public class QualityProfileConfig [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] public class QualityDefinitionConfig { - // -1 does not map to a valid enumerator. this is to force validation to fail if it is not set from YAML. - // All of this craziness is to avoid making the enum type nullable. - public RadarrQualityDefinitionType Type { get; init; } = (RadarrQualityDefinitionType) (-1); - + public string Type { get; init; } = ""; public decimal PreferredRatio { get; set; } = 1.0m; } diff --git a/src/TrashLib/Services/Radarr/Config/RadarrConfigurationValidator.cs b/src/TrashLib/Services/Radarr/Config/RadarrConfigurationValidator.cs index 3f95a257..be2257cf 100644 --- a/src/TrashLib/Services/Radarr/Config/RadarrConfigurationValidator.cs +++ b/src/TrashLib/Services/Radarr/Config/RadarrConfigurationValidator.cs @@ -46,6 +46,6 @@ internal class QualityDefinitionConfigValidator : AbstractValidator x.Type).IsInEnum().WithMessage(messages.QualityDefinitionType); + RuleFor(x => x.Type).NotEmpty().WithMessage(messages.QualityDefinitionType); } } diff --git a/src/TrashLib/Services/Radarr/QualityDefinition/IRadarrQualityDefinitionGuideParser.cs b/src/TrashLib/Services/Radarr/QualityDefinition/IRadarrQualityDefinitionGuideParser.cs deleted file mode 100644 index 575d01c3..00000000 --- a/src/TrashLib/Services/Radarr/QualityDefinition/IRadarrQualityDefinitionGuideParser.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace TrashLib.Services.Radarr.QualityDefinition; - -public interface IRadarrQualityDefinitionGuideParser -{ - Task GetMarkdownData(); - IDictionary> ParseMarkdown(string markdown); -} diff --git a/src/TrashLib/Services/Radarr/QualityDefinition/IRadarrQualityGuideParser.cs b/src/TrashLib/Services/Radarr/QualityDefinition/IRadarrQualityGuideParser.cs new file mode 100644 index 00000000..cb831c84 --- /dev/null +++ b/src/TrashLib/Services/Radarr/QualityDefinition/IRadarrQualityGuideParser.cs @@ -0,0 +1,6 @@ +namespace TrashLib.Services.Radarr.QualityDefinition; + +public interface IRadarrQualityGuideParser +{ + ICollection GetQualities(); +} diff --git a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityData.cs b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityData.cs index 26496a20..a22c3ee4 100644 --- a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityData.cs +++ b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityData.cs @@ -1,25 +1,7 @@ -using TrashLib.Services.Sonarr.QualityDefinition; - namespace TrashLib.Services.Radarr.QualityDefinition; -public class RadarrQualityData : SonarrQualityData -{ - public const decimal PreferredUnlimitedThreshold = 395; - - public decimal Preferred { get; set; } - public decimal? PreferredForApi => Preferred < PreferredUnlimitedThreshold ? Preferred : null; - public string AnnotatedPreferred => AnnotatedValue(Preferred, PreferredUnlimitedThreshold); - - public decimal InterpolatedPreferred(decimal ratio) - { - var cappedMax = Math.Min(Max, PreferredUnlimitedThreshold); - return Math.Round(Min + (cappedMax - Min) * ratio, 1); - } - - public bool IsPreferredDifferent(decimal? serviceValue) - { - return serviceValue == null - ? PreferredUnlimitedThreshold != Preferred - : serviceValue != Preferred || PreferredUnlimitedThreshold == Preferred; - } -} +public record RadarrQualityData( + string TrashId, + string Type, + IReadOnlyCollection Qualities +); diff --git a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionGuideParser.cs b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionGuideParser.cs deleted file mode 100644 index 3e4b1a1b..00000000 --- a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionGuideParser.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.IO.Abstractions; -using System.Text.RegularExpressions; -using Common.Extensions; -using TrashLib.Startup; - -namespace TrashLib.Services.Radarr.QualityDefinition; - -internal class RadarrQualityDefinitionGuideParser : IRadarrQualityDefinitionGuideParser -{ - private readonly IAppPaths _paths; - private readonly Regex _regexHeader = new(@"^#+", RegexOptions.Compiled); - - private readonly Regex _regexTableRow = - new(@"\| *(.*?) *\| *([\d.]+) *\| *([\d.]+) *\|", RegexOptions.Compiled); - - public RadarrQualityDefinitionGuideParser(IAppPaths paths) - { - _paths = paths; - } - - public async Task GetMarkdownData() - { - var repoDir = _paths.RepoDirectory; - var file = repoDir - .SubDirectory("docs") - .SubDirectory("Radarr") - .File("Radarr-Quality-Settings-File-Size.md").OpenText(); - - return await file.ReadToEndAsync(); - } - - public IDictionary> ParseMarkdown(string markdown) - { - var results = new Dictionary>(); - List? table = null; - - var reader = new StringReader(markdown); - for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) - { - if (string.IsNullOrEmpty(line)) - { - continue; - } - - var match = _regexHeader.Match(line); - if (match.Success) - { - // todo: hard-coded for now since there's only one supported right now. - var type = RadarrQualityDefinitionType.Movie; - table = results.GetOrCreate(type); - - // If we grab a table that isn't empty, that means for whatever reason *another* table - // in the markdown is trying to modify a previous table's data. For example, maybe there - // are two "Series" quality tables. That would be a weird edge case, but handle that - // here just in case. - if (table.Count > 0) - { - table = null; - } - } - else if (table != null) - { - match = _regexTableRow.Match(line); - if (match.Success) - { - table.Add(new RadarrQualityData - { - Name = match.Groups[1].Value, - Min = match.Groups[2].Value.ToDecimal(), - Max = match.Groups[3].Value.ToDecimal() - }); - } - } - } - - return results; - } -} diff --git a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionType.cs b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionType.cs deleted file mode 100644 index 70a180e8..00000000 --- a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionType.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace TrashLib.Services.Radarr.QualityDefinition; - -public enum RadarrQualityDefinitionType -{ - Movie -} diff --git a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionUpdater.cs b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionUpdater.cs index 82c08b4a..36a846eb 100644 --- a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionUpdater.cs +++ b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityDefinitionUpdater.cs @@ -1,4 +1,5 @@ using CliFx.Infrastructure; +using Common.Extensions; using Serilog; using TrashLib.Services.Radarr.Config; using TrashLib.Services.Radarr.QualityDefinition.Api; @@ -8,58 +9,65 @@ namespace TrashLib.Services.Radarr.QualityDefinition; internal class RadarrQualityDefinitionUpdater : IRadarrQualityDefinitionUpdater { + private readonly ILogger _log; private readonly IQualityDefinitionService _api; private readonly IConsole _console; - private readonly IRadarrQualityDefinitionGuideParser _parser; + private readonly IRadarrQualityGuideParser _parser; public RadarrQualityDefinitionUpdater( ILogger logger, - IRadarrQualityDefinitionGuideParser parser, + IRadarrQualityGuideParser parser, IQualityDefinitionService api, IConsole console) { - Log = logger; + _log = logger; _parser = parser; _api = api; _console = console; } - private ILogger Log { get; } - public async Task Process(bool isPreview, RadarrConfiguration config) { - Log.Information("Processing Quality Definition: {QualityDefinition}", config.QualityDefinition!.Type); - var qualityDefinitions = _parser.ParseMarkdown(await _parser.GetMarkdownData()); + _log.Information("Processing Quality Definition: {QualityDefinition}", config.QualityDefinition!.Type); + var qualityDefinitions = _parser.GetQualities(); + var qualityTypeInConfig = config.QualityDefinition!.Type; + + var selectedQuality = qualityDefinitions + .FirstOrDefault(x => x.Type.EqualsIgnoreCase(qualityTypeInConfig)); - var selectedQuality = qualityDefinitions[config.QualityDefinition!.Type]; + if (selectedQuality == null) + { + _log.Error("The specified quality definition type does not exist: {Type}", qualityTypeInConfig); + return; + } // Fix an out of range ratio and warn the user if (config.QualityDefinition.PreferredRatio is < 0 or > 1) { var clampedRatio = Math.Clamp(config.QualityDefinition.PreferredRatio, 0, 1); - Log.Warning("Your `preferred_ratio` of {CurrentRatio} is out of range. " + - "It must be a decimal between 0.0 and 1.0. It has been clamped to {ClampedRatio}", + _log.Warning("Your `preferred_ratio` of {CurrentRatio} is out of range. " + + "It must be a decimal between 0.0 and 1.0. It has been clamped to {ClampedRatio}", config.QualityDefinition.PreferredRatio, clampedRatio); config.QualityDefinition.PreferredRatio = clampedRatio; } // Apply a calculated preferred size - foreach (var quality in selectedQuality) + foreach (var quality in selectedQuality.Qualities) { quality.Preferred = quality.InterpolatedPreferred(config.QualityDefinition.PreferredRatio); } if (isPreview) { - PrintQualityPreview(selectedQuality); + PrintQualityPreview(selectedQuality.Qualities); return; } - await ProcessQualityDefinition(selectedQuality); + await ProcessQualityDefinition(selectedQuality.Qualities); } - private void PrintQualityPreview(IEnumerable quality) + private void PrintQualityPreview(IEnumerable quality) { _console.Output.WriteLine(""); const string format = "{0,-20} {1,-10} {2,-15} {3,-15}"; @@ -68,22 +76,22 @@ internal class RadarrQualityDefinitionUpdater : IRadarrQualityDefinitionUpdater foreach (var q in quality) { - _console.Output.WriteLine(format, q.Name, q.AnnotatedMin, q.AnnotatedMax, q.AnnotatedPreferred); + _console.Output.WriteLine(format, q.Quality, q.AnnotatedMin, q.AnnotatedMax, q.AnnotatedPreferred); } _console.Output.WriteLine(""); } - private async Task ProcessQualityDefinition(IEnumerable guideQuality) + private async Task ProcessQualityDefinition(IEnumerable guideQuality) { var serverQuality = await _api.GetQualityDefinition(); await UpdateQualityDefinition(serverQuality, guideQuality); } private async Task UpdateQualityDefinition(IReadOnlyCollection serverQuality, - IEnumerable guideQuality) + IEnumerable guideQuality) { - static bool QualityIsDifferent(RadarrQualityDefinitionItem a, RadarrQualityData b) + static bool QualityIsDifferent(RadarrQualityDefinitionItem a, RadarrQualityItem b) { return b.IsMinDifferent(a.MinSize) || b.IsMaxDifferent(a.MaxSize) || @@ -93,10 +101,10 @@ internal class RadarrQualityDefinitionUpdater : IRadarrQualityDefinitionUpdater var newQuality = new List(); foreach (var qualityData in guideQuality) { - var entry = serverQuality.FirstOrDefault(q => q.Quality?.Name == qualityData.Name); + var entry = serverQuality.FirstOrDefault(q => q.Quality?.Name == qualityData.Quality); if (entry == null) { - Log.Warning("Server lacks quality definition for {Quality}; it will be skipped", qualityData.Name); + _log.Warning("Server lacks quality definition for {Quality}; it will be skipped", qualityData.Quality); continue; } @@ -111,12 +119,12 @@ internal class RadarrQualityDefinitionUpdater : IRadarrQualityDefinitionUpdater entry.PreferredSize = qualityData.PreferredForApi; newQuality.Add(entry); - Log.Debug("Setting Quality " + - "[Name: {Name}] [Source: {Source}] [Min: {Min}] [Max: {Max}] [Preferred: {Preferred}]", + _log.Debug("Setting Quality " + + "[Name: {Name}] [Source: {Source}] [Min: {Min}] [Max: {Max}] [Preferred: {Preferred}]", entry.Quality?.Name, entry.Quality?.Source, entry.MinSize, entry.MaxSize, entry.PreferredSize); } await _api.UpdateQualityDefinition(newQuality); - Log.Information("Number of updated qualities: {Count}", newQuality.Count); + _log.Information("Number of updated qualities: {Count}", newQuality.Count); } } diff --git a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityGuideParser.cs b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityGuideParser.cs new file mode 100644 index 00000000..82540a5e --- /dev/null +++ b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityGuideParser.cs @@ -0,0 +1,20 @@ +using Serilog; +using TrashLib.Repo; +using TrashLib.Services.Common.QualityDefinition; + +namespace TrashLib.Services.Radarr.QualityDefinition; + +internal class RadarrQualityGuideParser : IRadarrQualityGuideParser +{ + private readonly QualityGuideParser _parser; + private readonly IRepoPathsFactory _pathFactory; + + public RadarrQualityGuideParser(ILogger log, IRepoPathsFactory pathFactory) + { + _parser = new QualityGuideParser(log); + _pathFactory = pathFactory; + } + + public ICollection GetQualities() + => _parser.GetQualities(_pathFactory.Create().RadarrQualityPaths); +} diff --git a/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityItem.cs b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityItem.cs new file mode 100644 index 00000000..9882fffd --- /dev/null +++ b/src/TrashLib/Services/Radarr/QualityDefinition/RadarrQualityItem.cs @@ -0,0 +1,31 @@ +using TrashLib.Services.Common.QualityDefinition; + +namespace TrashLib.Services.Radarr.QualityDefinition; + +public class RadarrQualityItem : QualityItem +{ + public RadarrQualityItem(string quality, decimal min, decimal max, decimal preferred) + : base(quality, min, max) + { + Preferred = preferred; + } + + public const decimal PreferredUnlimitedThreshold = 395; + + public decimal Preferred { get; set; } + public decimal? PreferredForApi => Preferred < PreferredUnlimitedThreshold ? Preferred : null; + public string AnnotatedPreferred => AnnotatedValue(Preferred, PreferredUnlimitedThreshold); + + public decimal InterpolatedPreferred(decimal ratio) + { + var cappedMax = Math.Min(Max, PreferredUnlimitedThreshold); + return Math.Round(Min + (cappedMax - Min) * ratio, 1); + } + + public bool IsPreferredDifferent(decimal? serviceValue) + { + return serviceValue == null + ? PreferredUnlimitedThreshold != Preferred + : serviceValue != Preferred || PreferredUnlimitedThreshold == Preferred; + } +} diff --git a/src/TrashLib/Services/Radarr/RadarrAutofacModule.cs b/src/TrashLib/Services/Radarr/RadarrAutofacModule.cs index 092f77b1..acb0a53a 100644 --- a/src/TrashLib/Services/Radarr/RadarrAutofacModule.cs +++ b/src/TrashLib/Services/Radarr/RadarrAutofacModule.cs @@ -28,7 +28,7 @@ public class RadarrAutofacModule : Module // Quality Definition Support builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As(); // Custom Format Support builder.RegisterType().As(); diff --git a/src/TrashLib/Services/Sonarr/Config/SonarrConfiguration.cs b/src/TrashLib/Services/Sonarr/Config/SonarrConfiguration.cs index 32eeaadb..4d0d4349 100644 --- a/src/TrashLib/Services/Sonarr/Config/SonarrConfiguration.cs +++ b/src/TrashLib/Services/Sonarr/Config/SonarrConfiguration.cs @@ -1,12 +1,11 @@ using TrashLib.Config.Services; -using TrashLib.Services.Sonarr.QualityDefinition; namespace TrashLib.Services.Sonarr.Config; public class SonarrConfiguration : ServiceConfiguration { public IList ReleaseProfiles { get; init; } = Array.Empty(); - public SonarrQualityDefinitionType? QualityDefinition { get; init; } + public string QualityDefinition { get; init; } = ""; } public class ReleaseProfileConfig diff --git a/src/TrashLib/Services/Sonarr/QualityDefinition/ISonarrQualityDefinitionGuideParser.cs b/src/TrashLib/Services/Sonarr/QualityDefinition/ISonarrQualityDefinitionGuideParser.cs deleted file mode 100644 index 6cba477e..00000000 --- a/src/TrashLib/Services/Sonarr/QualityDefinition/ISonarrQualityDefinitionGuideParser.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace TrashLib.Services.Sonarr.QualityDefinition; - -public interface ISonarrQualityDefinitionGuideParser -{ - Task GetMarkdownData(); - IDictionary> ParseMarkdown(string markdown); -} diff --git a/src/TrashLib/Services/Sonarr/QualityDefinition/ISonarrQualityGuideParser.cs b/src/TrashLib/Services/Sonarr/QualityDefinition/ISonarrQualityGuideParser.cs new file mode 100644 index 00000000..57489d49 --- /dev/null +++ b/src/TrashLib/Services/Sonarr/QualityDefinition/ISonarrQualityGuideParser.cs @@ -0,0 +1,6 @@ +namespace TrashLib.Services.Sonarr.QualityDefinition; + +public interface ISonarrQualityGuideParser +{ + ICollection GetQualities(); +} diff --git a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityData.cs b/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityData.cs index 68cba62e..f0f614d3 100644 --- a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityData.cs +++ b/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityData.cs @@ -1,39 +1,9 @@ -using System.Globalization; -using System.Text; +using TrashLib.Services.Common.QualityDefinition; namespace TrashLib.Services.Sonarr.QualityDefinition; -public class SonarrQualityData -{ - 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 IsMinDifferent(decimal serviceValue) => serviceValue != Min; - - public bool IsMaxDifferent(decimal? serviceValue) - { - return serviceValue == null - ? MaxUnlimitedThreshold != Max - : serviceValue != Max || MaxUnlimitedThreshold == Max; - } -} +public record SonarrQualityData( + string TrashId, + string Type, + IReadOnlyCollection Qualities +); diff --git a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionGuideParser.cs b/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionGuideParser.cs deleted file mode 100644 index 65a0af43..00000000 --- a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionGuideParser.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.IO.Abstractions; -using System.Text.RegularExpressions; -using Common.Extensions; -using TrashLib.Startup; - -namespace TrashLib.Services.Sonarr.QualityDefinition; - -internal class SonarrQualityDefinitionGuideParser : ISonarrQualityDefinitionGuideParser -{ - private readonly Regex _regexHeader = new(@"^#+", RegexOptions.Compiled); - - private readonly Regex _regexTableRow = - new(@"\| *(.*?) *\| *([\d.]+) *\| *([\d.]+) *\|", RegexOptions.Compiled); - - private readonly IAppPaths _paths; - - public SonarrQualityDefinitionGuideParser(IAppPaths paths) - { - _paths = paths; - } - - public async Task GetMarkdownData() - { - var repoDir = _paths.RepoDirectory; - var file = repoDir - .SubDirectory("docs") - .SubDirectory("Sonarr") - .File("Sonarr-Quality-Settings-File-Size.md").OpenText(); - - return await file.ReadToEndAsync(); - } - - public IDictionary> ParseMarkdown(string markdown) - { - var results = new Dictionary>(); - List? table = null; - - var reader = new StringReader(markdown); - for (var line = reader.ReadLine(); line != null; line = reader.ReadLine()) - { - if (string.IsNullOrEmpty(line)) - { - continue; - } - - var match = _regexHeader.Match(line); - if (match.Success) - { - var type = line.ContainsIgnoreCase("anime") - ? SonarrQualityDefinitionType.Anime - : SonarrQualityDefinitionType.Series; - - table = results.GetOrCreate(type); - - // If we grab a table that isn't empty, that means for whatever reason *another* table - // in the markdown is trying to modify a previous table's data. For example, maybe there - // are two "Series" quality tables. That would be a weird edge case, but handle that - // here just in case. - if (table.Count > 0) - { - table = null; - } - } - else if (table != null) - { - match = _regexTableRow.Match(line); - if (match.Success) - { - table.Add(new SonarrQualityData - { - Name = match.Groups[1].Value, - Min = match.Groups[2].Value.ToDecimal(), - Max = match.Groups[3].Value.ToDecimal() - }); - } - } - } - - return results; - } -} diff --git a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionType.cs b/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionType.cs deleted file mode 100644 index 05203f70..00000000 --- a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace TrashLib.Services.Sonarr.QualityDefinition; - -public enum SonarrQualityDefinitionType -{ - Anime, - Series, - Hybrid -} diff --git a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionUpdater.cs b/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionUpdater.cs index a355700a..ef8fd71c 100644 --- a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionUpdater.cs +++ b/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityDefinitionUpdater.cs @@ -1,6 +1,8 @@ using System.Text.RegularExpressions; using CliFx.Infrastructure; +using Common.Extensions; using Serilog; +using TrashLib.Services.Common.QualityDefinition; using TrashLib.Services.Sonarr.Api; using TrashLib.Services.Sonarr.Api.Objects; using TrashLib.Services.Sonarr.Config; @@ -9,91 +11,117 @@ namespace TrashLib.Services.Sonarr.QualityDefinition; internal class SonarrQualityDefinitionUpdater : ISonarrQualityDefinitionUpdater { + private readonly ILogger _log; private readonly ISonarrApi _api; private readonly IConsole _console; - private readonly ISonarrQualityDefinitionGuideParser _parser; + private readonly ISonarrQualityGuideParser _parser; private readonly Regex _regexHybrid = new(@"720|1080", RegexOptions.Compiled); public SonarrQualityDefinitionUpdater( ILogger logger, - ISonarrQualityDefinitionGuideParser parser, + ISonarrQualityGuideParser parser, ISonarrApi api, IConsole console) { - Log = logger; + _log = logger; _parser = parser; _api = api; _console = console; } - private ILogger Log { get; } + private SonarrQualityData? GetQualityOrError(ICollection qualityDefinitions, string type) + { + var quality = qualityDefinitions.FirstOrDefault(x => x.Type.EqualsIgnoreCase(type)); + if (quality is null) + { + _log.Error( + "The following quality definition is required for hybrid, but was not found in the guide: {Type}", + type); + } + + return quality; + } public async Task Process(bool isPreview, SonarrConfiguration config) { - Log.Information("Processing Quality Definition: {QualityDefinition}", config.QualityDefinition); - var qualityDefinitions = _parser.ParseMarkdown(await _parser.GetMarkdownData()); - List selectedQuality; + _log.Information("Processing Quality Definition: {QualityDefinition}", config.QualityDefinition); + var qualityDefinitions = _parser.GetQualities(); + var qualityTypeInConfig = config.QualityDefinition; + + // var qualityDefinitions = _parser.ParseMarkdown(await _parser.GetMarkdownData()); + SonarrQualityData? selectedQuality; - if (config.QualityDefinition == SonarrQualityDefinitionType.Hybrid) + if (config.QualityDefinition.EqualsIgnoreCase("hybrid")) { - selectedQuality = BuildHybridQuality(qualityDefinitions[SonarrQualityDefinitionType.Anime], - qualityDefinitions[SonarrQualityDefinitionType.Series]); + var animeQuality = GetQualityOrError(qualityDefinitions, "anime"); + var seriesQuality = GetQualityOrError(qualityDefinitions, "series"); + if (animeQuality is null || seriesQuality is null) + { + return; + } + + selectedQuality = BuildHybridQuality(animeQuality.Qualities, seriesQuality.Qualities); } else { - selectedQuality = qualityDefinitions[config.QualityDefinition!.Value]; + selectedQuality = qualityDefinitions + .FirstOrDefault(x => x.Type.EqualsIgnoreCase(qualityTypeInConfig)); + + if (selectedQuality == null) + { + _log.Error("The specified quality definition type does not exist: {Type}", qualityTypeInConfig); + return; + } } if (isPreview) { - PrintQualityPreview(selectedQuality); + PrintQualityPreview(selectedQuality.Qualities); return; } - await ProcessQualityDefinition(selectedQuality); + await ProcessQualityDefinition(selectedQuality.Qualities); } - private List BuildHybridQuality(IReadOnlyCollection anime, - IEnumerable series) + private SonarrQualityData BuildHybridQuality( + IReadOnlyCollection anime, + IReadOnlyCollection series) { // todo Verify anime & series are the same length? Probably not, because we might not care about some rows anyway. - Log.Information( + _log.Information( "Notice: Hybrid only functions on 720/1080 qualities and uses non-anime values for the rest (e.g. 2160)"); - var hybrid = new List(); + var hybrid = new List(); foreach (var left in series) { // Any qualities that anime doesn't care about get immediately added from Series quality - var match = _regexHybrid.Match(left.Name); + var match = _regexHybrid.Match(left.Quality); if (!match.Success) { - Log.Debug("Using 'Series' Quality For: {QualityName}", left.Name); + _log.Debug("Using 'Series' Quality For: {QualityName}", left.Quality); hybrid.Add(left); continue; } // If there's a quality in Series that Anime doesn't know about, we add the Series quality - var right = anime.FirstOrDefault(row => row.Name == left.Name); + var right = anime.FirstOrDefault(row => row.Quality == left.Quality); if (right == null) { - Log.Error("Could not find matching anime quality for series quality named {QualityName}", - left.Name); + _log.Error("Could not find matching anime quality for series quality named {QualityName}", + left.Quality); hybrid.Add(left); continue; } - hybrid.Add(new SonarrQualityData - { - Name = left.Name, - Min = Math.Min(left.Min, right.Min), - Max = Math.Max(left.Max, right.Max) - }); + hybrid.Add(new QualityItem(left.Quality, + Math.Min(left.Min, right.Min), + Math.Max(left.Max, right.Max))); } - return hybrid; + return new SonarrQualityData("", "hybrid", hybrid); } - private void PrintQualityPreview(IEnumerable quality) + private void PrintQualityPreview(IEnumerable quality) { _console.Output.WriteLine(""); const string format = "{0,-20} {1,-10} {2,-15}"; @@ -102,22 +130,22 @@ internal class SonarrQualityDefinitionUpdater : ISonarrQualityDefinitionUpdater foreach (var q in quality) { - _console.Output.WriteLine(format, q.Name, q.AnnotatedMin, q.AnnotatedMax); + _console.Output.WriteLine(format, q.Quality, q.AnnotatedMin, q.AnnotatedMax); } _console.Output.WriteLine(""); } - private async Task ProcessQualityDefinition(IEnumerable guideQuality) + private async Task ProcessQualityDefinition(IEnumerable guideQuality) { var serverQuality = await _api.GetQualityDefinition(); await UpdateQualityDefinition(serverQuality, guideQuality); } private async Task UpdateQualityDefinition(IReadOnlyCollection serverQuality, - IEnumerable guideQuality) + IEnumerable guideQuality) { - static bool QualityIsDifferent(SonarrQualityDefinitionItem a, SonarrQualityData b) + static bool QualityIsDifferent(SonarrQualityDefinitionItem a, QualityItem b) { return b.IsMinDifferent(a.MinSize) || b.IsMaxDifferent(a.MaxSize); @@ -126,10 +154,10 @@ internal class SonarrQualityDefinitionUpdater : ISonarrQualityDefinitionUpdater var newQuality = new List(); foreach (var qualityData in guideQuality) { - var entry = serverQuality.FirstOrDefault(q => q.Quality?.Name == qualityData.Name); + var entry = serverQuality.FirstOrDefault(q => q.Quality?.Name == qualityData.Quality); if (entry == null) { - Log.Warning("Server lacks quality definition for {Quality}; it will be skipped", qualityData.Name); + _log.Warning("Server lacks quality definition for {Quality}; it will be skipped", qualityData.Quality); continue; } @@ -143,12 +171,12 @@ internal class SonarrQualityDefinitionUpdater : ISonarrQualityDefinitionUpdater entry.MaxSize = qualityData.MaxForApi; newQuality.Add(entry); - Log.Debug("Setting Quality " + - "[Name: {Name}] [Source: {Source}] [Min: {Min}] [Max: {Max}]", + _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); - Log.Information("Number of updated qualities: {Count}", newQuality.Count); + _log.Information("Number of updated qualities: {Count}", newQuality.Count); } } diff --git a/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityGuideParser.cs b/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityGuideParser.cs new file mode 100644 index 00000000..7f8cff7c --- /dev/null +++ b/src/TrashLib/Services/Sonarr/QualityDefinition/SonarrQualityGuideParser.cs @@ -0,0 +1,20 @@ +using Serilog; +using TrashLib.Repo; +using TrashLib.Services.Common.QualityDefinition; + +namespace TrashLib.Services.Sonarr.QualityDefinition; + +internal class SonarrQualityGuideParser : ISonarrQualityGuideParser +{ + private readonly QualityGuideParser _parser; + private readonly IRepoPathsFactory _pathFactory; + + public SonarrQualityGuideParser(ILogger log, IRepoPathsFactory pathFactory) + { + _parser = new QualityGuideParser(log); + _pathFactory = pathFactory; + } + + public ICollection GetQualities() + => _parser.GetQualities(_pathFactory.Create().SonarrQualityPaths); +} diff --git a/src/TrashLib/Services/Sonarr/SonarrAutofacModule.cs b/src/TrashLib/Services/Sonarr/SonarrAutofacModule.cs index 4a33aad6..7fd7268d 100644 --- a/src/TrashLib/Services/Sonarr/SonarrAutofacModule.cs +++ b/src/TrashLib/Services/Sonarr/SonarrAutofacModule.cs @@ -34,6 +34,6 @@ public class SonarrAutofacModule : Module // Quality Definition Support builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As(); } } diff --git a/src/TrashLib/TrashLib.csproj b/src/TrashLib/TrashLib.csproj index 32ab2e35..2d3bd0c1 100644 --- a/src/TrashLib/TrashLib.csproj +++ b/src/TrashLib/TrashLib.csproj @@ -21,8 +21,4 @@ - - - -