From 289d661247454aed25b7a69cf321516bd3091630 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Fri, 17 Feb 2023 08:23:19 -0600 Subject: [PATCH 01/10] refactor: Silence warnings in _Layout.cshtml --- src/Recyclarr.Gui/Pages/_Layout.cshtml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Recyclarr.Gui/Pages/_Layout.cshtml b/src/Recyclarr.Gui/Pages/_Layout.cshtml index dbbc7528..5e10cea1 100644 --- a/src/Recyclarr.Gui/Pages/_Layout.cshtml +++ b/src/Recyclarr.Gui/Pages/_Layout.cshtml @@ -3,6 +3,9 @@ @namespace Recyclarr.Gui.Pages @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@* ReSharper disable Html.PathError *@ + + From 82c2b47d91338dc6c20cba5bd0c0d7a6700682da Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Fri, 20 Jan 2023 18:11:52 -0600 Subject: [PATCH 02/10] chore: Ignore whitespace in DotSettings files --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 6623402f..e5c3ba68 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,4 @@ Dockerfile eol=lf # Ignore whitespace in these files *.sln -whitespace +*.DotSettings -whitespace From f73b9b5f4670bdf251996e33b3bd69e73a4350bf Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Fri, 27 Jan 2023 07:09:36 -0600 Subject: [PATCH 03/10] chore(style): Wrap params after opening brace --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index 19326120..7ba0c6b9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -331,6 +331,7 @@ resharper_csharp_naming_rule.other = AaBb resharper_csharp_prefer_qualified_reference = false resharper_csharp_space_after_unary_operator = false resharper_csharp_stick_comment = false +resharper_csharp_wrap_after_declaration_lpar = true resharper_csharp_wrap_lines = true resharper_cxxcli_property_declaration_braces = next_line resharper_default_exception_variable_name = e From ea59e71b18a4c775af040c1fe27f0b466d4105f7 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Mon, 30 Jan 2023 08:51:35 -0600 Subject: [PATCH 04/10] chore(style): Some defaults explicitly added by Rider --- .editorconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.editorconfig b/.editorconfig index 7ba0c6b9..32463602 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,7 @@ csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = false csharp_new_line_before_open_brace = all csharp_new_line_between_query_expression_clauses = true +csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion csharp_prefer_braces = true:none csharp_preserve_single_line_blocks = true csharp_space_after_cast = true @@ -325,9 +326,15 @@ resharper_csharp_align_multiple_declaration = false resharper_csharp_keep_blank_lines_in_code = 1 resharper_csharp_keep_blank_lines_in_declarations = 1 resharper_csharp_max_line_length = 120 +resharper_csharp_naming_rule.constants = AaBb resharper_csharp_naming_rule.enum_member = AaBb +resharper_csharp_naming_rule.local_constants = aaBb resharper_csharp_naming_rule.method_property_event = AaBb resharper_csharp_naming_rule.other = AaBb +resharper_csharp_naming_rule.private_constants = AaBb +resharper_csharp_naming_rule.private_static_fields = _ + aaBb +resharper_csharp_naming_rule.private_static_readonly = AaBb +resharper_csharp_naming_rule.static_readonly = AaBb resharper_csharp_prefer_qualified_reference = false resharper_csharp_space_after_unary_operator = false resharper_csharp_stick_comment = false From 7dec45a07a36d6b6e15c724ad7494312096e8681 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Mon, 16 Jan 2023 15:29:03 -0600 Subject: [PATCH 05/10] refactor: Eliminate generics for config type --- .../Console/Helpers/CacheStoragePath.cs | 2 +- .../TestConfig.cs | 3 +- .../Config/Parsing/ConfigRegistryTest.cs | 39 +++++++++++++++---- .../Parsing/ConfigurationLoaderEnvVarTest.cs | 13 +++---- .../Parsing/ConfigurationLoaderSecretsTest.cs | 2 +- .../Config/Parsing/ConfigurationLoaderTest.cs | 7 ++-- .../SonarrConfigurationValidatorTest.cs | 2 +- .../Config/Parsing/ConfigParser.cs | 2 +- .../Config/Parsing/ConfigRegistry.cs | 18 ++++----- .../Config/Parsing/IConfigRegistry.cs | 2 +- .../Config/Services/IServiceConfiguration.cs | 2 +- .../Config/Services/ServiceConfiguration.cs | 2 +- .../Services/ServiceConfigurationValidator.cs | 1 - .../Services/Processors/IServiceProcessor.cs | 6 +-- .../Services/Processors/RadarrProcessor.cs | 17 ++++---- .../Processors/ServiceProcessorFactory.cs | 9 +++-- .../ServiceProcessorsAutofacModule.cs | 7 ++-- .../Services/Processors/SonarrProcessor.cs | 23 ++++++----- .../Services/Processors/SyncProcessor.cs | 35 ++++++----------- .../Radarr/Config/RadarrConfiguration.cs | 4 +- .../Sonarr/Config/SonarrConfiguration.cs | 2 +- 21 files changed, 104 insertions(+), 94 deletions(-) diff --git a/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs b/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs index d89ad8b7..104e99e3 100644 --- a/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs +++ b/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs @@ -42,7 +42,7 @@ public class CacheStoragePath : ICacheStoragePath public IFileInfo CalculatePath(string cacheObjectName) { return _paths.CacheDirectory - .SubDirectory(_config.ServiceName.ToLower(CultureInfo.CurrentCulture)) + .SubDirectory(_config.ServiceType.ToString().ToLower(CultureInfo.CurrentCulture)) .SubDirectory(BuildUniqueServiceDir()) .File(cacheObjectName + ".json"); } diff --git a/src/Recyclarr.TrashLib.TestLibrary/TestConfig.cs b/src/Recyclarr.TrashLib.TestLibrary/TestConfig.cs index 20a558e1..5edd4f93 100644 --- a/src/Recyclarr.TrashLib.TestLibrary/TestConfig.cs +++ b/src/Recyclarr.TrashLib.TestLibrary/TestConfig.cs @@ -1,4 +1,5 @@ using JetBrains.Annotations; +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.TestLibrary; @@ -6,5 +7,5 @@ namespace Recyclarr.TrashLib.TestLibrary; [UsedImplicitly] public class TestConfig : ServiceConfiguration { - public override string ServiceName => "Test"; + public override SupportedServices ServiceType => SupportedServices.Sonarr; } diff --git a/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigRegistryTest.cs b/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigRegistryTest.cs index f80f5fc7..40366818 100644 --- a/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigRegistryTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigRegistryTest.cs @@ -2,6 +2,7 @@ using FluentAssertions; using NUnit.Framework; using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Parsing; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.Radarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config; @@ -14,17 +15,41 @@ public class ConfigRegistryTest [Test] public void Get_configs_by_type() { - var configs = new[] + var configs = new IServiceConfiguration[] { new SonarrConfiguration {InstanceName = "one"}, - new SonarrConfiguration {InstanceName = "two"} + new SonarrConfiguration {InstanceName = "two"}, + new RadarrConfiguration {InstanceName = "three"} }; var sut = new ConfigRegistry(); - sut.Add(SupportedServices.Sonarr, configs[0]); - sut.Add(SupportedServices.Sonarr, configs[1]); + foreach (var c in configs) + { + sut.Add(c); + } + + var result = sut.GetConfigsOfType(SupportedServices.Sonarr); + + result.Should().Equal(configs.Take(2)); + } + + [Test] + public void Null_service_type_returns_configs_of_all_types() + { + var configs = new IServiceConfiguration[] + { + new SonarrConfiguration {InstanceName = "one"}, + new SonarrConfiguration {InstanceName = "two"}, + new RadarrConfiguration {InstanceName = "three"} + }; + + var sut = new ConfigRegistry(); + foreach (var c in configs) + { + sut.Add(c); + } - var result = sut.GetConfigsOfType(SupportedServices.Sonarr); + var result = sut.GetConfigsOfType(null); result.Should().Equal(configs); } @@ -33,9 +58,9 @@ public class ConfigRegistryTest public void Get_empty_collection_when_no_configs_of_type() { var sut = new ConfigRegistry(); - sut.Add(SupportedServices.Sonarr, new SonarrConfiguration()); + sut.Add(new SonarrConfiguration()); - var result = sut.GetConfigsOfType(SupportedServices.Radarr); + var result = sut.GetConfigsOfType(SupportedServices.Radarr); result.Should().BeEmpty(); } diff --git a/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderEnvVarTest.cs b/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderEnvVarTest.cs index 856a6175..588530f8 100644 --- a/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderEnvVarTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderEnvVarTest.cs @@ -6,7 +6,6 @@ using Recyclarr.Common; using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.EnvironmentVariables; using Recyclarr.TrashLib.Config.Parsing; -using Recyclarr.TrashLib.Services.Sonarr.Config; using YamlDotNet.Core; namespace Recyclarr.TrashLib.Tests.Config.Parsing; @@ -33,7 +32,7 @@ sonarr: var configCollection = sut.LoadFromStream(new StringReader(testYml)); - var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); + var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); config.Should().BeEquivalentTo(new[] { new @@ -58,7 +57,7 @@ sonarr: var configCollection = sut.LoadFromStream(new StringReader(testYml)); - var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); + var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); config.Should().BeEquivalentTo(new[] { new @@ -85,7 +84,7 @@ sonarr: var configCollection = sut.LoadFromStream(new StringReader(testYml)); - var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); + var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); config.Should().BeEquivalentTo(new[] { new @@ -112,7 +111,7 @@ sonarr: var configCollection = sut.LoadFromStream(new StringReader(testYml)); - var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); + var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); config.Should().BeEquivalentTo(new[] { new @@ -137,7 +136,7 @@ sonarr: var configCollection = sut.LoadFromStream(new StringReader(testYml)); - var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); + var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); config.Should().BeEquivalentTo(new[] { new @@ -161,7 +160,7 @@ sonarr: var configCollection = sut.LoadFromStream(new StringReader(testYml)); - var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); + var config = configCollection.GetConfigsOfType(SupportedServices.Sonarr); config.Should().BeEquivalentTo(new[] { new diff --git a/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderSecretsTest.cs b/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderSecretsTest.cs index e30e7898..a2f1d0db 100644 --- a/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderSecretsTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderSecretsTest.cs @@ -55,7 +55,7 @@ secret_rp: 1234567 }; var parsedSecret = configLoader.LoadFromStream(new StringReader(testYml), "sonarr"); - parsedSecret.GetConfigsOfType(SupportedServices.Sonarr) + parsedSecret.GetConfigsOfType(SupportedServices.Sonarr) .Should().BeEquivalentTo(expected, o => o.Excluding(x => x.LineNumber)); } diff --git a/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderTest.cs b/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderTest.cs index febd82c9..6a3f14a9 100644 --- a/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Config/Parsing/ConfigurationLoaderTest.cs @@ -14,7 +14,6 @@ using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Parsing; using Recyclarr.TrashLib.Config.Yaml; -using Recyclarr.TrashLib.Services.Radarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.TestLibrary; @@ -81,10 +80,10 @@ public class ConfigurationLoaderTest : IntegrationFixture var loader = Resolve(); var actual = loader.LoadMany(fileData.Select(x => x.Item1)); - actual.GetConfigsOfType(SupportedServices.Sonarr) + actual.GetConfigsOfType(SupportedServices.Sonarr) .Should().BeEquivalentTo(expectedSonarr); - actual.GetConfigsOfType(SupportedServices.Radarr) + actual.GetConfigsOfType(SupportedServices.Radarr) .Should().BeEquivalentTo(expectedRadarr); } @@ -94,7 +93,7 @@ public class ConfigurationLoaderTest : IntegrationFixture var configLoader = Resolve(); var configs = configLoader.LoadFromStream(GetResourceData("Load_UsingStream_CorrectParsing.yml"), "sonarr"); - configs.GetConfigsOfType(SupportedServices.Sonarr) + configs.GetConfigsOfType(SupportedServices.Sonarr) .Should().BeEquivalentTo(new List { new() diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrConfigurationValidatorTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrConfigurationValidatorTest.cs index c444ae4d..a89cdc28 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrConfigurationValidatorTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrConfigurationValidatorTest.cs @@ -18,7 +18,7 @@ public class SonarrConfigurationValidatorTest : IntegrationFixture var validator = Resolve(); var result = validator.TestValidate(config); - result.ShouldNotHaveValidationErrorFor(x => x.ServiceName); + result.ShouldNotHaveValidationErrorFor(x => x.ServiceType); } [Test] diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ConfigParser.cs b/src/Recyclarr.TrashLib/Config/Parsing/ConfigParser.cs index a3de3c1c..51f17fc6 100644 --- a/src/Recyclarr.TrashLib/Config/Parsing/ConfigParser.cs +++ b/src/Recyclarr.TrashLib/Config/Parsing/ConfigParser.cs @@ -75,6 +75,6 @@ public class ConfigParser throw new YamlException("Validation failed"); } - _configs.Add(_currentSection.Value, newConfig); + _configs.Add(newConfig); } } diff --git a/src/Recyclarr.TrashLib/Config/Parsing/ConfigRegistry.cs b/src/Recyclarr.TrashLib/Config/Parsing/ConfigRegistry.cs index 504d2b62..6bfd0405 100644 --- a/src/Recyclarr.TrashLib/Config/Parsing/ConfigRegistry.cs +++ b/src/Recyclarr.TrashLib/Config/Parsing/ConfigRegistry.cs @@ -5,21 +5,19 @@ namespace Recyclarr.TrashLib.Config.Parsing; public class ConfigRegistry : IConfigRegistry { - private readonly Dictionary> _configs = new(); + private readonly Dictionary> _configs = new(); - public void Add(SupportedServices configType, ServiceConfiguration config) + public void Add(IServiceConfiguration config) { - _configs.GetOrCreate(configType).Add(config); + _configs.GetOrCreate(config.ServiceType).Add(config); } - public IReadOnlyCollection GetConfigsOfType(SupportedServices serviceType) where T : ServiceConfiguration + public IReadOnlyCollection GetConfigsOfType(SupportedServices? serviceType) { - if (_configs.TryGetValue(serviceType, out var configs)) - { - return configs.Cast().ToList(); - } - - return Array.Empty(); + return _configs + .Where(x => serviceType is null || serviceType.Value == x.Key) + .SelectMany(x => x.Value) + .ToList(); } public bool DoesConfigExist(string name) diff --git a/src/Recyclarr.TrashLib/Config/Parsing/IConfigRegistry.cs b/src/Recyclarr.TrashLib/Config/Parsing/IConfigRegistry.cs index bb85e370..9f0f6ef9 100644 --- a/src/Recyclarr.TrashLib/Config/Parsing/IConfigRegistry.cs +++ b/src/Recyclarr.TrashLib/Config/Parsing/IConfigRegistry.cs @@ -4,6 +4,6 @@ namespace Recyclarr.TrashLib.Config.Parsing; public interface IConfigRegistry { - IReadOnlyCollection GetConfigsOfType(SupportedServices serviceType) where T : ServiceConfiguration; + IReadOnlyCollection GetConfigsOfType(SupportedServices? serviceType); bool DoesConfigExist(string name); } diff --git a/src/Recyclarr.TrashLib/Config/Services/IServiceConfiguration.cs b/src/Recyclarr.TrashLib/Config/Services/IServiceConfiguration.cs index 05de2005..1074f6ea 100644 --- a/src/Recyclarr.TrashLib/Config/Services/IServiceConfiguration.cs +++ b/src/Recyclarr.TrashLib/Config/Services/IServiceConfiguration.cs @@ -2,7 +2,7 @@ namespace Recyclarr.TrashLib.Config.Services; public interface IServiceConfiguration { - string ServiceName { get; } + SupportedServices ServiceType { get; } string? InstanceName { get; } Uri BaseUrl { get; } string ApiKey { get; } diff --git a/src/Recyclarr.TrashLib/Config/Services/ServiceConfiguration.cs b/src/Recyclarr.TrashLib/Config/Services/ServiceConfiguration.cs index 90b63aa4..a3e29bc1 100644 --- a/src/Recyclarr.TrashLib/Config/Services/ServiceConfiguration.cs +++ b/src/Recyclarr.TrashLib/Config/Services/ServiceConfiguration.cs @@ -6,7 +6,7 @@ namespace Recyclarr.TrashLib.Config.Services; public abstract class ServiceConfiguration : IServiceConfiguration { [YamlIgnore] - public abstract string ServiceName { get; } + public abstract SupportedServices ServiceType { get; } // Name is set dynamically by ConfigurationLoader [YamlIgnore] diff --git a/src/Recyclarr.TrashLib/Config/Services/ServiceConfigurationValidator.cs b/src/Recyclarr.TrashLib/Config/Services/ServiceConfigurationValidator.cs index ac06ed81..04da3f57 100644 --- a/src/Recyclarr.TrashLib/Config/Services/ServiceConfigurationValidator.cs +++ b/src/Recyclarr.TrashLib/Config/Services/ServiceConfigurationValidator.cs @@ -14,7 +14,6 @@ internal class ServiceConfigurationValidator : AbstractValidator radarrValidator) { RuleFor(x => x.InstanceName).NotEmpty(); - RuleFor(x => x.ServiceName).NotEmpty(); RuleFor(x => x.LineNumber).NotEqual(0); RuleFor(x => x.BaseUrl).Must(x => x.Scheme is "http" or "https") .WithMessage("Property 'base_url' is required and must be a valid URL"); diff --git a/src/Recyclarr.TrashLib/Services/Processors/IServiceProcessor.cs b/src/Recyclarr.TrashLib/Services/Processors/IServiceProcessor.cs index 48154cff..4426a71f 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/IServiceProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/IServiceProcessor.cs @@ -1,8 +1,6 @@ -using Recyclarr.TrashLib.Config.Services; - namespace Recyclarr.TrashLib.Services.Processors; -public interface IServiceProcessor where T : ServiceConfiguration +public interface IServiceProcessor { - Task Process(T config, ISyncSettings settings); + Task Process(ISyncSettings settings); } diff --git a/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs b/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs index 4c7c68a4..5952c97d 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs @@ -5,38 +5,41 @@ using Recyclarr.TrashLib.Services.Radarr.Config; namespace Recyclarr.TrashLib.Services.Processors; -public class RadarrProcessor : IServiceProcessor +public class RadarrProcessor : IServiceProcessor { private readonly ILogger _log; private readonly ICustomFormatUpdater _cfUpdater; private readonly IQualitySizeUpdater _qualityUpdater; private readonly RadarrGuideService _guideService; + private readonly RadarrConfiguration _config; public RadarrProcessor( ILogger log, ICustomFormatUpdater cfUpdater, IQualitySizeUpdater qualityUpdater, - RadarrGuideService guideService) + RadarrGuideService guideService, + RadarrConfiguration config) { _log = log; _cfUpdater = cfUpdater; _qualityUpdater = qualityUpdater; _guideService = guideService; + _config = config; } - public async Task Process(RadarrConfiguration config, ISyncSettings settings) + public async Task Process(ISyncSettings settings) { var didWork = false; - if (config.QualityDefinition != null) + if (_config.QualityDefinition != null) { - await _qualityUpdater.Process(settings.Preview, config.QualityDefinition, _guideService); + await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, _guideService); didWork = true; } - if (config.CustomFormats.Count > 0) + if (_config.CustomFormats.Count > 0) { - await _cfUpdater.Process(settings.Preview, config.CustomFormats, _guideService); + await _cfUpdater.Process(settings.Preview, _config.CustomFormats, _guideService); didWork = true; } diff --git a/src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorFactory.cs b/src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorFactory.cs index 3548fbba..a04bf81e 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorFactory.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorFactory.cs @@ -1,6 +1,7 @@ using Autofac; using JetBrains.Annotations; using Recyclarr.Common.Autofac; +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Services.Processors; @@ -15,14 +16,14 @@ public class ServiceProcessorFactory _scope = scope; } - public LifetimeScopedValue> CreateProcessor(IServiceConfiguration config) - where T : ServiceConfiguration + public LifetimeScopedValue CreateProcessor( + SupportedServices serviceType, IServiceConfiguration config) { var scope = _scope.BeginLifetimeScope(builder => { - builder.RegisterInstance(config).As(); + builder.RegisterInstance(config).As().AsSelf(); }); - return new LifetimeScopedValue>(scope, scope.Resolve>()); + return new LifetimeScopedValue(scope, scope.ResolveKeyed(serviceType)); } } diff --git a/src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorsAutofacModule.cs b/src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorsAutofacModule.cs index e8b65a6c..f36e2652 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorsAutofacModule.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorsAutofacModule.cs @@ -1,6 +1,5 @@ using Autofac; -using Recyclarr.TrashLib.Services.Radarr.Config; -using Recyclarr.TrashLib.Services.Sonarr.Config; +using Recyclarr.TrashLib.Config; namespace Recyclarr.TrashLib.Services.Processors; @@ -10,8 +9,8 @@ public class ServiceProcessorsAutofacModule : Module { base.Load(builder); builder.RegisterType(); - builder.RegisterType().As>(); - builder.RegisterType().As>(); + builder.RegisterType().Keyed(SupportedServices.Radarr); + builder.RegisterType().Keyed(SupportedServices.Sonarr); builder.RegisterType().As(); } } diff --git a/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs b/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs index 83b7e1e9..f7ae9867 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs @@ -7,7 +7,7 @@ using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Services.Processors; -public class SonarrProcessor : IServiceProcessor +public class SonarrProcessor : IServiceProcessor { private readonly ILogger _log; private readonly ICustomFormatUpdater _cfUpdater; @@ -15,6 +15,7 @@ public class SonarrProcessor : IServiceProcessor private readonly SonarrGuideService _guideService; private readonly IReleaseProfileUpdater _profileUpdater; private readonly SonarrCapabilityEnforcer _compatibilityEnforcer; + private readonly SonarrConfiguration _config; public SonarrProcessor( ILogger log, @@ -22,7 +23,8 @@ public class SonarrProcessor : IServiceProcessor IQualitySizeUpdater qualityUpdater, SonarrGuideService guideService, IReleaseProfileUpdater profileUpdater, - SonarrCapabilityEnforcer compatibilityEnforcer) + SonarrCapabilityEnforcer compatibilityEnforcer, + SonarrConfiguration config) { _log = log; _cfUpdater = cfUpdater; @@ -30,30 +32,31 @@ public class SonarrProcessor : IServiceProcessor _guideService = guideService; _profileUpdater = profileUpdater; _compatibilityEnforcer = compatibilityEnforcer; + _config = config; } - public async Task Process(SonarrConfiguration config, ISyncSettings settings) + public async Task Process(ISyncSettings settings) { // Any compatibility failures will be thrown as exceptions - _compatibilityEnforcer.Check(config); + _compatibilityEnforcer.Check(_config); var didWork = false; - if (config.ReleaseProfiles.Count > 0) + if (_config.ReleaseProfiles.Count > 0) { - await _profileUpdater.Process(settings.Preview, config); + await _profileUpdater.Process(settings.Preview, _config); didWork = true; } - if (config.QualityDefinition != null) + if (_config.QualityDefinition != null) { - await _qualityUpdater.Process(settings.Preview, config.QualityDefinition, _guideService); + await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, _guideService); didWork = true; } - if (config.CustomFormats.Count > 0) + if (_config.CustomFormats.Count > 0) { - await _cfUpdater.Process(settings.Preview, config.CustomFormats, _guideService); + await _cfUpdater.Process(settings.Preview, _config.CustomFormats, _guideService); didWork = true; } diff --git a/src/Recyclarr.TrashLib/Services/Processors/SyncProcessor.cs b/src/Recyclarr.TrashLib/Services/Processors/SyncProcessor.cs index 56718fab..e71da344 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/SyncProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/SyncProcessor.cs @@ -6,8 +6,6 @@ using Recyclarr.TrashLib.Config.Parsing; using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Http; using Recyclarr.TrashLib.Repo.VersionControl; -using Recyclarr.TrashLib.Services.Radarr.Config; -using Recyclarr.TrashLib.Services.Sonarr.Config; using Spectre.Console; namespace Recyclarr.TrashLib.Services.Processors; @@ -37,7 +35,7 @@ public class SyncProcessor : ISyncProcessor public async Task ProcessConfigs(ISyncSettings settings) { - var failureDetected = false; + bool failureDetected; try { var configs = _configLoader.LoadMany(_configFinder.GetConfigFiles(settings.Configs)); @@ -48,17 +46,7 @@ public class SyncProcessor : ISyncProcessor _log.Warning("These instances do not exist: {Instances}", invalidInstances); } - if (settings.Service is null or SupportedServices.Radarr) - { - failureDetected |= - await ProcessService(SupportedServices.Radarr, settings, configs); - } - - if (settings.Service is null or SupportedServices.Sonarr) - { - failureDetected |= - await ProcessService(SupportedServices.Sonarr, settings, configs); - } + failureDetected = await ProcessService(settings.Service, settings, configs); } catch (Exception e) { @@ -69,11 +57,10 @@ public class SyncProcessor : ISyncProcessor return failureDetected ? ExitStatus.Failed : ExitStatus.Succeeded; } - private async Task ProcessService( - SupportedServices service, ISyncSettings settings, IConfigRegistry configs) - where TConfig : ServiceConfiguration + private async Task ProcessService( + SupportedServices? serviceType, ISyncSettings settings, IConfigRegistry configs) { - var serviceConfigs = configs.GetConfigsOfType(service); + var serviceConfigs = configs.GetConfigsOfType(serviceType); // If any config names are null, that means user specified array-style (deprecated) instances. if (serviceConfigs.Any(x => x.InstanceName is null)) @@ -98,9 +85,9 @@ public class SyncProcessor : ISyncProcessor continue; } - PrintProcessingHeader(service.ToString(), config); - using var processor = _factory.CreateProcessor(config); - await processor.Value.Process(config, settings); + PrintProcessingHeader(config.ServiceType, config); + using var processor = _factory.CreateProcessor(config.ServiceType, config); + await processor.Value.Process(settings); } catch (Exception e) { @@ -131,16 +118,16 @@ public class SyncProcessor : ISyncProcessor } } - private void PrintProcessingHeader(string serverName, ServiceConfiguration config) + private void PrintProcessingHeader(SupportedServices serviceType, IServiceConfiguration config) { var instanceName = config.InstanceName ?? FlurlLogging.SanitizeUrl(config.BaseUrl); _console.WriteLine($@" =========================================== -Processing {serverName} Server: [{instanceName}] +Processing {serviceType} Server: [{instanceName}] =========================================== "); - _log.Debug("Processing {Server} server {Name}", serverName, instanceName); + _log.Debug("Processing {Server} server {Name}", serviceType, instanceName); } } diff --git a/src/Recyclarr.TrashLib/Services/Radarr/Config/RadarrConfiguration.cs b/src/Recyclarr.TrashLib/Services/Radarr/Config/RadarrConfiguration.cs index 6c01618b..9bc99151 100644 --- a/src/Recyclarr.TrashLib/Services/Radarr/Config/RadarrConfiguration.cs +++ b/src/Recyclarr.TrashLib/Services/Radarr/Config/RadarrConfiguration.cs @@ -1,11 +1,9 @@ -using JetBrains.Annotations; using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Services.Radarr.Config; -[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] public class RadarrConfiguration : ServiceConfiguration { - public override string ServiceName { get; } = SupportedServices.Radarr.ToString(); + public override SupportedServices ServiceType => SupportedServices.Radarr; } diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Config/SonarrConfiguration.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Config/SonarrConfiguration.cs index 3e6482fc..a2a8ef0f 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Config/SonarrConfiguration.cs +++ b/src/Recyclarr.TrashLib/Services/Sonarr/Config/SonarrConfiguration.cs @@ -6,7 +6,7 @@ namespace Recyclarr.TrashLib.Services.Sonarr.Config; public class SonarrConfiguration : ServiceConfiguration { - public override string ServiceName { get; } = SupportedServices.Sonarr.ToString(); + public override SupportedServices ServiceType => SupportedServices.Sonarr; public IList ReleaseProfiles { get; [UsedImplicitly] init; } = Array.Empty(); From bcc65857df2966e8f7e3d619668baa49f81556a1 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Thu, 19 Jan 2023 16:05:31 -0600 Subject: [PATCH 06/10] refactor: Split Sonarr/Radarr orthogonally into service-based classes Instead of organizing logic using service, such as Sonarr or Radarr, organize it using function. So now logic is broken up by Custom Format, Release Profile, and Quality Size. --- src/Recyclarr.Cli/CompositionRoot.cs | 8 +- .../Commands/ListCustomFormatsCommand.cs | 13 +-- .../Console/Commands/ListQualitiesCommand.cs | 15 +-- .../Commands/ListReleaseProfilesCommand.cs | 6 +- .../Console/Commands/RadarrCommand.cs | 16 +-- .../Console/Commands/SonarrCommand.cs | 24 +++-- .../CustomFormat/GuideDataListerTest.cs | 10 +- .../Processors/GuideProcessorTest.cs | 9 +- .../GuideServiceTest.cs} | 22 ++-- .../ReleaseProfileDataListerTest.cs} | 34 +++--- .../Filters/ReleaseProfileDataFiltererTest.cs | 4 +- ...eleaseProfileDataValidationFiltererTest.cs | 4 +- .../Filters/StrictNegativeScoresFilterTest.cs | 4 +- .../ReleaseProfileDataValidatorTest.cs | 2 +- .../Sonarr/ReleaseProfile/ScopedStateTest.cs | 2 +- .../Sonarr/SonarrCompatibilityTest.cs | 4 +- .../Repo/IRepoMetadataBuilder.cs | 10 ++ src/Recyclarr.TrashLib/Repo/IRepoPaths.cs | 14 --- .../Repo/IRepoPathsFactory.cs | 7 -- .../Repo/RepoAutofacModule.cs | 2 +- src/Recyclarr.TrashLib/Repo/RepoMetadata.cs | 36 ++++--- .../Repo/RepoMetadataBuilder.cs | 30 ++++++ src/Recyclarr.TrashLib/Repo/RepoPaths.cs | 13 --- .../Repo/RepoPathsFactory.cs | 40 ------- .../Services/Common/GuideDataLister.cs | 54 ---------- .../Common/GuideServicesAutofacModule.cs | 12 --- .../Services/Common/IGuideDataLister.cs | 10 -- .../Services/Common/IGuideService.cs | 10 -- .../CustomFormat/CustomFormatAutofacModule.cs | 1 + .../CustomFormat/CustomFormatUpdater.cs | 6 +- .../Guide/CustomFormatDataLister.cs | 41 +++++++ .../Guide/CustomFormatGuideService.cs | 46 ++++++++ .../CustomFormat/Guide/CustomFormatPaths.cs | 8 ++ .../Guide/ICustomFormatGuideService.cs | 9 ++ .../CustomFormat/ICustomFormatUpdater.cs | 4 +- .../CustomFormat/Processors/GuideProcessor.cs | 17 ++- .../Processors/IGuideProcessor.cs | 8 +- .../Services/Processors/RadarrProcessor.cs | 9 +- .../Services/Processors/SonarrProcessor.cs | 11 +- .../QualitySize/Guide/IQualityGuideService.cs | 8 ++ .../QualitySize/Guide/QualityGuideService.cs | 39 +++++++ .../Guide/QualitySizeDataLister.cs | 32 ++++++ .../Guide/QualitySizeGuideParser.cs | 8 +- .../QualitySize/Guide/QualitySizePaths.cs | 7 ++ .../QualitySize/IQualitySizeUpdater.cs | 4 +- .../QualitySize/QualitySizeAutofacModule.cs | 16 +++ .../QualitySize/QualitySizeUpdater.cs | 12 ++- .../Services/Radarr/IRadarrGuideDataLister.cs | 7 -- .../Radarr/LocalRepoRadarrGuideService.cs | 34 ------ .../Services/Radarr/RadarrAutofacModule.cs | 12 --- .../Services/Radarr/RadarrGuideDataLister.cs | 29 ----- .../Services/Radarr/RadarrGuideService.cs | 11 -- .../Api/IReleaseProfileApiService.cs | 4 +- ...onarrReleaseProfileCompatibilityHandler.cs | 4 +- .../Mappings/SonarrApiObjectMappingProfile.cs | 4 +- .../Api/Objects/SonarrReleaseProfile.cs | 2 +- .../Api/ReleaseProfileApiService.cs | 4 +- .../Api/Schemas/SonarrReleaseProfileSchema.cs | 2 +- ...onarrReleaseProfileCompatibilityHandler.cs | 6 +- .../Filters/IReleaseProfileFilter.cs | 2 +- .../Filters/IReleaseProfileFilterPipeline.cs | 2 +- .../Filters/IncludeExcludeFilter.cs | 2 +- .../Filters/ReleaseProfileDataFilterer.cs | 2 +- .../ReleaseProfileDataValidationFilterer.cs | 2 +- .../Filters/ReleaseProfileFilterPipeline.cs | 2 +- .../Filters/StrictNegativeScoresFilter.cs | 2 +- .../Guide/IReleaseProfileGuideService.cs | 6 ++ .../Guide/ReleaseProfileDataLister.cs} | 32 ++---- .../Guide/ReleaseProfileGuideParser.cs | 59 +++++++++++ .../Guide/ReleaseProfileGuideService.cs | 31 ++++++ .../Guide/ReleaseProfilePaths.cs | 7 ++ .../ReleaseProfile/IReleaseProfileUpdater.cs | 2 +- .../ReleaseProfileAutofacModule.cs | 31 ++++++ .../ReleaseProfile/ReleaseProfileData.cs | 22 +--- .../ReleaseProfileDataValidator.cs | 2 +- .../ReleaseProfile/ReleaseProfileUpdater.cs | 21 ++-- .../ReleaseProfile/ScopedState.cs | 2 +- .../ReleaseProfile/TermDataConverter.cs | 2 +- .../Services/Sonarr/Api/ISonarrApi.cs | 13 --- .../Sonarr/Api/ISonarrTagApiService.cs | 9 ++ .../Objects/SonarrQualityDefinitionItem.cs | 23 ---- .../Services/Sonarr/Api/SonarrApi.cs | 42 -------- .../Sonarr/Api/SonarrTagApiService.cs | 28 +++++ .../Services/Sonarr/ISonarrGuideDataLister.cs | 9 -- .../Sonarr/LocalRepoSonarrGuideService.cs | 100 ------------------ .../Services/Sonarr/SonarrAutofacModule.cs | 27 +---- .../Services/Sonarr/SonarrGuideService.cs | 14 --- 87 files changed, 620 insertions(+), 696 deletions(-) rename src/Recyclarr.TrashLib.Tests/{Sonarr/ReleaseProfile/Guide/LocalRepoSonarrGuideServiceTest.cs => Services/GuideServiceTest.cs} (79%) rename src/Recyclarr.TrashLib.Tests/{Sonarr/SonarrGuideDataListerTest.cs => Services/ReleaseProfileDataListerTest.cs} (72%) create mode 100644 src/Recyclarr.TrashLib/Repo/IRepoMetadataBuilder.cs delete mode 100644 src/Recyclarr.TrashLib/Repo/IRepoPaths.cs delete mode 100644 src/Recyclarr.TrashLib/Repo/IRepoPathsFactory.cs create mode 100644 src/Recyclarr.TrashLib/Repo/RepoMetadataBuilder.cs delete mode 100644 src/Recyclarr.TrashLib/Repo/RepoPaths.cs delete mode 100644 src/Recyclarr.TrashLib/Repo/RepoPathsFactory.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Common/GuideDataLister.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Common/GuideServicesAutofacModule.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Common/IGuideDataLister.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Common/IGuideService.cs create mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatDataLister.cs create mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatGuideService.cs create mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatPaths.cs create mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Guide/ICustomFormatGuideService.cs create mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/Guide/IQualityGuideService.cs create mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualityGuideService.cs create mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizeDataLister.cs create mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizePaths.cs create mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeAutofacModule.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Radarr/IRadarrGuideDataLister.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Radarr/LocalRepoRadarrGuideService.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Radarr/RadarrGuideDataLister.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Radarr/RadarrGuideService.cs rename src/Recyclarr.TrashLib/Services/{Sonarr => ReleaseProfile}/Api/IReleaseProfileApiService.cs (71%) rename src/Recyclarr.TrashLib/Services/{Sonarr => ReleaseProfile}/Api/ISonarrReleaseProfileCompatibilityHandler.cs (67%) rename src/Recyclarr.TrashLib/Services/{Sonarr => ReleaseProfile}/Api/Mappings/SonarrApiObjectMappingProfile.cs (86%) rename src/Recyclarr.TrashLib/Services/{Sonarr => ReleaseProfile}/Api/Objects/SonarrReleaseProfile.cs (96%) rename src/Recyclarr.TrashLib/Services/{Sonarr => ReleaseProfile}/Api/ReleaseProfileApiService.cs (93%) rename src/Recyclarr.TrashLib/Services/{Sonarr => ReleaseProfile}/Api/Schemas/SonarrReleaseProfileSchema.cs (97%) rename src/Recyclarr.TrashLib/Services/{Sonarr => ReleaseProfile}/Api/SonarrReleaseProfileCompatibilityHandler.cs (92%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/Filters/IReleaseProfileFilter.cs (72%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/Filters/IReleaseProfileFilterPipeline.cs (73%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/Filters/IncludeExcludeFilter.cs (91%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/Filters/ReleaseProfileDataFilterer.cs (97%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/Filters/ReleaseProfileDataValidationFilterer.cs (95%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/Filters/ReleaseProfileFilterPipeline.cs (88%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/Filters/StrictNegativeScoresFilter.cs (91%) create mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/IReleaseProfileGuideService.cs rename src/Recyclarr.TrashLib/Services/{Sonarr/SonarrGuideDataLister.cs => ReleaseProfile/Guide/ReleaseProfileDataLister.cs} (76%) create mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideParser.cs create mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideService.cs create mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfilePaths.cs rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/IReleaseProfileUpdater.cs (72%) create mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileAutofacModule.cs rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/ReleaseProfileData.cs (67%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/ReleaseProfileDataValidator.cs (93%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/ReleaseProfileUpdater.cs (93%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/ScopedState.cs (95%) rename src/Recyclarr.TrashLib/Services/{Sonarr => }/ReleaseProfile/TermDataConverter.cs (92%) delete mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrApi.cs create mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrTagApiService.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/Api/Objects/SonarrQualityDefinitionItem.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrApi.cs create mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrTagApiService.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/ISonarrGuideDataLister.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/LocalRepoSonarrGuideService.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/SonarrGuideService.cs diff --git a/src/Recyclarr.Cli/CompositionRoot.cs b/src/Recyclarr.Cli/CompositionRoot.cs index 10fae7e2..268bc580 100644 --- a/src/Recyclarr.Cli/CompositionRoot.cs +++ b/src/Recyclarr.Cli/CompositionRoot.cs @@ -14,10 +14,11 @@ using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Http; using Recyclarr.TrashLib.Repo; using Recyclarr.TrashLib.Repo.VersionControl; -using Recyclarr.TrashLib.Services.Common; using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.Processors; +using Recyclarr.TrashLib.Services.QualitySize; using Recyclarr.TrashLib.Services.Radarr; +using Recyclarr.TrashLib.Services.ReleaseProfile; using Recyclarr.TrashLib.Services.Sonarr; using Recyclarr.TrashLib.Services.System; using Recyclarr.TrashLib.Startup; @@ -38,11 +39,12 @@ public static class CompositionRoot builder.RegisterModule(); builder.RegisterModule(); + builder.RegisterModule(); + builder.RegisterModule(); + builder.RegisterModule(); builder.RegisterModule(); builder.RegisterModule(); builder.RegisterModule(); - builder.RegisterModule(); - builder.RegisterModule(); builder.RegisterModule(); builder.RegisterModule(new ConfigAutofacModule(assemblies)); builder.RegisterModule(); diff --git a/src/Recyclarr.Cli/Console/Commands/ListCustomFormatsCommand.cs b/src/Recyclarr.Cli/Console/Commands/ListCustomFormatsCommand.cs index 5ff2348a..ef8ab4fc 100644 --- a/src/Recyclarr.Cli/Console/Commands/ListCustomFormatsCommand.cs +++ b/src/Recyclarr.Cli/Console/Commands/ListCustomFormatsCommand.cs @@ -1,11 +1,10 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using Autofac.Features.Indexed; using JetBrains.Annotations; using Recyclarr.Cli.Console.Helpers; using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Repo; -using Recyclarr.TrashLib.Services.Common; +using Recyclarr.TrashLib.Services.CustomFormat.Guide; using Spectre.Console.Cli; #pragma warning disable CS8765 @@ -16,8 +15,7 @@ namespace Recyclarr.Cli.Console.Commands; [Description("List custom formats in the guide for a particular service.")] internal class ListCustomFormatsCommand : AsyncCommand { - private readonly IGuideDataLister _lister; - private readonly IIndex _guideService; + private readonly CustomFormatDataLister _lister; private readonly IRepoUpdater _repoUpdater; [UsedImplicitly] @@ -31,20 +29,17 @@ internal class ListCustomFormatsCommand : AsyncCommand guideService, + CustomFormatDataLister lister, IRepoUpdater repoUpdater) { _lister = lister; - _guideService = guideService; _repoUpdater = repoUpdater; } public override async Task ExecuteAsync(CommandContext context, CliSettings settings) { await _repoUpdater.UpdateRepo(); - var guideService = _guideService[settings.Service]; - _lister.ListCustomFormats(guideService.GetCustomFormatData()); + _lister.ListCustomFormats(settings.Service); return 0; } } diff --git a/src/Recyclarr.Cli/Console/Commands/ListQualitiesCommand.cs b/src/Recyclarr.Cli/Console/Commands/ListQualitiesCommand.cs index 6a6c256e..5738ee70 100644 --- a/src/Recyclarr.Cli/Console/Commands/ListQualitiesCommand.cs +++ b/src/Recyclarr.Cli/Console/Commands/ListQualitiesCommand.cs @@ -1,11 +1,10 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using Autofac.Features.Indexed; using JetBrains.Annotations; using Recyclarr.Cli.Console.Helpers; using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Repo; -using Recyclarr.TrashLib.Services.Common; +using Recyclarr.TrashLib.Services.QualitySize.Guide; using Spectre.Console.Cli; namespace Recyclarr.Cli.Console.Commands; @@ -15,8 +14,7 @@ namespace Recyclarr.Cli.Console.Commands; [Description("List quality definitions in the guide for a particular service.")] internal class ListQualitiesCommand : AsyncCommand { - private readonly IGuideDataLister _lister; - private readonly IIndex _guideService; + private readonly QualitySizeDataLister _lister; private readonly IRepoUpdater _repoUpdater; [UsedImplicitly] @@ -29,21 +27,16 @@ internal class ListQualitiesCommand : AsyncCommand guideService, - IRepoUpdater repoUpdater) + public ListQualitiesCommand(QualitySizeDataLister lister, IRepoUpdater repoUpdater) { _lister = lister; - _guideService = guideService; _repoUpdater = repoUpdater; } public override async Task ExecuteAsync(CommandContext context, CliSettings settings) { await _repoUpdater.UpdateRepo(); - var guideService = _guideService[settings.Service]; - _lister.ListQualities(guideService.GetQualities()); + _lister.ListQualities(settings.Service); return 0; } } diff --git a/src/Recyclarr.Cli/Console/Commands/ListReleaseProfilesCommand.cs b/src/Recyclarr.Cli/Console/Commands/ListReleaseProfilesCommand.cs index e6745175..eaed91d0 100644 --- a/src/Recyclarr.Cli/Console/Commands/ListReleaseProfilesCommand.cs +++ b/src/Recyclarr.Cli/Console/Commands/ListReleaseProfilesCommand.cs @@ -2,7 +2,7 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using JetBrains.Annotations; using Recyclarr.TrashLib.Repo; -using Recyclarr.TrashLib.Services.Sonarr; +using Recyclarr.TrashLib.Services.ReleaseProfile.Guide; using Spectre.Console.Cli; #pragma warning disable CS8765 @@ -14,7 +14,7 @@ namespace Recyclarr.Cli.Console.Commands; internal class ListReleaseProfilesCommand : AsyncCommand { private readonly ILogger _log; - private readonly ISonarrGuideDataLister _lister; + private readonly ReleaseProfileDataLister _lister; private readonly IRepoUpdater _repoUpdater; [UsedImplicitly] @@ -31,7 +31,7 @@ internal class ListReleaseProfilesCommand : AsyncCommand { private readonly ILogger _log; - private readonly IRadarrGuideDataLister _lister; + private readonly CustomFormatDataLister _cfLister; + private readonly QualitySizeDataLister _qualityLister; private readonly IMigrationExecutor _migration; private readonly IRepoUpdater _repoUpdater; private readonly ISyncProcessor _syncProcessor; @@ -56,13 +58,15 @@ internal class RadarrCommand : AsyncCommand public RadarrCommand( ILogger log, - IRadarrGuideDataLister lister, + CustomFormatDataLister cfLister, + QualitySizeDataLister qualityLister, IMigrationExecutor migration, IRepoUpdater repoUpdater, ISyncProcessor syncProcessor) { _log = log; - _lister = lister; + _cfLister = cfLister; + _qualityLister = qualityLister; _migration = migration; _repoUpdater = repoUpdater; _syncProcessor = syncProcessor; @@ -77,14 +81,14 @@ internal class RadarrCommand : AsyncCommand if (settings.ListCustomFormats) { _log.Warning("The `radarr` subcommand is DEPRECATED -- Use `list custom-formats radarr` instead!"); - _lister.ListCustomFormats(); + _cfLister.ListCustomFormats(SupportedServices.Radarr); return 0; } if (settings.ListQualities) { _log.Warning("The `radarr` subcommand is DEPRECATED -- Use `list qualities radarr` instead!"); - _lister.ListQualities(); + _qualityLister.ListQualities(SupportedServices.Radarr); return 0; } diff --git a/src/Recyclarr.Cli/Console/Commands/SonarrCommand.cs b/src/Recyclarr.Cli/Console/Commands/SonarrCommand.cs index 89bbbf2a..8acf86b7 100644 --- a/src/Recyclarr.Cli/Console/Commands/SonarrCommand.cs +++ b/src/Recyclarr.Cli/Console/Commands/SonarrCommand.cs @@ -6,8 +6,10 @@ using Recyclarr.Cli.Console.Helpers; using Recyclarr.Cli.Migration; using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Repo; +using Recyclarr.TrashLib.Services.CustomFormat.Guide; using Recyclarr.TrashLib.Services.Processors; -using Recyclarr.TrashLib.Services.Sonarr; +using Recyclarr.TrashLib.Services.QualitySize.Guide; +using Recyclarr.TrashLib.Services.ReleaseProfile.Guide; using Spectre.Console.Cli; namespace Recyclarr.Cli.Console.Commands; @@ -17,7 +19,9 @@ namespace Recyclarr.Cli.Console.Commands; internal class SonarrCommand : AsyncCommand { private readonly ILogger _log; - private readonly ISonarrGuideDataLister _lister; + private readonly CustomFormatDataLister _cfLister; + private readonly QualitySizeDataLister _qualityLister; + private readonly ReleaseProfileDataLister _rpLister; private readonly IMigrationExecutor _migration; private readonly IRepoUpdater _repoUpdater; private readonly ISyncProcessor _syncProcessor; @@ -70,13 +74,17 @@ internal class SonarrCommand : AsyncCommand public SonarrCommand( ILogger log, - ISonarrGuideDataLister lister, + CustomFormatDataLister cfLister, + QualitySizeDataLister qualityLister, + ReleaseProfileDataLister rpLister, IMigrationExecutor migration, IRepoUpdater repoUpdater, ISyncProcessor syncProcessor) { _log = log; - _lister = lister; + _cfLister = cfLister; + _qualityLister = qualityLister; + _rpLister = rpLister; _migration = migration; _repoUpdater = repoUpdater; _syncProcessor = syncProcessor; @@ -91,27 +99,27 @@ internal class SonarrCommand : AsyncCommand if (settings.ListCustomFormats) { _log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list custom-formats sonarr` instead!"); - _lister.ListCustomFormats(); + _cfLister.ListCustomFormats(SupportedServices.Sonarr); return 0; } if (settings.ListQualities) { _log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list qualities sonarr` instead!"); - _lister.ListQualities(); + _qualityLister.ListQualities(SupportedServices.Sonarr); return 0; } if (settings.ListReleaseProfiles) { _log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list release-profiles` instead!"); - _lister.ListReleaseProfiles(); + _rpLister.ListReleaseProfiles(); return 0; } if (settings.ListTerms is not null) { - _lister.ListTerms(settings.ListTerms); + _rpLister.ListTerms(settings.ListTerms); return 0; } diff --git a/src/Recyclarr.TrashLib.Tests/CustomFormat/GuideDataListerTest.cs b/src/Recyclarr.TrashLib.Tests/CustomFormat/GuideDataListerTest.cs index ac2dd82b..40592978 100644 --- a/src/Recyclarr.TrashLib.Tests/CustomFormat/GuideDataListerTest.cs +++ b/src/Recyclarr.TrashLib.Tests/CustomFormat/GuideDataListerTest.cs @@ -1,8 +1,9 @@ using AutoFixture.NUnit3; using FluentAssertions; +using NSubstitute; using NUnit.Framework; using Recyclarr.TestLibrary.AutoFixture; -using Recyclarr.TrashLib.Services.Common; +using Recyclarr.TrashLib.Services.CustomFormat.Guide; using Recyclarr.TrashLib.TestLibrary; using Spectre.Console.Testing; @@ -15,7 +16,8 @@ public class GuideDataListerTest [Test, AutoMockData] public void Custom_formats_appear_in_console_output( [Frozen(Matching.ImplementedInterfaces)] TestConsole console, - GuideDataLister sut) + [Frozen] ICustomFormatGuideService guide, + CustomFormatDataLister sut) { var testData = new[] { @@ -23,7 +25,9 @@ public class GuideDataListerTest NewCf.Data("Second", "456") }; - sut.ListCustomFormats(testData); + guide.GetCustomFormatData(default!).ReturnsForAnyArgs(testData); + + sut.ListCustomFormats(default!); console.Output.Should().ContainAll( testData.SelectMany(x => new[] {x.Name, x.TrashId})); diff --git a/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/GuideProcessorTest.cs b/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/GuideProcessorTest.cs index a0b99f63..bbdc206a 100644 --- a/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/GuideProcessorTest.cs +++ b/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/GuideProcessorTest.cs @@ -10,7 +10,6 @@ using Recyclarr.TrashLib.Services.CustomFormat.Guide; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Processors; using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps; -using Recyclarr.TrashLib.Services.Radarr; using Recyclarr.TrashLib.TestLibrary; namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors; @@ -57,11 +56,11 @@ public class GuideProcessorTest public async Task Guide_processor_behaves_as_expected_with_normal_guide_data() { var ctx = new Context(); - var guideService = Substitute.For(); - var guideProcessor = new GuideProcessor(new TestGuideProcessorSteps()); + var guideService = Substitute.For(); + var guideProcessor = new GuideProcessor(new TestGuideProcessorSteps(), guideService); // simulate guide data - guideService.GetCustomFormatData().Returns(new[] + guideService.GetCustomFormatData(default!).ReturnsForAnyArgs(new[] { ctx.ReadCustomFormat("ImportableCustomFormat1.json"), ctx.ReadCustomFormat("ImportableCustomFormat2.json"), @@ -102,7 +101,7 @@ public class GuideProcessorTest } }; - await guideProcessor.BuildGuideDataAsync(config, null, guideService); + await guideProcessor.BuildGuideDataAsync(config, null, default!); var expectedProcessedCustomFormatData = new List { diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Guide/LocalRepoSonarrGuideServiceTest.cs b/src/Recyclarr.TrashLib.Tests/Services/GuideServiceTest.cs similarity index 79% rename from src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Guide/LocalRepoSonarrGuideServiceTest.cs rename to src/Recyclarr.TrashLib.Tests/Services/GuideServiceTest.cs index 0da7de0c..3c8966ad 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Guide/LocalRepoSonarrGuideServiceTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Services/GuideServiceTest.cs @@ -9,20 +9,20 @@ using NUnit.Framework; using Recyclarr.TestLibrary; using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TrashLib.Repo; -using Recyclarr.TrashLib.Services.Sonarr; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile.Guide; -namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Guide; +namespace Recyclarr.TrashLib.Tests.Services; [TestFixture] [Parallelizable(ParallelScope.All)] -public class LocalRepoSonarrGuideServiceTest +public class GuideServiceTest { [Test, AutoMockData] - public void Get_custom_format_json_works( + public void Get_release_profile_json_works( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, - [Frozen] IRepoPaths repoPaths, - LocalRepoSonarrGuideService sut) + [Frozen] IRepoMetadataBuilder metadataBuilder, + ReleaseProfileGuideService sut) { static ReleaseProfileData MakeMockObject(string term) { @@ -50,7 +50,7 @@ public class LocalRepoSonarrGuideServiceTest fs.AddFile(baseDir.File("first.json").FullName, MockFileData(mockData1)); fs.AddFile(baseDir.File("second.json").FullName, MockFileData(mockData2)); - repoPaths.SonarrReleaseProfilePaths.Returns(new[] {baseDir}); + metadataBuilder.ToDirectoryInfoList(default!).ReturnsForAnyArgs(new[] {baseDir}); var results = sut.GetReleaseProfileData(); @@ -64,8 +64,8 @@ public class LocalRepoSonarrGuideServiceTest [Test, AutoMockData] public void Json_exceptions_do_not_interrupt_parsing_other_files( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, - [Frozen] IRepoPaths repoPaths, - LocalRepoSonarrGuideService sut) + [Frozen] IRepoMetadataBuilder metadataBuilder, + ReleaseProfileGuideService sut) { var rootPath = fs.CurrentDirectory().SubDirectory("files"); rootPath.Create(); @@ -84,7 +84,7 @@ public class LocalRepoSonarrGuideServiceTest fs.AddFile(rootPath.File("0_bad_data.json").FullName, MockData.FromString(badData)); fs.AddFile(rootPath.File("1_good_data.json").FullName, MockData.FromJson(goodData)); - repoPaths.SonarrReleaseProfilePaths.Returns(new[] {rootPath}); + metadataBuilder.ToDirectoryInfoList(default!).ReturnsForAnyArgs(new[] {rootPath}); var results = sut.GetReleaseProfileData(); diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrGuideDataListerTest.cs b/src/Recyclarr.TrashLib.Tests/Services/ReleaseProfileDataListerTest.cs similarity index 72% rename from src/Recyclarr.TrashLib.Tests/Sonarr/SonarrGuideDataListerTest.cs rename to src/Recyclarr.TrashLib.Tests/Services/ReleaseProfileDataListerTest.cs index 3fd9d0c8..3ff18aa0 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrGuideDataListerTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Services/ReleaseProfileDataListerTest.cs @@ -3,21 +3,21 @@ using FluentAssertions; using NSubstitute; using NUnit.Framework; using Recyclarr.TestLibrary.AutoFixture; -using Recyclarr.TrashLib.Services.Sonarr; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile.Guide; using Spectre.Console.Testing; -namespace Recyclarr.TrashLib.Tests.Sonarr; +namespace Recyclarr.TrashLib.Tests.Services; [TestFixture] [Parallelizable(ParallelScope.All)] -public class SonarrGuideDataListerTest +public class ReleaseProfileDataListerTest { [Test, AutoMockData] public void Release_profiles_appear_in_console_output( - [Frozen] SonarrGuideService guide, [Frozen(Matching.ImplementedInterfaces)] TestConsole console, - SonarrGuideDataLister sut) + [Frozen] IReleaseProfileGuideService guide, + ReleaseProfileDataLister sut) { var testData = new[] { @@ -35,9 +35,9 @@ public class SonarrGuideDataListerTest [Test, AutoMockData] public void Terms_appear_in_console_output( - [Frozen] SonarrGuideService guide, + [Frozen] IReleaseProfileGuideService guide, [Frozen(Matching.ImplementedInterfaces)] TestConsole console, - SonarrGuideDataLister sut) + ReleaseProfileDataLister sut) { var requiredData = new[] { @@ -57,15 +57,18 @@ public class SonarrGuideDataListerTest new TermData {Name = "Sixth", TrashId = "666", Term = "term6"} }; - guide.GetUnfilteredProfileById(default!).ReturnsForAnyArgs(new ReleaseProfileData + guide.GetReleaseProfileData().Returns(new[] { - Name = "Release Profile", - TrashId = "098", - Required = requiredData, - Ignored = ignoredData, - Preferred = new PreferredTermData[] + new ReleaseProfileData { - new() {Score = 100, Terms = preferredData} + Name = "Release Profile", + TrashId = "098", + Required = requiredData, + Ignored = ignoredData, + Preferred = new PreferredTermData[] + { + new() {Score = 100, Terms = preferredData} + } } }); @@ -78,7 +81,6 @@ public class SonarrGuideDataListerTest preferredData.SelectMany(x => new[] {x.Name, x.TrashId}) }; - guide.Received().GetUnfilteredProfileById("098"); console.Output.Should().ContainAll(expectedOutput.SelectMany(x => x)); } } diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataFiltererTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataFiltererTest.cs index 203496ba..8d05a28b 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataFiltererTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataFiltererTest.cs @@ -1,9 +1,9 @@ using FluentAssertions; using NUnit.Framework; using Recyclarr.TestLibrary.AutoFixture; +using Recyclarr.TrashLib.Services.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile.Filters; using Recyclarr.TrashLib.Services.Sonarr.Config; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters; diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataValidationFiltererTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataValidationFiltererTest.cs index abf520f7..967e3790 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataValidationFiltererTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataValidationFiltererTest.cs @@ -1,8 +1,8 @@ using FluentAssertions; using NUnit.Framework; using Recyclarr.TestLibrary.AutoFixture; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; +using Recyclarr.TrashLib.Services.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters; diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/StrictNegativeScoresFilterTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/StrictNegativeScoresFilterTest.cs index ef0ae845..83a4e21b 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/StrictNegativeScoresFilterTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/Filters/StrictNegativeScoresFilterTest.cs @@ -1,9 +1,9 @@ using FluentAssertions; using NUnit.Framework; using Recyclarr.TestLibrary.AutoFixture; +using Recyclarr.TrashLib.Services.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile.Filters; using Recyclarr.TrashLib.Services.Sonarr.Config; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters; diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/ReleaseProfileDataValidatorTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/ReleaseProfileDataValidatorTest.cs index 015cacb6..57033eec 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/ReleaseProfileDataValidatorTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/ReleaseProfileDataValidatorTest.cs @@ -1,7 +1,7 @@ using FluentAssertions; using FluentValidation.TestHelper; using NUnit.Framework; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile; diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/ScopedStateTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/ScopedStateTest.cs index f13bdd20..4ea0446f 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/ScopedStateTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/ReleaseProfile/ScopedStateTest.cs @@ -1,6 +1,6 @@ using FluentAssertions; using NUnit.Framework; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +using Recyclarr.TrashLib.Services.ReleaseProfile; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile; diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs index b4769be8..91b826f0 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs @@ -7,8 +7,8 @@ using NSubstitute; using NUnit.Framework; using Recyclarr.Cli.TestLibrary; using Recyclarr.TestLibrary; -using Recyclarr.TrashLib.Services.Sonarr.Api; -using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; using Recyclarr.TrashLib.Services.Sonarr.Capabilities; namespace Recyclarr.TrashLib.Tests.Sonarr; diff --git a/src/Recyclarr.TrashLib/Repo/IRepoMetadataBuilder.cs b/src/Recyclarr.TrashLib/Repo/IRepoMetadataBuilder.cs new file mode 100644 index 00000000..147aa69c --- /dev/null +++ b/src/Recyclarr.TrashLib/Repo/IRepoMetadataBuilder.cs @@ -0,0 +1,10 @@ +using System.IO.Abstractions; + +namespace Recyclarr.TrashLib.Repo; + +public interface IRepoMetadataBuilder +{ + RepoMetadata GetMetadata(); + IReadOnlyList ToDirectoryInfoList(IEnumerable listOfDirectories); + IDirectoryInfo DocsDirectory { get; } +} diff --git a/src/Recyclarr.TrashLib/Repo/IRepoPaths.cs b/src/Recyclarr.TrashLib/Repo/IRepoPaths.cs deleted file mode 100644 index 217a2516..00000000 --- a/src/Recyclarr.TrashLib/Repo/IRepoPaths.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.IO.Abstractions; - -namespace Recyclarr.TrashLib.Repo; - -public interface IRepoPaths -{ - IReadOnlyCollection RadarrCustomFormatPaths { get; } - IReadOnlyCollection SonarrReleaseProfilePaths { get; } - IReadOnlyCollection SonarrQualityPaths { get; } - IReadOnlyCollection RadarrQualityPaths { get; } - IReadOnlyCollection SonarrCustomFormatPaths { get; } - IFileInfo RadarrCollectionOfCustomFormats { get; } - IFileInfo SonarrCollectionOfCustomFormats { get; } -} diff --git a/src/Recyclarr.TrashLib/Repo/IRepoPathsFactory.cs b/src/Recyclarr.TrashLib/Repo/IRepoPathsFactory.cs deleted file mode 100644 index c2560523..00000000 --- a/src/Recyclarr.TrashLib/Repo/IRepoPathsFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Recyclarr.TrashLib.Repo; - -public interface IRepoPathsFactory -{ - IRepoPaths Create(); - RepoMetadata Metadata { get; } -} diff --git a/src/Recyclarr.TrashLib/Repo/RepoAutofacModule.cs b/src/Recyclarr.TrashLib/Repo/RepoAutofacModule.cs index 1e891905..b2ae5156 100644 --- a/src/Recyclarr.TrashLib/Repo/RepoAutofacModule.cs +++ b/src/Recyclarr.TrashLib/Repo/RepoAutofacModule.cs @@ -9,6 +9,6 @@ public class RepoAutofacModule : Module base.Load(builder); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); } } diff --git a/src/Recyclarr.TrashLib/Repo/RepoMetadata.cs b/src/Recyclarr.TrashLib/Repo/RepoMetadata.cs index 8b1f275b..f44e23ac 100644 --- a/src/Recyclarr.TrashLib/Repo/RepoMetadata.cs +++ b/src/Recyclarr.TrashLib/Repo/RepoMetadata.cs @@ -1,21 +1,25 @@ namespace Recyclarr.TrashLib.Repo; -public record RadarrMetadata( - IReadOnlyCollection CustomFormats, - IReadOnlyCollection Qualities -); +public record RadarrMetadata +{ + public IReadOnlyCollection CustomFormats { get; init; } = Array.Empty(); + public IReadOnlyCollection Qualities { get; init; } = Array.Empty(); +} -public record SonarrMetadata( - IReadOnlyCollection ReleaseProfiles, - IReadOnlyCollection Qualities, - IReadOnlyCollection CustomFormats -); +public record SonarrMetadata +{ + public IReadOnlyCollection ReleaseProfiles { get; init; } = Array.Empty(); + public IReadOnlyCollection Qualities { get; init; } = Array.Empty(); + public IReadOnlyCollection CustomFormats { get; init; } = Array.Empty(); +} -public record JsonPaths( - RadarrMetadata Radarr, - SonarrMetadata Sonarr -); +public record JsonPaths +{ + public RadarrMetadata Radarr { get; init; } = new(); + public SonarrMetadata Sonarr { get; init; } = new(); +} -public record RepoMetadata( - JsonPaths JsonPaths -); +public record RepoMetadata +{ + public JsonPaths JsonPaths { get; init; } = new(); +} diff --git a/src/Recyclarr.TrashLib/Repo/RepoMetadataBuilder.cs b/src/Recyclarr.TrashLib/Repo/RepoMetadataBuilder.cs new file mode 100644 index 00000000..3c6c48bc --- /dev/null +++ b/src/Recyclarr.TrashLib/Repo/RepoMetadataBuilder.cs @@ -0,0 +1,30 @@ +using System.IO.Abstractions; +using Recyclarr.TrashLib.Startup; + +namespace Recyclarr.TrashLib.Repo; + +public class RepoMetadataBuilder : IRepoMetadataBuilder +{ + private readonly IAppPaths _paths; + private readonly Lazy _metadata; + + public RepoMetadataBuilder( + IRepoMetadataParser parser, + IAppPaths paths) + { + _paths = paths; + _metadata = new Lazy(parser.Deserialize); + } + + public IReadOnlyList ToDirectoryInfoList(IEnumerable listOfDirectories) + { + return listOfDirectories.Select(x => _paths.RepoDirectory.SubDirectory(x)).ToList(); + } + + public IDirectoryInfo DocsDirectory => _paths.RepoDirectory.SubDirectory("docs"); + + public RepoMetadata GetMetadata() + { + return _metadata.Value; + } +} diff --git a/src/Recyclarr.TrashLib/Repo/RepoPaths.cs b/src/Recyclarr.TrashLib/Repo/RepoPaths.cs deleted file mode 100644 index ee6909b5..00000000 --- a/src/Recyclarr.TrashLib/Repo/RepoPaths.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.IO.Abstractions; - -namespace Recyclarr.TrashLib.Repo; - -public record RepoPaths( - IReadOnlyCollection RadarrCustomFormatPaths, - IReadOnlyCollection SonarrReleaseProfilePaths, - IReadOnlyCollection RadarrQualityPaths, - IReadOnlyCollection SonarrQualityPaths, - IReadOnlyCollection SonarrCustomFormatPaths, - IFileInfo RadarrCollectionOfCustomFormats, - IFileInfo SonarrCollectionOfCustomFormats -) : IRepoPaths; diff --git a/src/Recyclarr.TrashLib/Repo/RepoPathsFactory.cs b/src/Recyclarr.TrashLib/Repo/RepoPathsFactory.cs deleted file mode 100644 index 27e783a8..00000000 --- a/src/Recyclarr.TrashLib/Repo/RepoPathsFactory.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.IO.Abstractions; -using Recyclarr.TrashLib.Startup; - -namespace Recyclarr.TrashLib.Repo; - -public class RepoPathsFactory : IRepoPathsFactory -{ - private readonly IAppPaths _paths; - private readonly Lazy _metadata; - - public RepoMetadata Metadata => _metadata.Value; - - public RepoPathsFactory(IRepoMetadataParser parser, IAppPaths paths) - { - _paths = paths; - _metadata = new Lazy(parser.Deserialize); - } - - private List ToDirectoryInfoList(IEnumerable listOfDirectories) - { - return listOfDirectories - .Select(x => _paths.RepoDirectory.SubDirectory(x)) - .ToList(); - } - - public IRepoPaths Create() - { - var docs = _paths.RepoDirectory.SubDirectory("docs"); - var metadata = _metadata.Value; - return new RepoPaths( - ToDirectoryInfoList(metadata.JsonPaths.Radarr.CustomFormats), - ToDirectoryInfoList(metadata.JsonPaths.Sonarr.ReleaseProfiles), - ToDirectoryInfoList(metadata.JsonPaths.Radarr.Qualities), - ToDirectoryInfoList(metadata.JsonPaths.Sonarr.Qualities), - ToDirectoryInfoList(metadata.JsonPaths.Sonarr.CustomFormats), - docs.SubDirectory("Radarr").File("Radarr-collection-of-custom-formats.md"), - docs.SubDirectory("Sonarr").File("sonarr-collection-of-custom-formats.md") - ); - } -} diff --git a/src/Recyclarr.TrashLib/Services/Common/GuideDataLister.cs b/src/Recyclarr.TrashLib/Services/Common/GuideDataLister.cs deleted file mode 100644 index bede1a1f..00000000 --- a/src/Recyclarr.TrashLib/Services/Common/GuideDataLister.cs +++ /dev/null @@ -1,54 +0,0 @@ -using MoreLinq; -using Recyclarr.TrashLib.Services.CustomFormat.Models; -using Recyclarr.TrashLib.Services.QualitySize; -using Spectre.Console; - -namespace Recyclarr.TrashLib.Services.Common; - -public class GuideDataLister : IGuideDataLister -{ - private readonly IAnsiConsole _console; - - public GuideDataLister(IAnsiConsole console) - { - _console = console; - } - - public void ListCustomFormats(IEnumerable customFormats) - { - _console.WriteLine("\nList of Custom Formats in the TRaSH Guides:"); - - var categories = customFormats - .OrderBy(x => x.Name) - .ToLookup(x => x.Category) - .OrderBy(x => x.Key); - - foreach (var cat in categories) - { - var title = cat.Key is not null ? $"{cat.Key}" : "[No Category]"; - _console.WriteLine($"\n # {title}"); - - foreach (var cf in cat) - { - _console.WriteLine($" - {cf.TrashId} # {cf.Name}"); - } - } - - _console.WriteLine( - "\nThe above Custom Formats are in YAML format and ready to be copied & pasted " + - "under the `trash_ids:` property."); - } - - public void ListQualities(IEnumerable qualityData) - { - _console.WriteLine("\nList of Quality Definition types in the TRaSH Guides:\n"); - - qualityData - .Select(x => x.Type) - .ForEach(x => _console.WriteLine($" - {x}")); - - _console.WriteLine( - "\nThe above quality definition types can be used with the `quality_definition:` property in your " + - "recyclarr.yml file."); - } -} diff --git a/src/Recyclarr.TrashLib/Services/Common/GuideServicesAutofacModule.cs b/src/Recyclarr.TrashLib/Services/Common/GuideServicesAutofacModule.cs deleted file mode 100644 index ec863597..00000000 --- a/src/Recyclarr.TrashLib/Services/Common/GuideServicesAutofacModule.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Autofac; - -namespace Recyclarr.TrashLib.Services.Common; - -public class GuideServicesAutofacModule : Module -{ - protected override void Load(ContainerBuilder builder) - { - base.Load(builder); - builder.RegisterType().As(); - } -} diff --git a/src/Recyclarr.TrashLib/Services/Common/IGuideDataLister.cs b/src/Recyclarr.TrashLib/Services/Common/IGuideDataLister.cs deleted file mode 100644 index aee73df0..00000000 --- a/src/Recyclarr.TrashLib/Services/Common/IGuideDataLister.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Recyclarr.TrashLib.Services.CustomFormat.Models; -using Recyclarr.TrashLib.Services.QualitySize; - -namespace Recyclarr.TrashLib.Services.Common; - -public interface IGuideDataLister -{ - void ListCustomFormats(IEnumerable customFormats); - void ListQualities(IEnumerable qualityData); -} diff --git a/src/Recyclarr.TrashLib/Services/Common/IGuideService.cs b/src/Recyclarr.TrashLib/Services/Common/IGuideService.cs deleted file mode 100644 index 4d6acbbe..00000000 --- a/src/Recyclarr.TrashLib/Services/Common/IGuideService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Recyclarr.TrashLib.Services.CustomFormat.Models; -using Recyclarr.TrashLib.Services.QualitySize; - -namespace Recyclarr.TrashLib.Services.Common; - -public interface IGuideService -{ - ICollection GetCustomFormatData(); - ICollection GetQualities(); -} diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatAutofacModule.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatAutofacModule.cs index e4135367..19968a9c 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatAutofacModule.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatAutofacModule.cs @@ -15,6 +15,7 @@ public class CustomFormatAutofacModule : Module builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs index 59c529c5..15f3b3d1 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs @@ -1,6 +1,6 @@ using Recyclarr.Common.Extensions; +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; -using Recyclarr.TrashLib.Services.Common; using Recyclarr.TrashLib.Services.CustomFormat.Processors; using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; using Spectre.Console; @@ -29,11 +29,11 @@ internal class CustomFormatUpdater : ICustomFormatUpdater _console = console; } - public async Task Process(bool isPreview, IEnumerable configs, IGuideService guideService) + public async Task Process(bool isPreview, IEnumerable configs, SupportedServices serviceType) { _cache.Load(); - await _guideProcessor.BuildGuideDataAsync(configs, _cache.CfCache, guideService); + await _guideProcessor.BuildGuideDataAsync(configs, _cache.CfCache, serviceType); if (!ValidateGuideDataAndCheckShouldProceed()) { diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatDataLister.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatDataLister.cs new file mode 100644 index 00000000..f62799e7 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatDataLister.cs @@ -0,0 +1,41 @@ +using Recyclarr.TrashLib.Config; +using Spectre.Console; + +namespace Recyclarr.TrashLib.Services.CustomFormat.Guide; + +public class CustomFormatDataLister +{ + private readonly IAnsiConsole _console; + private readonly ICustomFormatGuideService _guide; + + public CustomFormatDataLister(IAnsiConsole console, ICustomFormatGuideService guide) + { + _console = console; + _guide = guide; + } + + public void ListCustomFormats(SupportedServices serviceType) + { + _console.WriteLine("\nList of Custom Formats in the TRaSH Guides:"); + + var categories = _guide.GetCustomFormatData(serviceType) + .OrderBy(x => x.Name) + .ToLookup(x => x.Category) + .OrderBy(x => x.Key); + + foreach (var cat in categories) + { + var title = cat.Key is not null ? $"{cat.Key}" : "[No Category]"; + _console.WriteLine($"\n # {title}"); + + foreach (var cf in cat) + { + _console.WriteLine($" - {cf.TrashId} # {cf.Name}"); + } + } + + _console.WriteLine( + "\nThe above Custom Formats are in YAML format and ready to be copied & pasted " + + "under the `trash_ids:` property."); + } +} diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatGuideService.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatGuideService.cs new file mode 100644 index 00000000..e0579815 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatGuideService.cs @@ -0,0 +1,46 @@ +using System.IO.Abstractions; +using Recyclarr.TrashLib.Config; +using Recyclarr.TrashLib.Repo; +using Recyclarr.TrashLib.Services.CustomFormat.Models; + +namespace Recyclarr.TrashLib.Services.CustomFormat.Guide; + +public class CustomFormatGuideService : ICustomFormatGuideService +{ + private readonly IRepoMetadataBuilder _metadataBuilder; + private readonly ICustomFormatLoader _cfLoader; + + public CustomFormatGuideService( + IRepoMetadataBuilder metadataBuilder, + ICustomFormatLoader cfLoader) + { + _metadataBuilder = metadataBuilder; + _cfLoader = cfLoader; + } + + private CustomFormatPaths CreatePaths(SupportedServices serviceType) + { + var metadata = _metadataBuilder.GetMetadata(); + return serviceType switch + { + SupportedServices.Radarr => new CustomFormatPaths( + _metadataBuilder.ToDirectoryInfoList(metadata.JsonPaths.Radarr.CustomFormats), + _metadataBuilder.DocsDirectory.SubDirectory("Radarr").File("Radarr-collection-of-custom-formats.md") + ), + SupportedServices.Sonarr => new CustomFormatPaths( + _metadataBuilder.ToDirectoryInfoList(metadata.JsonPaths.Sonarr.CustomFormats), + _metadataBuilder.DocsDirectory.SubDirectory("Sonarr").File("sonarr-collection-of-custom-formats.md") + ), + _ => throw new ArgumentOutOfRangeException(nameof(serviceType), serviceType, null) + }; + } + + public ICollection GetCustomFormatData(SupportedServices serviceType) + { + var paths = CreatePaths(serviceType); + + return _cfLoader.LoadAllCustomFormatsAtPaths( + paths.CustomFormatDirectories, + paths.CollectionOfCustomFormatsMarkdown); + } +} diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatPaths.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatPaths.cs new file mode 100644 index 00000000..5cae8114 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatPaths.cs @@ -0,0 +1,8 @@ +using System.IO.Abstractions; + +namespace Recyclarr.TrashLib.Services.CustomFormat.Guide; + +internal record CustomFormatPaths( + IReadOnlyList CustomFormatDirectories, + IFileInfo CollectionOfCustomFormatsMarkdown +); diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/ICustomFormatGuideService.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/ICustomFormatGuideService.cs new file mode 100644 index 00000000..9b993d80 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Guide/ICustomFormatGuideService.cs @@ -0,0 +1,9 @@ +using Recyclarr.TrashLib.Config; +using Recyclarr.TrashLib.Services.CustomFormat.Models; + +namespace Recyclarr.TrashLib.Services.CustomFormat.Guide; + +public interface ICustomFormatGuideService +{ + ICollection GetCustomFormatData(SupportedServices serviceType); +} diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs index bcae28da..eaad2498 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs @@ -1,9 +1,9 @@ +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; -using Recyclarr.TrashLib.Services.Common; namespace Recyclarr.TrashLib.Services.CustomFormat; public interface ICustomFormatUpdater { - Task Process(bool isPreview, IEnumerable configs, IGuideService guideService); + Task Process(bool isPreview, IEnumerable configs, SupportedServices serviceType); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideProcessor.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideProcessor.cs index 96efce82..c53eea98 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideProcessor.cs @@ -1,5 +1,6 @@ +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; -using Recyclarr.TrashLib.Services.Common; +using Recyclarr.TrashLib.Services.CustomFormat.Guide; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps; @@ -17,10 +18,14 @@ internal class GuideProcessor : IGuideProcessor { private IList? _guideCustomFormatJson; private readonly IGuideProcessorSteps _steps; + private readonly ICustomFormatGuideService _guide; - public GuideProcessor(IGuideProcessorSteps steps) + public GuideProcessor( + IGuideProcessorSteps steps, + ICustomFormatGuideService guide) { _steps = steps; + _guide = guide; } public IReadOnlyCollection ProcessedCustomFormats @@ -44,10 +49,12 @@ internal class GuideProcessor : IGuideProcessor public IReadOnlyCollection DeletedCustomFormatsInCache => _steps.CustomFormat.DeletedCustomFormatsInCache; - public Task BuildGuideDataAsync(IEnumerable config, CustomFormatCache? cache, - IGuideService guideService) + public Task BuildGuideDataAsync( + IEnumerable config, + CustomFormatCache? cache, + SupportedServices serviceType) { - _guideCustomFormatJson ??= guideService.GetCustomFormatData().ToList(); + _guideCustomFormatJson ??= _guide.GetCustomFormatData(serviceType).ToList(); var listOfConfigs = config.ToList(); diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IGuideProcessor.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IGuideProcessor.cs index 6e5926f4..24e52028 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IGuideProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IGuideProcessor.cs @@ -1,5 +1,5 @@ +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; -using Recyclarr.TrashLib.Services.Common; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; @@ -15,6 +15,8 @@ internal interface IGuideProcessor IReadOnlyCollection DeletedCustomFormatsInCache { get; } IReadOnlyDictionary>> DuplicateScores { get; } - Task BuildGuideDataAsync(IEnumerable config, CustomFormatCache? cache, - IGuideService guideService); + Task BuildGuideDataAsync( + IEnumerable config, + CustomFormatCache? cache, + SupportedServices serviceType); } diff --git a/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs b/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs index 5952c97d..fcf3dad3 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs @@ -1,6 +1,6 @@ +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.QualitySize; -using Recyclarr.TrashLib.Services.Radarr; using Recyclarr.TrashLib.Services.Radarr.Config; namespace Recyclarr.TrashLib.Services.Processors; @@ -10,20 +10,17 @@ public class RadarrProcessor : IServiceProcessor private readonly ILogger _log; private readonly ICustomFormatUpdater _cfUpdater; private readonly IQualitySizeUpdater _qualityUpdater; - private readonly RadarrGuideService _guideService; private readonly RadarrConfiguration _config; public RadarrProcessor( ILogger log, ICustomFormatUpdater cfUpdater, IQualitySizeUpdater qualityUpdater, - RadarrGuideService guideService, RadarrConfiguration config) { _log = log; _cfUpdater = cfUpdater; _qualityUpdater = qualityUpdater; - _guideService = guideService; _config = config; } @@ -33,13 +30,13 @@ public class RadarrProcessor : IServiceProcessor if (_config.QualityDefinition != null) { - await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, _guideService); + await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, SupportedServices.Radarr); didWork = true; } if (_config.CustomFormats.Count > 0) { - await _cfUpdater.Process(settings.Preview, _config.CustomFormats, _guideService); + await _cfUpdater.Process(settings.Preview, _config.CustomFormats, SupportedServices.Radarr); didWork = true; } diff --git a/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs b/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs index f7ae9867..d81b2cc9 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs @@ -1,9 +1,9 @@ +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.QualitySize; -using Recyclarr.TrashLib.Services.Sonarr; +using Recyclarr.TrashLib.Services.ReleaseProfile; using Recyclarr.TrashLib.Services.Sonarr.Capabilities; using Recyclarr.TrashLib.Services.Sonarr.Config; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Services.Processors; @@ -12,7 +12,6 @@ public class SonarrProcessor : IServiceProcessor private readonly ILogger _log; private readonly ICustomFormatUpdater _cfUpdater; private readonly IQualitySizeUpdater _qualityUpdater; - private readonly SonarrGuideService _guideService; private readonly IReleaseProfileUpdater _profileUpdater; private readonly SonarrCapabilityEnforcer _compatibilityEnforcer; private readonly SonarrConfiguration _config; @@ -21,7 +20,6 @@ public class SonarrProcessor : IServiceProcessor ILogger log, ICustomFormatUpdater cfUpdater, IQualitySizeUpdater qualityUpdater, - SonarrGuideService guideService, IReleaseProfileUpdater profileUpdater, SonarrCapabilityEnforcer compatibilityEnforcer, SonarrConfiguration config) @@ -29,7 +27,6 @@ public class SonarrProcessor : IServiceProcessor _log = log; _cfUpdater = cfUpdater; _qualityUpdater = qualityUpdater; - _guideService = guideService; _profileUpdater = profileUpdater; _compatibilityEnforcer = compatibilityEnforcer; _config = config; @@ -50,13 +47,13 @@ public class SonarrProcessor : IServiceProcessor if (_config.QualityDefinition != null) { - await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, _guideService); + await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, SupportedServices.Sonarr); didWork = true; } if (_config.CustomFormats.Count > 0) { - await _cfUpdater.Process(settings.Preview, _config.CustomFormats, _guideService); + await _cfUpdater.Process(settings.Preview, _config.CustomFormats, SupportedServices.Sonarr); didWork = true; } diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/Guide/IQualityGuideService.cs b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/IQualityGuideService.cs new file mode 100644 index 00000000..5ee46d43 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/IQualityGuideService.cs @@ -0,0 +1,8 @@ +using Recyclarr.TrashLib.Config; + +namespace Recyclarr.TrashLib.Services.QualitySize.Guide; + +public interface IQualityGuideService +{ + IReadOnlyList GetQualitySizeData(SupportedServices serviceType); +} diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualityGuideService.cs b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualityGuideService.cs new file mode 100644 index 00000000..46eaa464 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualityGuideService.cs @@ -0,0 +1,39 @@ +using Recyclarr.TrashLib.Config; +using Recyclarr.TrashLib.Repo; + +namespace Recyclarr.TrashLib.Services.QualitySize.Guide; + +public class QualityGuideService : IQualityGuideService +{ + private readonly IRepoMetadataBuilder _metadataBuilder; + private readonly QualitySizeGuideParser _parser; + + public QualityGuideService( + IRepoMetadataBuilder metadataBuilder, + QualitySizeGuideParser parser) + { + _metadataBuilder = metadataBuilder; + _parser = parser; + } + + private QualitySizePaths CreatePaths(SupportedServices serviceType) + { + var metadata = _metadataBuilder.GetMetadata(); + return serviceType switch + { + SupportedServices.Radarr => new QualitySizePaths( + _metadataBuilder.ToDirectoryInfoList(metadata.JsonPaths.Radarr.Qualities) + ), + SupportedServices.Sonarr => new QualitySizePaths( + _metadataBuilder.ToDirectoryInfoList(metadata.JsonPaths.Sonarr.Qualities) + ), + _ => throw new ArgumentOutOfRangeException(nameof(serviceType), serviceType, null) + }; + } + + public IReadOnlyList GetQualitySizeData(SupportedServices serviceType) + { + var paths = CreatePaths(serviceType); + return _parser.GetQualities(paths.QualitySizeDirectories); + } +} diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizeDataLister.cs b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizeDataLister.cs new file mode 100644 index 00000000..842ba0d1 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizeDataLister.cs @@ -0,0 +1,32 @@ +using MoreLinq; +using Recyclarr.TrashLib.Config; +using Spectre.Console; + +namespace Recyclarr.TrashLib.Services.QualitySize.Guide; + +public class QualitySizeDataLister +{ + private readonly IAnsiConsole _console; + private readonly IQualityGuideService _guide; + + public QualitySizeDataLister( + IAnsiConsole console, + IQualityGuideService guide) + { + _console = console; + _guide = guide; + } + + public void ListQualities(SupportedServices serviceType) + { + _console.WriteLine("\nList of Quality Definition types in the TRaSH Guides:\n"); + + _guide.GetQualitySizeData(serviceType) + .Select(x => x.Type) + .ForEach(x => _console.WriteLine($" - {x}")); + + _console.WriteLine( + "\nThe above quality definition types can be used with the `quality_definition:` property in your " + + "recyclarr.yml file."); + } +} diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizeGuideParser.cs b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizeGuideParser.cs index 58dc540d..1941c1dd 100644 --- a/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizeGuideParser.cs +++ b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizeGuideParser.cs @@ -6,7 +6,7 @@ using Recyclarr.Common.Extensions; namespace Recyclarr.TrashLib.Services.QualitySize.Guide; -internal class QualitySizeGuideParser where T : class +public class QualitySizeGuideParser { private readonly ILogger _log; @@ -15,7 +15,7 @@ internal class QualitySizeGuideParser where T : class _log = log; } - public ICollection GetQualities(IEnumerable jsonDirectories) + public IReadOnlyList GetQualities(IEnumerable jsonDirectories) { return JsonUtils.GetJsonFilesInDirectories(jsonDirectories, _log) .Select(ParseQuality) @@ -23,7 +23,7 @@ internal class QualitySizeGuideParser where T : class .ToList(); } - private T? ParseQuality(IFileInfo jsonFile) + private QualitySizeData? ParseQuality(IFileInfo jsonFile) { var serializer = JsonSerializer.Create(new JsonSerializerSettings { @@ -34,7 +34,7 @@ internal class QualitySizeGuideParser where T : class }); using var json = new JsonTextReader(jsonFile.OpenText()); - var quality = serializer.Deserialize(json); + var quality = serializer.Deserialize(json); if (quality is null) { _log.Debug("Failed to parse quality definition JSON file: {Filename}", jsonFile.FullName); diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizePaths.cs b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizePaths.cs new file mode 100644 index 00000000..b90cd1ff --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/QualitySize/Guide/QualitySizePaths.cs @@ -0,0 +1,7 @@ +using System.IO.Abstractions; + +namespace Recyclarr.TrashLib.Services.QualitySize.Guide; + +internal record QualitySizePaths( + IReadOnlyCollection QualitySizeDirectories +); diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs b/src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs index ff14273c..b13b7e9a 100644 --- a/src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs @@ -1,9 +1,9 @@ +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; -using Recyclarr.TrashLib.Services.Common; namespace Recyclarr.TrashLib.Services.QualitySize; public interface IQualitySizeUpdater { - Task Process(bool isPreview, QualityDefinitionConfig config, IGuideService guideService); + Task Process(bool isPreview, QualityDefinitionConfig config, SupportedServices serviceType); } diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeAutofacModule.cs b/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeAutofacModule.cs new file mode 100644 index 00000000..19e7b057 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeAutofacModule.cs @@ -0,0 +1,16 @@ +using Autofac; +using Recyclarr.TrashLib.Services.QualitySize.Api; +using Recyclarr.TrashLib.Services.QualitySize.Guide; + +namespace Recyclarr.TrashLib.Services.QualitySize; + +public class QualitySizeAutofacModule : Module +{ + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType(); + } +} diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs b/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs index 1be14090..2fddac2e 100644 --- a/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs @@ -1,7 +1,8 @@ using Recyclarr.Common.Extensions; +using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; -using Recyclarr.TrashLib.Services.Common; using Recyclarr.TrashLib.Services.QualitySize.Api; +using Recyclarr.TrashLib.Services.QualitySize.Guide; using Spectre.Console; namespace Recyclarr.TrashLib.Services.QualitySize; @@ -11,21 +12,24 @@ internal class QualitySizeUpdater : IQualitySizeUpdater private readonly ILogger _log; private readonly IQualityDefinitionService _api; private readonly IAnsiConsole _console; + private readonly IQualityGuideService _guide; public QualitySizeUpdater( ILogger logger, IQualityDefinitionService api, - IAnsiConsole console) + IAnsiConsole console, + IQualityGuideService guide) { _log = logger; _api = api; _console = console; + _guide = guide; } - public async Task Process(bool isPreview, QualityDefinitionConfig config, IGuideService guideService) + public async Task Process(bool isPreview, QualityDefinitionConfig config, SupportedServices serviceType) { _log.Information("Processing Quality Definition: {QualityDefinition}", config.Type); - var qualityDefinitions = guideService.GetQualities(); + var qualityDefinitions = _guide.GetQualitySizeData(serviceType); var qualityTypeInConfig = config.Type; var selectedQuality = qualityDefinitions diff --git a/src/Recyclarr.TrashLib/Services/Radarr/IRadarrGuideDataLister.cs b/src/Recyclarr.TrashLib/Services/Radarr/IRadarrGuideDataLister.cs deleted file mode 100644 index 3d1c02ff..00000000 --- a/src/Recyclarr.TrashLib/Services/Radarr/IRadarrGuideDataLister.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Recyclarr.TrashLib.Services.Radarr; - -public interface IRadarrGuideDataLister -{ - void ListCustomFormats(); - void ListQualities(); -} diff --git a/src/Recyclarr.TrashLib/Services/Radarr/LocalRepoRadarrGuideService.cs b/src/Recyclarr.TrashLib/Services/Radarr/LocalRepoRadarrGuideService.cs deleted file mode 100644 index 606b53f5..00000000 --- a/src/Recyclarr.TrashLib/Services/Radarr/LocalRepoRadarrGuideService.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Recyclarr.TrashLib.Repo; -using Recyclarr.TrashLib.Services.CustomFormat.Guide; -using Recyclarr.TrashLib.Services.CustomFormat.Models; -using Recyclarr.TrashLib.Services.QualitySize; -using Recyclarr.TrashLib.Services.QualitySize.Guide; - -namespace Recyclarr.TrashLib.Services.Radarr; - -public class LocalRepoRadarrGuideService : RadarrGuideService -{ - private readonly IRepoPathsFactory _pathsFactory; - private readonly ICustomFormatLoader _cfLoader; - private readonly QualitySizeGuideParser _parser; - - public LocalRepoRadarrGuideService(IRepoPathsFactory pathsFactory, ILogger log, ICustomFormatLoader cfLoader) - { - _pathsFactory = pathsFactory; - _cfLoader = cfLoader; - _parser = new QualitySizeGuideParser(log); - } - - public override ICollection GetQualities() - { - return _parser.GetQualities(_pathsFactory.Create().RadarrQualityPaths); - } - - public override ICollection GetCustomFormatData() - { - var paths = _pathsFactory.Create(); - return _cfLoader.LoadAllCustomFormatsAtPaths( - paths.RadarrCustomFormatPaths, - paths.RadarrCollectionOfCustomFormats); - } -} diff --git a/src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs b/src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs index 09c35af4..286c3748 100644 --- a/src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs +++ b/src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs @@ -1,8 +1,4 @@ using Autofac; -using Recyclarr.TrashLib.Config; -using Recyclarr.TrashLib.Services.Common; -using Recyclarr.TrashLib.Services.QualitySize; -using Recyclarr.TrashLib.Services.QualitySize.Api; namespace Recyclarr.TrashLib.Services.Radarr; @@ -10,14 +6,6 @@ public class RadarrAutofacModule : Module { protected override void Load(ContainerBuilder builder) { - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - - builder.RegisterType() - .As() - .Keyed(SupportedServices.Radarr); - builder.RegisterType().As() .InstancePerLifetimeScope(); } diff --git a/src/Recyclarr.TrashLib/Services/Radarr/RadarrGuideDataLister.cs b/src/Recyclarr.TrashLib/Services/Radarr/RadarrGuideDataLister.cs deleted file mode 100644 index 5a635996..00000000 --- a/src/Recyclarr.TrashLib/Services/Radarr/RadarrGuideDataLister.cs +++ /dev/null @@ -1,29 +0,0 @@ -using JetBrains.Annotations; -using Recyclarr.TrashLib.Services.Common; - -namespace Recyclarr.TrashLib.Services.Radarr; - -[UsedImplicitly] -public class RadarrGuideDataLister : IRadarrGuideDataLister -{ - private readonly RadarrGuideService _guide; - private readonly IGuideDataLister _guideLister; - - public RadarrGuideDataLister( - RadarrGuideService guide, - IGuideDataLister guideLister) - { - _guide = guide; - _guideLister = guideLister; - } - - public void ListCustomFormats() - { - _guideLister.ListCustomFormats(_guide.GetCustomFormatData()); - } - - public void ListQualities() - { - _guideLister.ListQualities(_guide.GetQualities()); - } -} diff --git a/src/Recyclarr.TrashLib/Services/Radarr/RadarrGuideService.cs b/src/Recyclarr.TrashLib/Services/Radarr/RadarrGuideService.cs deleted file mode 100644 index 2fa96086..00000000 --- a/src/Recyclarr.TrashLib/Services/Radarr/RadarrGuideService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Recyclarr.TrashLib.Services.Common; -using Recyclarr.TrashLib.Services.CustomFormat.Models; -using Recyclarr.TrashLib.Services.QualitySize; - -namespace Recyclarr.TrashLib.Services.Radarr; - -public abstract class RadarrGuideService : IGuideService -{ - public abstract ICollection GetCustomFormatData(); - public abstract ICollection GetQualities(); -} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/IReleaseProfileApiService.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/IReleaseProfileApiService.cs similarity index 71% rename from src/Recyclarr.TrashLib/Services/Sonarr/Api/IReleaseProfileApiService.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/IReleaseProfileApiService.cs index e0a81bee..f14cdffa 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/IReleaseProfileApiService.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/IReleaseProfileApiService.cs @@ -1,6 +1,6 @@ -using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; -namespace Recyclarr.TrashLib.Services.Sonarr.Api; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api; public interface IReleaseProfileApiService { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrReleaseProfileCompatibilityHandler.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs similarity index 67% rename from src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrReleaseProfileCompatibilityHandler.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs index 4332c49b..d1a9da94 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrReleaseProfileCompatibilityHandler.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json.Linq; -using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; -namespace Recyclarr.TrashLib.Services.Sonarr.Api; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api; public interface ISonarrReleaseProfileCompatibilityHandler { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/Mappings/SonarrApiObjectMappingProfile.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Mappings/SonarrApiObjectMappingProfile.cs similarity index 86% rename from src/Recyclarr.TrashLib/Services/Sonarr/Api/Mappings/SonarrApiObjectMappingProfile.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Mappings/SonarrApiObjectMappingProfile.cs index 51bac83e..0f6692ae 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/Mappings/SonarrApiObjectMappingProfile.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Mappings/SonarrApiObjectMappingProfile.cs @@ -1,8 +1,8 @@ using AutoMapper; using JetBrains.Annotations; -using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; -namespace Recyclarr.TrashLib.Services.Sonarr.Api.Mappings; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api.Mappings; [UsedImplicitly] public class SonarrApiObjectMappingProfile : Profile diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/Objects/SonarrReleaseProfile.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Objects/SonarrReleaseProfile.cs similarity index 96% rename from src/Recyclarr.TrashLib/Services/Sonarr/Api/Objects/SonarrReleaseProfile.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Objects/SonarrReleaseProfile.cs index 668c122d..419c33c9 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/Objects/SonarrReleaseProfile.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Objects/SonarrReleaseProfile.cs @@ -1,7 +1,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; -namespace Recyclarr.TrashLib.Services.Sonarr.Api.Objects; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; [UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Members)] public class SonarrPreferredTerm diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ReleaseProfileApiService.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ReleaseProfileApiService.cs similarity index 93% rename from src/Recyclarr.TrashLib/Services/Sonarr/Api/ReleaseProfileApiService.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ReleaseProfileApiService.cs index ba18a483..13ad1552 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ReleaseProfileApiService.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ReleaseProfileApiService.cs @@ -1,9 +1,9 @@ using Flurl.Http; using Newtonsoft.Json.Linq; using Recyclarr.TrashLib.Http; -using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; -namespace Recyclarr.TrashLib.Services.Sonarr.Api; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api; public class ReleaseProfileApiService : IReleaseProfileApiService { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/Schemas/SonarrReleaseProfileSchema.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Schemas/SonarrReleaseProfileSchema.cs similarity index 97% rename from src/Recyclarr.TrashLib/Services/Sonarr/Api/Schemas/SonarrReleaseProfileSchema.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Schemas/SonarrReleaseProfileSchema.cs index ae72c7b8..822da445 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/Schemas/SonarrReleaseProfileSchema.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/Schemas/SonarrReleaseProfileSchema.cs @@ -1,4 +1,4 @@ -namespace Recyclarr.TrashLib.Services.Sonarr.Api.Schemas; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api.Schemas; public static class SonarrReleaseProfileSchema { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrReleaseProfileCompatibilityHandler.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs similarity index 92% rename from src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrReleaseProfileCompatibilityHandler.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs index 800685cd..d00afbcf 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrReleaseProfileCompatibilityHandler.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs @@ -2,11 +2,11 @@ using AutoMapper; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; using Recyclarr.TrashLib.ExceptionTypes; -using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; -using Recyclarr.TrashLib.Services.Sonarr.Api.Schemas; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Schemas; using Recyclarr.TrashLib.Services.Sonarr.Capabilities; -namespace Recyclarr.TrashLib.Services.Sonarr.Api; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api; public class SonarrReleaseProfileCompatibilityHandler : ISonarrReleaseProfileCompatibilityHandler { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IReleaseProfileFilter.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IReleaseProfileFilter.cs similarity index 72% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IReleaseProfileFilter.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IReleaseProfileFilter.cs index 9afcfc05..1e0a2f6e 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IReleaseProfileFilter.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IReleaseProfileFilter.cs @@ -1,6 +1,6 @@ using Recyclarr.TrashLib.Services.Sonarr.Config; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters; public interface IReleaseProfileFilter { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IReleaseProfileFilterPipeline.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IReleaseProfileFilterPipeline.cs similarity index 73% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IReleaseProfileFilterPipeline.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IReleaseProfileFilterPipeline.cs index 9fcb1810..735b5bcc 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IReleaseProfileFilterPipeline.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IReleaseProfileFilterPipeline.cs @@ -1,6 +1,6 @@ using Recyclarr.TrashLib.Services.Sonarr.Config; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters; public interface IReleaseProfileFilterPipeline { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IncludeExcludeFilter.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IncludeExcludeFilter.cs similarity index 91% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IncludeExcludeFilter.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IncludeExcludeFilter.cs index 05e19f66..46529829 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/IncludeExcludeFilter.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/IncludeExcludeFilter.cs @@ -1,6 +1,6 @@ using Recyclarr.TrashLib.Services.Sonarr.Config; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters; public class IncludeExcludeFilter : IReleaseProfileFilter { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataFilterer.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileDataFilterer.cs similarity index 97% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataFilterer.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileDataFilterer.cs index d76c48f6..a83e6224 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataFilterer.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileDataFilterer.cs @@ -1,7 +1,7 @@ using System.Collections.ObjectModel; using Recyclarr.TrashLib.Services.Sonarr.Config; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters; public class ReleaseProfileDataFilterer { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataValidationFilterer.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileDataValidationFilterer.cs similarity index 95% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataValidationFilterer.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileDataValidationFilterer.cs index 239e52ca..2873a348 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileDataValidationFilterer.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileDataValidationFilterer.cs @@ -1,7 +1,7 @@ using FluentValidation.Results; using Recyclarr.Common.FluentValidation; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters; public class ReleaseProfileDataValidationFilterer { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileFilterPipeline.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileFilterPipeline.cs similarity index 88% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileFilterPipeline.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileFilterPipeline.cs index 4c6b94a6..196737a4 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/ReleaseProfileFilterPipeline.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/ReleaseProfileFilterPipeline.cs @@ -1,6 +1,6 @@ using Recyclarr.TrashLib.Services.Sonarr.Config; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters; public class ReleaseProfileFilterPipeline : IReleaseProfileFilterPipeline { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/StrictNegativeScoresFilter.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/StrictNegativeScoresFilter.cs similarity index 91% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/StrictNegativeScoresFilter.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/StrictNegativeScoresFilter.cs index b02987c3..fbf2b390 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/Filters/StrictNegativeScoresFilter.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Filters/StrictNegativeScoresFilter.cs @@ -1,6 +1,6 @@ using Recyclarr.TrashLib.Services.Sonarr.Config; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters; public class StrictNegativeScoresFilter : IReleaseProfileFilter { diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/IReleaseProfileGuideService.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/IReleaseProfileGuideService.cs new file mode 100644 index 00000000..6decef66 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/IReleaseProfileGuideService.cs @@ -0,0 +1,6 @@ +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Guide; + +public interface IReleaseProfileGuideService +{ + IReadOnlyList GetReleaseProfileData(); +} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/SonarrGuideDataLister.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileDataLister.cs similarity index 76% rename from src/Recyclarr.TrashLib/Services/Sonarr/SonarrGuideDataLister.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileDataLister.cs index 6499175a..35e7a260 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/SonarrGuideDataLister.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileDataLister.cs @@ -1,36 +1,18 @@ using System.Text; -using JetBrains.Annotations; -using Recyclarr.TrashLib.Services.Common; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +using Recyclarr.Common.Extensions; using Spectre.Console; -namespace Recyclarr.TrashLib.Services.Sonarr; +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Guide; -[UsedImplicitly] -public class SonarrGuideDataLister : ISonarrGuideDataLister +public class ReleaseProfileDataLister { private readonly IAnsiConsole _console; - private readonly SonarrGuideService _guide; - private readonly IGuideDataLister _guideLister; + private readonly IReleaseProfileGuideService _guide; - public SonarrGuideDataLister( - IAnsiConsole console, - SonarrGuideService guide, - IGuideDataLister guideLister) + public ReleaseProfileDataLister(IAnsiConsole console, IReleaseProfileGuideService guide) { _console = console; _guide = guide; - _guideLister = guideLister; - } - - public void ListCustomFormats() - { - _guideLister.ListCustomFormats(_guide.GetCustomFormatData()); - } - - public void ListQualities() - { - _guideLister.ListQualities(_guide.GetQualities()); } public void ListReleaseProfiles() @@ -62,7 +44,9 @@ public class SonarrGuideDataLister : ISonarrGuideDataLister public void ListTerms(string releaseProfileId) { - var profile = _guide.GetUnfilteredProfileById(releaseProfileId); + var profile = _guide.GetReleaseProfileData() + .FirstOrDefault(x => x.TrashId.EqualsIgnoreCase(releaseProfileId)); + if (profile is null) { throw new ArgumentException("No release profile found with that Trash ID"); diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideParser.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideParser.cs new file mode 100644 index 00000000..4daee561 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideParser.cs @@ -0,0 +1,59 @@ +using System.IO.Abstractions; +using MoreLinq; +using Newtonsoft.Json; +using Recyclarr.Common; +using Recyclarr.TrashLib.Services.ReleaseProfile.Filters; +using Serilog; + +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Guide; + +public class ReleaseProfileGuideParser +{ + private readonly ILogger _log; + + public ReleaseProfileGuideParser(ILogger log) + { + _log = log; + } + + private async Task LoadAndParseFile(IFileInfo file, params JsonConverter[] converters) + { + try + { + using var stream = file.OpenText(); + var json = await stream.ReadToEndAsync(); + return JsonConvert.DeserializeObject(json, converters); + } + catch (JsonException e) + { + HandleJsonException(e, file); + } + catch (AggregateException ae) when (ae.InnerException is JsonException e) + { + HandleJsonException(e, file); + } + + return null; + } + + private void HandleJsonException(JsonException exception, IFileInfo file) + { + _log.Warning(exception, + "Failed to parse Sonarr JSON file (This likely indicates a bug that should be " + + "reported in the TRaSH repo): {File}", file.Name); + } + + public IEnumerable GetReleaseProfileData(IEnumerable paths) + { + var converter = new TermDataConverter(); + var tasks = JsonUtils.GetJsonFilesInDirectories(paths, _log) + .Select(x => LoadAndParseFile(x, converter)); + + var data = Task.WhenAll(tasks).Result + // Make non-nullable type and filter out null values + .Choose(x => x is not null ? (true, x) : default); + + var validator = new ReleaseProfileDataValidationFilterer(_log); + return validator.FilterProfiles(data); + } +} diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideService.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideService.cs new file mode 100644 index 00000000..f7dbd201 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideService.cs @@ -0,0 +1,31 @@ +using Recyclarr.TrashLib.Repo; + +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Guide; + +public class ReleaseProfileGuideService : IReleaseProfileGuideService +{ + private readonly IRepoMetadataBuilder _metadataBuilder; + private readonly ReleaseProfileGuideParser _parser; + + public ReleaseProfileGuideService( + IRepoMetadataBuilder metadataBuilder, + ReleaseProfileGuideParser parser) + { + _metadataBuilder = metadataBuilder; + _parser = parser; + } + + private ReleaseProfilePaths GetPaths() + { + var metadata = _metadataBuilder.GetMetadata(); + return new ReleaseProfilePaths( + _metadataBuilder.ToDirectoryInfoList(metadata.JsonPaths.Sonarr.ReleaseProfiles) + ); + } + + public IReadOnlyList GetReleaseProfileData() + { + var paths = GetPaths(); + return _parser.GetReleaseProfileData(paths.ReleaseProfileDirectories).ToList(); + } +} diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfilePaths.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfilePaths.cs new file mode 100644 index 00000000..e6607485 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfilePaths.cs @@ -0,0 +1,7 @@ +using System.IO.Abstractions; + +namespace Recyclarr.TrashLib.Services.ReleaseProfile.Guide; + +public record ReleaseProfilePaths( + IReadOnlyList ReleaseProfileDirectories +); diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/IReleaseProfileUpdater.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/IReleaseProfileUpdater.cs similarity index 72% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/IReleaseProfileUpdater.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/IReleaseProfileUpdater.cs index 7015cce6..81f26110 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/IReleaseProfileUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/IReleaseProfileUpdater.cs @@ -1,6 +1,6 @@ using Recyclarr.TrashLib.Services.Sonarr.Config; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +namespace Recyclarr.TrashLib.Services.ReleaseProfile; public interface IReleaseProfileUpdater { diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileAutofacModule.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileAutofacModule.cs new file mode 100644 index 00000000..aeeb4ec4 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileAutofacModule.cs @@ -0,0 +1,31 @@ +using Autofac; +using Autofac.Extras.Ordering; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api; +using Recyclarr.TrashLib.Services.ReleaseProfile.Filters; +using Recyclarr.TrashLib.Services.ReleaseProfile.Guide; + +namespace Recyclarr.TrashLib.Services.ReleaseProfile; + +public class ReleaseProfileAutofacModule : Module +{ + protected override void Load(ContainerBuilder builder) + { + base.Load(builder); + + builder.RegisterType().As(); + + builder.RegisterType().As(); + builder.RegisterType() + .As(); + builder.RegisterType().As(); + builder.RegisterType(); + builder.RegisterType().As(); + + // Release Profile Filters (ORDER MATTERS!) + builder.RegisterTypes( + typeof(IncludeExcludeFilter), + typeof(StrictNegativeScoresFilter)) + .As() + .OrderByRegistration(); + } +} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileData.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileData.cs similarity index 67% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileData.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileData.cs index 1a8c3981..6f3c13c8 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileData.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileData.cs @@ -1,7 +1,7 @@ using JetBrains.Annotations; using Newtonsoft.Json; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +namespace Recyclarr.TrashLib.Services.ReleaseProfile; [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] public record TermData @@ -11,11 +11,6 @@ public record TermData public string Name { get; init; } = string.Empty; public string Term { get; init; } = string.Empty; - - public override string ToString() - { - return $"[TrashId: {TrashId}] [Name: {Name}] [Term: {Term}]"; - } } [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] @@ -29,11 +24,6 @@ public record PreferredTermData score = Score; terms = Terms; } - - public override string ToString() - { - return $"[Score: {Score}] [Terms: {Terms.Count}]"; - } } [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] @@ -47,14 +37,4 @@ public record ReleaseProfileData public IReadOnlyCollection Required { get; init; } = Array.Empty(); public IReadOnlyCollection Ignored { get; init; } = Array.Empty(); public IReadOnlyCollection Preferred { get; init; } = Array.Empty(); - - public override string ToString() - { - return $"[TrashId: {TrashId}] " + - $"[Name: {Name}] " + - $"[IncludePreferred: {IncludePreferredWhenRenaming}] " + - $"[Required: {Required.Count}] " + - $"[Ignored: {Ignored.Count}] " + - $"[Preferred: {Preferred.Count}]"; - } } diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileDataValidator.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileDataValidator.cs similarity index 93% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileDataValidator.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileDataValidator.cs index c1308d21..3b05d9c8 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileDataValidator.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileDataValidator.cs @@ -1,6 +1,6 @@ using FluentValidation; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +namespace Recyclarr.TrashLib.Services.ReleaseProfile; internal class TermDataValidator : AbstractValidator { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileUpdater.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileUpdater.cs similarity index 93% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileUpdater.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileUpdater.cs index 7f850d18..9f461335 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ReleaseProfileUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileUpdater.cs @@ -1,32 +1,35 @@ using Recyclarr.Common.Extensions; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api; +using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; +using Recyclarr.TrashLib.Services.ReleaseProfile.Filters; +using Recyclarr.TrashLib.Services.ReleaseProfile.Guide; using Recyclarr.TrashLib.Services.Sonarr.Api; using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; using Recyclarr.TrashLib.Services.Sonarr.Config; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; using Spectre.Console; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +namespace Recyclarr.TrashLib.Services.ReleaseProfile; public class ReleaseProfileUpdater : IReleaseProfileUpdater { private readonly IReleaseProfileApiService _releaseProfileApi; private readonly IReleaseProfileFilterPipeline _pipeline; private readonly IAnsiConsole _console; - private readonly SonarrGuideService _guide; - private readonly ISonarrApi _api; + private readonly IReleaseProfileGuideService _guide; + private readonly ISonarrTagApiService _tagApiService; private readonly ILogger _log; public ReleaseProfileUpdater( ILogger logger, - SonarrGuideService guide, - ISonarrApi api, + IReleaseProfileGuideService guide, + ISonarrTagApiService tagApiService, IReleaseProfileApiService releaseProfileApi, IReleaseProfileFilterPipeline pipeline, IAnsiConsole console) { _log = logger; _guide = guide; - _api = api; + _tagApiService = tagApiService; _releaseProfileApi = releaseProfileApi; _pipeline = pipeline; _console = console; @@ -195,7 +198,7 @@ public class ReleaseProfileUpdater : IReleaseProfileUpdater return Array.Empty(); } - var sonarrTags = await _api.GetTags(); + var sonarrTags = await _tagApiService.GetTags(); await CreateMissingTags(sonarrTags, tags); return sonarrTags .Where(t => tags.Any(ct => ct.EqualsIgnoreCase(t.Label))) @@ -209,7 +212,7 @@ public class ReleaseProfileUpdater : IReleaseProfileUpdater foreach (var tag in missingTags) { _log.Debug("Creating Tag: {Tag}", tag); - var newTag = await _api.CreateTag(tag); + var newTag = await _tagApiService.CreateTag(tag); sonarrTags.Add(newTag); } } diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ScopedState.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ScopedState.cs similarity index 95% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ScopedState.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/ScopedState.cs index 1d153073..dbd39b64 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/ScopedState.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ScopedState.cs @@ -1,4 +1,4 @@ -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +namespace Recyclarr.TrashLib.Services.ReleaseProfile; public class ScopedState { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/TermDataConverter.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/TermDataConverter.cs similarity index 92% rename from src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/TermDataConverter.cs rename to src/Recyclarr.TrashLib/Services/ReleaseProfile/TermDataConverter.cs index 478fb7c0..441f6c43 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ReleaseProfile/TermDataConverter.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/TermDataConverter.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; +namespace Recyclarr.TrashLib.Services.ReleaseProfile; internal class TermDataConverter : JsonConverter { diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrApi.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrApi.cs deleted file mode 100644 index 28cfc375..00000000 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrApi.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; - -namespace Recyclarr.TrashLib.Services.Sonarr.Api; - -public interface ISonarrApi -{ - Task> GetTags(); - Task CreateTag(string tag); - Task> GetQualityDefinition(); - - Task> UpdateQualityDefinition( - IReadOnlyCollection newQuality); -} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrTagApiService.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrTagApiService.cs new file mode 100644 index 00000000..e815e335 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrTagApiService.cs @@ -0,0 +1,9 @@ +using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; + +namespace Recyclarr.TrashLib.Services.Sonarr.Api; + +public interface ISonarrTagApiService +{ + Task> GetTags(); + Task CreateTag(string tag); +} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/Objects/SonarrQualityDefinitionItem.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Api/Objects/SonarrQualityDefinitionItem.cs deleted file mode 100644 index 50ac9a0f..00000000 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/Objects/SonarrQualityDefinitionItem.cs +++ /dev/null @@ -1,23 +0,0 @@ -using JetBrains.Annotations; - -namespace Recyclarr.TrashLib.Services.Sonarr.Api.Objects; - -[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] -public class SonarrQualityItem -{ - public int Id { get; set; } - public string Name { get; set; } = ""; - public string Source { get; set; } = ""; - public int Resolution { get; set; } -} - -[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] -public class SonarrQualityDefinitionItem -{ - public int Id { get; set; } - public SonarrQualityItem? Quality { get; set; } - public string Title { get; set; } = ""; - public int Weight { get; set; } - public decimal MinSize { get; set; } - public decimal? MaxSize { get; set; } -} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrApi.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrApi.cs deleted file mode 100644 index e429e0ae..00000000 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrApi.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Flurl.Http; -using Recyclarr.TrashLib.Http; -using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; - -namespace Recyclarr.TrashLib.Services.Sonarr.Api; - -public class SonarrApi : ISonarrApi -{ - private readonly IServiceRequestBuilder _service; - - public SonarrApi(IServiceRequestBuilder service) - { - _service = service; - } - - public async Task> GetTags() - { - return await _service.Request("tag") - .GetJsonAsync>(); - } - - public async Task CreateTag(string tag) - { - return await _service.Request("tag") - .PostJsonAsync(new {label = tag}) - .ReceiveJson(); - } - - public async Task> GetQualityDefinition() - { - return await _service.Request("qualitydefinition") - .GetJsonAsync>(); - } - - public async Task> UpdateQualityDefinition( - IReadOnlyCollection newQuality) - { - return await _service.Request("qualityDefinition", "update") - .PutJsonAsync(newQuality) - .ReceiveJson>(); - } -} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrTagApiService.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrTagApiService.cs new file mode 100644 index 00000000..142f2618 --- /dev/null +++ b/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrTagApiService.cs @@ -0,0 +1,28 @@ +using Flurl.Http; +using Recyclarr.TrashLib.Http; +using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; + +namespace Recyclarr.TrashLib.Services.Sonarr.Api; + +public class SonarrTagApiService : ISonarrTagApiService +{ + private readonly IServiceRequestBuilder _service; + + public SonarrTagApiService(IServiceRequestBuilder service) + { + _service = service; + } + + public async Task> GetTags() + { + return await _service.Request("tag") + .GetJsonAsync>(); + } + + public async Task CreateTag(string tag) + { + return await _service.Request("tag") + .PostJsonAsync(new {label = tag}) + .ReceiveJson(); + } +} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/ISonarrGuideDataLister.cs b/src/Recyclarr.TrashLib/Services/Sonarr/ISonarrGuideDataLister.cs deleted file mode 100644 index 2bcf67a1..00000000 --- a/src/Recyclarr.TrashLib/Services/Sonarr/ISonarrGuideDataLister.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Recyclarr.TrashLib.Services.Sonarr; - -public interface ISonarrGuideDataLister -{ - void ListReleaseProfiles(); - void ListTerms(string releaseProfileId); - void ListQualities(); - void ListCustomFormats(); -} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/LocalRepoSonarrGuideService.cs b/src/Recyclarr.TrashLib/Services/Sonarr/LocalRepoSonarrGuideService.cs deleted file mode 100644 index 9db4423d..00000000 --- a/src/Recyclarr.TrashLib/Services/Sonarr/LocalRepoSonarrGuideService.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.IO.Abstractions; -using MoreLinq; -using Newtonsoft.Json; -using Recyclarr.Common; -using Recyclarr.Common.Extensions; -using Recyclarr.TrashLib.Repo; -using Recyclarr.TrashLib.Services.CustomFormat.Guide; -using Recyclarr.TrashLib.Services.CustomFormat.Models; -using Recyclarr.TrashLib.Services.QualitySize; -using Recyclarr.TrashLib.Services.QualitySize.Guide; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; - -namespace Recyclarr.TrashLib.Services.Sonarr; - -public class LocalRepoSonarrGuideService : SonarrGuideService -{ - private readonly IRepoPathsFactory _pathsFactory; - private readonly ILogger _log; - private readonly ICustomFormatLoader _cfLoader; - private readonly Lazy> _data; - private readonly QualitySizeGuideParser _parser; - - public LocalRepoSonarrGuideService( - IRepoPathsFactory pathsFactory, - ILogger log, - ICustomFormatLoader cfLoader) - { - _pathsFactory = pathsFactory; - _log = log; - _cfLoader = cfLoader; - _data = new Lazy>(GetReleaseProfileDataImpl); - _parser = new QualitySizeGuideParser(log); - } - - public override ICollection GetQualities() - { - return _parser.GetQualities(_pathsFactory.Create().SonarrQualityPaths); - } - - public override ICollection GetCustomFormatData() - { - var paths = _pathsFactory.Create(); - return _cfLoader.LoadAllCustomFormatsAtPaths( - paths.SonarrCustomFormatPaths, - paths.SonarrCollectionOfCustomFormats); - } - - private IEnumerable GetReleaseProfileDataImpl() - { - var converter = new TermDataConverter(); - var paths = _pathsFactory.Create(); - var tasks = JsonUtils.GetJsonFilesInDirectories(paths.SonarrReleaseProfilePaths, _log) - .Select(x => LoadAndParseFile(x, converter)); - - var data = Task.WhenAll(tasks).Result - // Make non-nullable type and filter out null values - .Choose(x => x is not null ? (true, x) : default); - - var validator = new ReleaseProfileDataValidationFilterer(_log); - return validator.FilterProfiles(data); - } - - private async Task LoadAndParseFile(IFileInfo file, params JsonConverter[] converters) - { - try - { - using var stream = file.OpenText(); - var json = await stream.ReadToEndAsync(); - return JsonConvert.DeserializeObject(json, converters); - } - catch (JsonException e) - { - HandleJsonException(e, file); - } - catch (AggregateException ae) when (ae.InnerException is JsonException e) - { - HandleJsonException(e, file); - } - - return null; - } - - private void HandleJsonException(JsonException exception, IFileInfo file) - { - _log.Warning(exception, - "Failed to parse Sonarr JSON file (This likely indicates a bug that should be " + - "reported in the TRaSH repo): {File}", file.Name); - } - - public override ReleaseProfileData? GetUnfilteredProfileById(string trashId) - { - return _data.Value.FirstOrDefault(x => x.TrashId.EqualsIgnoreCase(trashId)); - } - - public override IReadOnlyCollection GetReleaseProfileData() - { - return _data.Value.ToList(); - } -} diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/SonarrAutofacModule.cs b/src/Recyclarr.TrashLib/Services/Sonarr/SonarrAutofacModule.cs index 52757b44..e7c4add5 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/SonarrAutofacModule.cs +++ b/src/Recyclarr.TrashLib/Services/Sonarr/SonarrAutofacModule.cs @@ -1,11 +1,6 @@ using Autofac; -using Autofac.Extras.Ordering; -using Recyclarr.TrashLib.Config; -using Recyclarr.TrashLib.Services.Common; using Recyclarr.TrashLib.Services.Sonarr.Api; using Recyclarr.TrashLib.Services.Sonarr.Capabilities; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Services.Sonarr; @@ -13,29 +8,9 @@ public class SonarrAutofacModule : Module { protected override void Load(ContainerBuilder builder) { - builder.RegisterType().As(); - builder.RegisterType().As(); - + builder.RegisterType().As(); builder.RegisterType(); builder.RegisterType().As() .InstancePerLifetimeScope(); - - builder.RegisterType().As(); - builder.RegisterType() - .As() - .Keyed(SupportedServices.Sonarr); - - // Release Profile Support - builder.RegisterType().As(); - builder.RegisterType() - .As(); - builder.RegisterType().As(); - - // Release Profile Filters (ORDER MATTERS!) - builder.RegisterTypes( - typeof(IncludeExcludeFilter), - typeof(StrictNegativeScoresFilter)) - .As() - .OrderByRegistration(); } } diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/SonarrGuideService.cs b/src/Recyclarr.TrashLib/Services/Sonarr/SonarrGuideService.cs deleted file mode 100644 index 448dd9bc..00000000 --- a/src/Recyclarr.TrashLib/Services/Sonarr/SonarrGuideService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Recyclarr.TrashLib.Services.Common; -using Recyclarr.TrashLib.Services.CustomFormat.Models; -using Recyclarr.TrashLib.Services.QualitySize; -using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; - -namespace Recyclarr.TrashLib.Services.Sonarr; - -public abstract class SonarrGuideService : IGuideService -{ - public abstract IReadOnlyCollection GetReleaseProfileData(); - public abstract ReleaseProfileData? GetUnfilteredProfileById(string trashId); - public abstract ICollection GetCustomFormatData(); - public abstract ICollection GetQualities(); -} From e3d6d4f79a5b20130f52e54267f5e450b2e97f6a Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Fri, 20 Jan 2023 16:09:08 -0600 Subject: [PATCH 07/10] refactor: Remove DI for IServiceConfiguration in API services --- .../IntegrationFixture.cs | 5 +- .../Console/Helpers/CacheStoragePathTest.cs | 29 +++------ .../Console/Helpers/CacheStoragePath.cs | 19 +++--- .../Cache/ServiceCacheTest.cs | 65 ++++++++++++------- .../CustomFormat/CachePersisterTest.cs | 42 +++++++----- .../Processors/PersistenceProcessorTest.cs | 10 ++- .../CustomFormatApiPersistenceStepTest.cs | 12 ++-- .../QualityProfileApiPersistenceStepTest.cs | 38 +++++++---- .../Sonarr/SonarrCapabilityEnforcerTest.cs | 16 ++--- .../Sonarr/SonarrCompatibilityTest.cs | 13 ++-- .../Cache/ICacheStoragePath.cs | 3 +- src/Recyclarr.TrashLib/Cache/IServiceCache.cs | 6 +- src/Recyclarr.TrashLib/Cache/ServiceCache.cs | 13 ++-- .../Config/Services/IServiceConfiguration.cs | 2 + .../Http/IServiceRequestBuilder.cs | 3 +- .../Http/ServiceRequestBuilder.cs | 10 ++- .../Common/ServiceCapabilityChecker.cs | 17 ++--- .../CustomFormat/Api/CustomFormatService.cs | 17 ++--- .../CustomFormat/Api/ICustomFormatService.cs | 9 +-- .../Api/IQualityProfileService.cs | 5 +- .../CustomFormat/Api/QualityProfileService.cs | 9 +-- .../Services/CustomFormat/CachePersister.cs | 9 +-- .../CustomFormat/CustomFormatUpdater.cs | 10 +-- .../Services/CustomFormat/ICachePersister.cs | 5 +- .../CustomFormat/ICustomFormatUpdater.cs | 3 +- .../Processors/IPersistenceProcessor.cs | 5 +- .../Processors/PersistenceProcessor.cs | 16 ++--- .../CustomFormatApiPersistenceStep.cs | 16 +++-- .../ICustomFormatApiPersistenceStep.cs | 4 +- .../IQualityProfileApiPersistenceStep.cs | 5 +- .../QualityProfileApiPersistenceStep.cs | 14 +++- .../Services/Processors/RadarrProcessor.cs | 17 +---- .../Services/Processors/SonarrProcessor.cs | 7 +- .../Api/IQualityDefinitionService.cs | 9 ++- .../Api/QualityDefinitionService.cs | 8 ++- .../QualitySize/IQualitySizeUpdater.cs | 3 +- .../QualitySize/QualitySizeUpdater.cs | 40 +++++++----- .../Radarr/IRadarrCapabilityChecker.cs | 6 -- .../Services/Radarr/RadarrAutofacModule.cs | 3 +- .../Radarr/RadarrCapabilityChecker.cs | 2 +- .../Api/IReleaseProfileApiService.cs | 9 +-- ...onarrReleaseProfileCompatibilityHandler.cs | 5 +- .../Api/ReleaseProfileApiService.cs | 23 ++++--- ...onarrReleaseProfileCompatibilityHandler.cs | 7 +- .../ReleaseProfile/ReleaseProfileUpdater.cs | 47 +++++++++----- .../Sonarr/Api/ISonarrTagApiService.cs | 5 +- .../Sonarr/Api/SonarrTagApiService.cs | 9 +-- .../Capabilities/ISonarrCapabilityChecker.cs | 4 +- .../Capabilities/SonarrCapabilityEnforcer.cs | 4 +- .../Services/System/IServiceInformation.cs | 4 +- .../Services/System/ISystemApiService.cs | 3 +- .../Services/System/ServiceInformation.cs | 39 +++++------ .../Services/System/SystemApiService.cs | 5 +- src/Recyclarr.sln.DotSettings | 1 + 54 files changed, 375 insertions(+), 315 deletions(-) delete mode 100644 src/Recyclarr.TrashLib/Services/Radarr/IRadarrCapabilityChecker.cs diff --git a/src/Recyclarr.Cli.TestLibrary/IntegrationFixture.cs b/src/Recyclarr.Cli.TestLibrary/IntegrationFixture.cs index 7068836b..96d1dbc8 100644 --- a/src/Recyclarr.Cli.TestLibrary/IntegrationFixture.cs +++ b/src/Recyclarr.Cli.TestLibrary/IntegrationFixture.cs @@ -1,7 +1,6 @@ using System.IO.Abstractions; using System.IO.Abstractions.Extensions; using System.IO.Abstractions.TestingHelpers; -using System.Reactive.Linq; using Autofac; using Autofac.Features.ResolveAnything; using NSubstitute; @@ -10,7 +9,6 @@ using Recyclarr.Common; using Recyclarr.Common.TestLibrary; using Recyclarr.TestLibrary; using Recyclarr.TrashLib; -using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Repo.VersionControl; using Recyclarr.TrashLib.Services.System; using Recyclarr.TrashLib.Startup; @@ -43,12 +41,11 @@ public abstract class IntegrationFixture : IDisposable builder.RegisterMockFor(); builder.RegisterMockFor(); - builder.RegisterMockFor(); builder.RegisterMockFor(); builder.RegisterMockFor(m => { // By default, choose some extremely high number so that all the newest features are enabled. - m.Version.Returns(_ => Observable.Return(new Version("99.0.0.0"))); + m.GetVersion(default!).ReturnsForAnyArgs(_ => new Version("99.0.0.0")); }); RegisterExtraTypes(builder); diff --git a/src/Recyclarr.Cli.Tests/Console/Helpers/CacheStoragePathTest.cs b/src/Recyclarr.Cli.Tests/Console/Helpers/CacheStoragePathTest.cs index 79770c13..d7a391df 100644 --- a/src/Recyclarr.Cli.Tests/Console/Helpers/CacheStoragePathTest.cs +++ b/src/Recyclarr.Cli.Tests/Console/Helpers/CacheStoragePathTest.cs @@ -1,49 +1,36 @@ -using Autofac; using FluentAssertions; using NSubstitute; using NUnit.Framework; using Recyclarr.Cli.Console.Helpers; -using Recyclarr.Cli.TestLibrary; +using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.Cli.Tests.Console.Helpers; [TestFixture] [Parallelizable(ParallelScope.All)] -public class CacheStoragePathTest : IntegrationFixture +public class CacheStoragePathTest { - [Test] - public void Use_guid_when_no_name() + [Test, AutoMockData] + public void Use_guid_when_no_name(CacheStoragePath sut) { var config = Substitute.ForPartsOf(); config.BaseUrl = new Uri("http://something"); config.InstanceName = null; - using var scope = Container.BeginLifetimeScope(builder => - { - builder.RegisterInstance(config).AsImplementedInterfaces(); - }); - - var sut = scope.Resolve(); - var result = sut.CalculatePath("obj"); + var result = sut.CalculatePath(config, "obj"); result.FullName.Should().MatchRegex(@".*[/\\][a-f0-9]+[/\\]obj\.json$"); } - [Test] - public void Use_name_when_not_null() + [Test, AutoMockData] + public void Use_name_when_not_null(CacheStoragePath sut) { var config = Substitute.ForPartsOf(); config.BaseUrl = new Uri("http://something"); config.InstanceName = "thename"; - using var scope = Container.BeginLifetimeScope(builder => - { - builder.RegisterInstance(config).AsImplementedInterfaces(); - }); - - var sut = scope.Resolve(); - var result = sut.CalculatePath("obj"); + var result = sut.CalculatePath(config, "obj"); result.FullName.Should().MatchRegex(@".*[/\\]thename_[a-f0-9]+[/\\]obj\.json$"); } diff --git a/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs b/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs index 104e99e3..44747a2c 100644 --- a/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs +++ b/src/Recyclarr.Cli/Console/Helpers/CacheStoragePath.cs @@ -11,39 +11,36 @@ namespace Recyclarr.Cli.Console.Helpers; public class CacheStoragePath : ICacheStoragePath { private readonly IAppPaths _paths; - private readonly IServiceConfiguration _config; private readonly IFNV1a _hash; public CacheStoragePath( - IAppPaths paths, - IServiceConfiguration config) + IAppPaths paths) { _paths = paths; - _config = config; _hash = FNV1aFactory.Instance.Create(FNVConfig.GetPredefinedConfig(32)); } - private string BuildUniqueServiceDir() + private string BuildUniqueServiceDir(IServiceConfiguration config) { // In the future, once array-style configurations are removed, the service name will no longer be optional // and the below condition can be removed and the logic simplified. var dirName = new StringBuilder(); - if (_config.InstanceName is not null) + if (config.InstanceName is not null) { - dirName.Append($"{_config.InstanceName}_"); + dirName.Append($"{config.InstanceName}_"); } - var url = _config.BaseUrl.OriginalString; + var url = config.BaseUrl.OriginalString; var guid = _hash.ComputeHash(Encoding.ASCII.GetBytes(url)).AsHexString(); dirName.Append(guid); return dirName.ToString(); } - public IFileInfo CalculatePath(string cacheObjectName) + public IFileInfo CalculatePath(IServiceConfiguration config, string cacheObjectName) { return _paths.CacheDirectory - .SubDirectory(_config.ServiceType.ToString().ToLower(CultureInfo.CurrentCulture)) - .SubDirectory(BuildUniqueServiceDir()) + .SubDirectory(config.ServiceType.ToString().ToLower(CultureInfo.CurrentCulture)) + .SubDirectory(BuildUniqueServiceDir(config)) .File(cacheObjectName + ".json"); } } diff --git a/src/Recyclarr.TrashLib.Tests/Cache/ServiceCacheTest.cs b/src/Recyclarr.TrashLib.Tests/Cache/ServiceCacheTest.cs index b852e8d0..a77552d5 100644 --- a/src/Recyclarr.TrashLib.Tests/Cache/ServiceCacheTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Cache/ServiceCacheTest.cs @@ -6,6 +6,7 @@ using NSubstitute; using NUnit.Framework; using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TrashLib.Cache; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; namespace Recyclarr.TrashLib.Tests.Cache; @@ -34,9 +35,10 @@ public class ServiceCacheTest [Test, AutoMockData] public void Load_returns_null_when_file_does_not_exist( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, + IServiceConfiguration config, ServiceCache sut) { - var result = sut.Load(); + var result = sut.Load(config); result.Should().BeNull(); } @@ -44,6 +46,7 @@ public class ServiceCacheTest public void Loading_with_attribute_parses_correctly( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, [Frozen] ICacheStoragePath storage, + IServiceConfiguration config, ServiceCache sut) { const string testJson = @"{'test_value': 'Foo'}"; @@ -51,18 +54,20 @@ public class ServiceCacheTest const string testJsonPath = "cacheFile.json"; fs.AddFile(testJsonPath, new MockFileData(testJson)); - storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.New(testJsonPath)); + storage.CalculatePath(default!, default!).ReturnsForAnyArgs(fs.FileInfo.New(testJsonPath)); - var obj = sut.Load(); + var obj = sut.Load(config); obj.Should().NotBeNull(); obj!.TestValue.Should().Be("Foo"); } [Test, AutoMockData] - public void Loading_with_invalid_object_name_throws(ServiceCache sut) + public void Loading_with_invalid_object_name_throws( + IServiceConfiguration config, + ServiceCache sut) { - Action act = () => sut.Load(); + Action act = () => sut.Load(config); act.Should() .Throw() @@ -70,9 +75,11 @@ public class ServiceCacheTest } [Test, AutoMockData] - public void Loading_without_attribute_throws(ServiceCache sut) + public void Loading_without_attribute_throws( + IServiceConfiguration config, + ServiceCache sut) { - Action act = () => sut.Load(); + Action act = () => sut.Load(config); act.Should() .Throw() @@ -83,15 +90,17 @@ public class ServiceCacheTest public void Properties_are_saved_using_snake_case( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, [Frozen] ICacheStoragePath storage, + IServiceConfiguration config, ServiceCache sut) { - storage.CalculatePath(default!).ReturnsForAnyArgs(_ => fs.FileInfo.New($"{ValidObjectName}.json")); + storage.CalculatePath(default!, default!) + .ReturnsForAnyArgs(_ => fs.FileInfo.New($"{ValidObjectName}.json")); - sut.Save(new ObjectWithAttribute {TestValue = "Foo"}); + sut.Save(new ObjectWithAttribute {TestValue = "Foo"}, config); fs.AllFiles.Should().ContainMatch($"*{ValidObjectName}.json"); - var file = fs.GetFile(storage.CalculatePath("").FullName); + var file = fs.GetFile(storage.CalculatePath(config, "").FullName); file.Should().NotBeNull(); file.TextContents.Should().Contain("\"test_value\""); } @@ -100,12 +109,13 @@ public class ServiceCacheTest public void Saving_with_attribute_parses_correctly( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, [Frozen] ICacheStoragePath storage, + IServiceConfiguration config, ServiceCache sut) { const string testJsonPath = "cacheFile.json"; - storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.New(testJsonPath)); + storage.CalculatePath(default!, default!).ReturnsForAnyArgs(fs.FileInfo.New(testJsonPath)); - sut.Save(new ObjectWithAttribute {TestValue = "Foo"}); + sut.Save(new ObjectWithAttribute {TestValue = "Foo"}, config); var expectedFile = fs.GetFile(testJsonPath); expectedFile.Should().NotBeNull(); @@ -115,9 +125,11 @@ public class ServiceCacheTest } [Test, AutoMockData] - public void Saving_with_invalid_object_name_throws(ServiceCache sut) + public void Saving_with_invalid_object_name_throws( + IServiceConfiguration config, + ServiceCache sut) { - var act = () => sut.Save(new ObjectWithAttributeInvalidChars()); + var act = () => sut.Save(new ObjectWithAttributeInvalidChars(), config); act.Should() .Throw() @@ -125,9 +137,11 @@ public class ServiceCacheTest } [Test, AutoMockData] - public void Saving_without_attribute_throws(ServiceCache sut) + public void Saving_without_attribute_throws( + IServiceConfiguration config, + ServiceCache sut) { - var act = () => sut.Save(new ObjectWithoutAttribute()); + var act = () => sut.Save(new ObjectWithoutAttribute(), config); act.Should() .Throw() @@ -138,13 +152,14 @@ public class ServiceCacheTest public void Switching_config_and_base_url_should_yield_different_cache_paths( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, [Frozen] ICacheStoragePath storage, + IServiceConfiguration config, ServiceCache sut) { - storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.New("Foo.json")); - sut.Save(new ObjectWithAttribute {TestValue = "Foo"}); + storage.CalculatePath(default!, default!).ReturnsForAnyArgs(fs.FileInfo.New("Foo.json")); + sut.Save(new ObjectWithAttribute {TestValue = "Foo"}, config); - storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.New("Bar.json")); - sut.Save(new ObjectWithAttribute {TestValue = "Bar"}); + storage.CalculatePath(default!, default!).ReturnsForAnyArgs(fs.FileInfo.New("Bar.json")); + sut.Save(new ObjectWithAttribute {TestValue = "Bar"}, config); var expectedFiles = new[] {"*Foo.json", "*Bar.json"}; foreach (var expectedFile in expectedFiles) @@ -157,12 +172,13 @@ public class ServiceCacheTest public void When_cache_file_is_empty_do_not_throw( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, [Frozen] ICacheStoragePath storage, + IServiceConfiguration config, ServiceCache sut) { - storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.New("cacheFile.json")); + storage.CalculatePath(default!, default!).ReturnsForAnyArgs(fs.FileInfo.New("cacheFile.json")); fs.AddFile("cacheFile.json", new MockFileData("")); - Action act = () => sut.Load(); + Action act = () => sut.Load(config); act.Should().NotThrow(); } @@ -171,6 +187,7 @@ public class ServiceCacheTest public void Name_properties_are_set_on_load( [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, [Frozen] ICacheStoragePath storage, + IServiceConfiguration config, ServiceCache sut) { const string cacheJson = @" @@ -187,9 +204,9 @@ public class ServiceCacheTest "; fs.AddFile("cacheFile.json", new MockFileData(cacheJson)); - storage.CalculatePath(default!).ReturnsForAnyArgs(fs.FileInfo.New("cacheFile.json")); + storage.CalculatePath(default!, default!).ReturnsForAnyArgs(fs.FileInfo.New("cacheFile.json")); - var result = sut.Load(); + var result = sut.Load(config); result.Should().BeEquivalentTo(new CustomFormatCache { diff --git a/src/Recyclarr.TrashLib.Tests/CustomFormat/CachePersisterTest.cs b/src/Recyclarr.TrashLib.Tests/CustomFormat/CachePersisterTest.cs index 824c270d..8e38e6de 100644 --- a/src/Recyclarr.TrashLib.Tests/CustomFormat/CachePersisterTest.cs +++ b/src/Recyclarr.TrashLib.Tests/CustomFormat/CachePersisterTest.cs @@ -3,6 +3,7 @@ using FluentAssertions; using NSubstitute; using NUnit.Framework; using Recyclarr.TrashLib.Cache; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; @@ -33,14 +34,15 @@ public class CachePersisterTest public void Set_loaded_cache_to_null_if_versions_mismatch(int versionToTest) { var ctx = new Context(); + var config = Substitute.For(); var testCfObj = new CustomFormatCache { Version = versionToTest, TrashIdMappings = new Collection {new("", "", 5)} }; - ctx.ServiceCache.Load().Returns(testCfObj); - ctx.Persister.Load(); + ctx.ServiceCache.Load(config).Returns(testCfObj); + ctx.Persister.Load(config); ctx.Persister.CfCache.Should().BeNull(); } @@ -48,14 +50,15 @@ public class CachePersisterTest public void Accept_loaded_cache_when_versions_match() { var ctx = new Context(); + var config = Substitute.For(); var testCfObj = new CustomFormatCache { Version = CustomFormatCache.LatestVersion, TrashIdMappings = new Collection {new("", "", 5)} }; - ctx.ServiceCache.Load().Returns(testCfObj); - ctx.Persister.Load(); + ctx.ServiceCache.Load(config).Returns(testCfObj); + ctx.Persister.Load(config); ctx.Persister.CfCache.Should().NotBeNull(); } @@ -64,9 +67,10 @@ public class CachePersisterTest { var ctx = new Context(); var testCfObj = new CustomFormatCache(); - ctx.ServiceCache.Load().Returns(testCfObj); + var config = Substitute.For(); - ctx.Persister.Load(); + ctx.ServiceCache.Load(config).Returns(testCfObj); + ctx.Persister.Load(config); ctx.Persister.CfCache.Should().BeSameAs(testCfObj); } @@ -74,7 +78,9 @@ public class CachePersisterTest public void Cf_cache_returns_null_if_not_loaded() { var ctx = new Context(); - ctx.Persister.Load(); + var config = Substitute.For(); + + ctx.Persister.Load(config); ctx.Persister.CfCache.Should().BeNull(); } @@ -83,33 +89,39 @@ public class CachePersisterTest { var ctx = new Context(); var testCfObj = new CustomFormatCache(); - ctx.ServiceCache.Load().Returns(testCfObj); + var config = Substitute.For(); + + ctx.ServiceCache.Load(config).Returns(testCfObj); - ctx.Persister.Load(); - ctx.Persister.Save(); + ctx.Persister.Load(config); + ctx.Persister.Save(config); - ctx.ServiceCache.Received().Save(Arg.Is(testCfObj)); + ctx.ServiceCache.Received().Save(testCfObj, config); } [Test] public void Saving_without_loading_does_nothing() { var ctx = new Context(); - ctx.Persister.Save(); - ctx.ServiceCache.DidNotReceive().Save(Arg.Any()); + var config = Substitute.For(); + + ctx.Persister.Save(config); + ctx.ServiceCache.DidNotReceiveWithAnyArgs().Save(Arg.Any(), default!); } [Test] public void Updating_overwrites_previous_cf_cache_and_updates_cf_data() { var ctx = new Context(); + var config = Substitute.For(); // Load initial CfCache just to test that it gets replaced - ctx.ServiceCache.Load().Returns(new CustomFormatCache + ctx.ServiceCache.Load(config).Returns(new CustomFormatCache { TrashIdMappings = new Collection {new("trashid", "", 1)} }); - ctx.Persister.Load(); + + ctx.Persister.Load(config); // Update with new cached items var customFormatData = new List diff --git a/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceProcessorTest.cs b/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceProcessorTest.cs index c3c7744b..50fdebf3 100644 --- a/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceProcessorTest.cs +++ b/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceProcessorTest.cs @@ -19,7 +19,6 @@ public class PersistenceProcessorTest { var steps = Substitute.For(); var cfApi = Substitute.For(); - var qpApi = Substitute.For(); var config = new RadarrConfiguration {DeleteOldCustomFormats = true}; @@ -27,8 +26,8 @@ public class PersistenceProcessorTest var deletedCfsInCache = new Collection(); var profileScores = new Dictionary(); - var processor = new PersistenceProcessor(cfApi, qpApi, config, steps); - await processor.PersistCustomFormats(guideCfs, deletedCfsInCache, profileScores); + var processor = new PersistenceProcessor(cfApi, steps); + await processor.PersistCustomFormats(config, guideCfs, deletedCfsInCache, profileScores); steps.JsonTransactionStep.Received().RecordDeletions(Arg.Is(deletedCfsInCache), Arg.Any>()); } @@ -38,7 +37,6 @@ public class PersistenceProcessorTest { var steps = Substitute.For(); var cfApi = Substitute.For(); - var qpApi = Substitute.For(); var config = new RadarrConfiguration {DeleteOldCustomFormats = false}; @@ -46,8 +44,8 @@ public class PersistenceProcessorTest var deletedCfsInCache = Array.Empty(); var profileScores = new Dictionary(); - var processor = new PersistenceProcessor(cfApi, qpApi, config, steps); - await processor.PersistCustomFormats(guideCfs, deletedCfsInCache, profileScores); + var processor = new PersistenceProcessor(cfApi, steps); + await processor.PersistCustomFormats(config, guideCfs, deletedCfsInCache, profileScores); steps.JsonTransactionStep.DidNotReceive() .RecordDeletions(Arg.Any>(), Arg.Any>()); diff --git a/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStepTest.cs b/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStepTest.cs index ee5e0ddc..ebb4803a 100644 --- a/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStepTest.cs +++ b/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStepTest.cs @@ -1,5 +1,6 @@ using NSubstitute; using NUnit.Framework; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Api; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; @@ -28,14 +29,15 @@ public class CustomFormatApiPersistenceStepTest var api = Substitute.For(); - var processor = new CustomFormatApiPersistenceStep(); - await processor.Process(api, transactions); + var processor = new CustomFormatApiPersistenceStep(api); + var config = Substitute.For(); + await processor.Process(config, transactions); Received.InOrder(() => { - api.CreateCustomFormat(transactions.NewCustomFormats.First()); - api.UpdateCustomFormat(transactions.UpdatedCustomFormats.First()); - api.DeleteCustomFormat(4); + api.CreateCustomFormat(config, transactions.NewCustomFormats.First()); + api.UpdateCustomFormat(config, transactions.UpdatedCustomFormats.First()); + api.DeleteCustomFormat(config, 4); }); } } diff --git a/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStepTest.cs b/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStepTest.cs index e1f36e73..963c6a88 100644 --- a/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStepTest.cs +++ b/src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStepTest.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json.Linq; using NSubstitute; using NUnit.Framework; using Recyclarr.TestLibrary.NSubstitute; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Api; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; @@ -41,7 +42,8 @@ public class QualityProfileApiPersistenceStepTest }]"; var api = Substitute.For(); - api.GetQualityProfiles()!.Returns(JsonConvert.DeserializeObject>(radarrQualityProfileData)); + api.GetQualityProfiles(default!)!.ReturnsForAnyArgs( + JsonConvert.DeserializeObject>(radarrQualityProfileData)); var cfScores = new Dictionary { @@ -50,10 +52,10 @@ public class QualityProfileApiPersistenceStepTest } }; - var processor = new QualityProfileApiPersistenceStep(); - await processor.Process(api, cfScores); + var processor = new QualityProfileApiPersistenceStep(api); + await processor.Process(Substitute.For(), cfScores); - await api.DidNotReceive().UpdateQualityProfile(Arg.Any(), Arg.Any()); + await api.DidNotReceiveWithAnyArgs().UpdateQualityProfile(default!, default!, default!); } [Test] @@ -62,15 +64,16 @@ public class QualityProfileApiPersistenceStepTest const string radarrQualityProfileData = @"[{'name': 'profile1'}]"; var api = Substitute.For(); - api.GetQualityProfiles()!.Returns(JsonConvert.DeserializeObject>(radarrQualityProfileData)); + api.GetQualityProfiles(default!)!.ReturnsForAnyArgs( + JsonConvert.DeserializeObject>(radarrQualityProfileData)); var cfScores = new Dictionary { {"wrong_profile_name", CfTestUtils.NewMapping()} }; - var processor = new QualityProfileApiPersistenceStep(); - await processor.Process(api, cfScores); + var processor = new QualityProfileApiPersistenceStep(api); + await processor.Process(Substitute.For(), cfScores); processor.InvalidProfileNames.Should().Equal("wrong_profile_name"); processor.UpdatedScores.Should().BeEmpty(); @@ -101,7 +104,8 @@ public class QualityProfileApiPersistenceStepTest }]"; var api = Substitute.For(); - api.GetQualityProfiles()!.Returns(JsonConvert.DeserializeObject>(radarrQualityProfileData)); + api.GetQualityProfiles(default!)!.ReturnsForAnyArgs( + JsonConvert.DeserializeObject>(radarrQualityProfileData)); var cfScores = new Dictionary { @@ -111,8 +115,8 @@ public class QualityProfileApiPersistenceStepTest } }; - var processor = new QualityProfileApiPersistenceStep(); - await processor.Process(api, cfScores); + var processor = new QualityProfileApiPersistenceStep(api); + await processor.Process(Substitute.For(), cfScores); processor.InvalidProfileNames.Should().BeEmpty(); processor.UpdatedScores.Should() @@ -125,6 +129,7 @@ public class QualityProfileApiPersistenceStepTest }); await api.Received().UpdateQualityProfile( + Arg.Any(), Verify.That(j => j["formatItems"]!.Children().Should().HaveCount(3)), Arg.Any()); } @@ -174,7 +179,8 @@ public class QualityProfileApiPersistenceStepTest }]"; var api = Substitute.For(); - api.GetQualityProfiles()!.Returns(JsonConvert.DeserializeObject>(radarrQualityProfileData)); + api.GetQualityProfiles(default!)!.ReturnsForAnyArgs( + JsonConvert.DeserializeObject>(radarrQualityProfileData)); var cfScores = new Dictionary { @@ -189,8 +195,9 @@ public class QualityProfileApiPersistenceStepTest } }; - var processor = new QualityProfileApiPersistenceStep(); - await processor.Process(api, cfScores); + var processor = new QualityProfileApiPersistenceStep(api); + var config = Substitute.For(); + await processor.Process(config, cfScores); var expectedProfileJson = JObject.Parse(@"{ 'name': 'profile1', @@ -234,7 +241,10 @@ public class QualityProfileApiPersistenceStepTest }"); await api.Received() - .UpdateQualityProfile(Verify.That(a => a.Should().BeEquivalentTo(expectedProfileJson)), 1); + .UpdateQualityProfile( + config, + Verify.That(a => a.Should().BeEquivalentTo(expectedProfileJson)), + 1); processor.InvalidProfileNames.Should().BeEmpty(); processor.UpdatedScores.Should() .ContainKey("profile1").WhoseValue.Should() diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCapabilityEnforcerTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCapabilityEnforcerTest.cs index 48d970ea..4c9b249e 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCapabilityEnforcerTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCapabilityEnforcerTest.cs @@ -21,11 +21,11 @@ public class SonarrCapabilityEnforcerTest { var config = new SonarrConfiguration(); - checker.GetCapabilities().Returns((SonarrCapabilities?) null); + checker.GetCapabilities(default!).ReturnsForAnyArgs((SonarrCapabilities?) null); var act = () => sut.Check(config); - act.Should().Throw().WithMessage("*obtained*"); + act.Should().ThrowAsync().WithMessage("*obtained*"); } [Test, AutoMockData] @@ -35,14 +35,14 @@ public class SonarrCapabilityEnforcerTest { var config = new SonarrConfiguration(); - checker.GetCapabilities().Returns(new SonarrCapabilities(new Version()) + checker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version()) { SupportsNamedReleaseProfiles = false }); var act = () => sut.Check(config); - act.Should().Throw().WithMessage("*minimum*"); + act.Should().ThrowAsync().WithMessage("*minimum*"); } [Test, AutoMockData] @@ -58,7 +58,7 @@ public class SonarrCapabilityEnforcerTest } }; - checker.GetCapabilities().Returns(new SonarrCapabilities(new Version()) + checker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version()) { SupportsNamedReleaseProfiles = true, SupportsCustomFormats = true @@ -66,7 +66,7 @@ public class SonarrCapabilityEnforcerTest var act = () => sut.Check(config); - act.Should().Throw().WithMessage("*v3*"); + act.Should().ThrowAsync().WithMessage("*v3*"); } [Test, AutoMockData] @@ -82,7 +82,7 @@ public class SonarrCapabilityEnforcerTest } }; - checker.GetCapabilities().Returns(new SonarrCapabilities(new Version()) + checker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version()) { SupportsNamedReleaseProfiles = true, SupportsCustomFormats = false @@ -90,6 +90,6 @@ public class SonarrCapabilityEnforcerTest var act = () => sut.Check(config); - act.Should().Throw().WithMessage("*v4*"); + act.Should().ThrowAsync().WithMessage("*v4*"); } } diff --git a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs b/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs index 91b826f0..e529415e 100644 --- a/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs +++ b/src/Recyclarr.TrashLib.Tests/Sonarr/SonarrCompatibilityTest.cs @@ -7,6 +7,7 @@ using NSubstitute; using NUnit.Framework; using Recyclarr.Cli.TestLibrary; using Recyclarr.TestLibrary; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.ReleaseProfile.Api; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; using Recyclarr.TrashLib.Services.Sonarr.Capabilities; @@ -58,10 +59,10 @@ public class SonarrCompatibilityTest : IntegrationFixture } [Test] - public void Send_v2_to_v1() + public async Task Send_v2_to_v1() { var capabilityChecker = Resolve(); - capabilityChecker.GetCapabilities().Returns(new SonarrCapabilities(new Version()) + capabilityChecker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version()) { ArraysNeededForReleaseProfileRequiredAndIgnored = false }); @@ -69,16 +70,16 @@ public class SonarrCompatibilityTest : IntegrationFixture var sut = Resolve(); var data = new SonarrReleaseProfile {Ignored = new List {"one", "two", "three"}}; - var result = sut.CompatibleReleaseProfileForSending(data); + var result = await sut.CompatibleReleaseProfileForSending(Substitute.For(), data); result.Should().BeEquivalentTo(new SonarrReleaseProfileV1 {Ignored = "one,two,three"}); } [Test] - public void Send_v2_to_v2() + public async Task Send_v2_to_v2() { var capabilityChecker = Resolve(); - capabilityChecker.GetCapabilities().Returns(new SonarrCapabilities(new Version()) + capabilityChecker.GetCapabilities(default!).ReturnsForAnyArgs(new SonarrCapabilities(new Version()) { ArraysNeededForReleaseProfileRequiredAndIgnored = true }); @@ -86,7 +87,7 @@ public class SonarrCompatibilityTest : IntegrationFixture var sut = Resolve(); var data = new SonarrReleaseProfile {Ignored = new List {"one", "two", "three"}}; - var result = sut.CompatibleReleaseProfileForSending(data); + var result = await sut.CompatibleReleaseProfileForSending(Substitute.For(), data); result.Should().BeEquivalentTo(data); } diff --git a/src/Recyclarr.TrashLib/Cache/ICacheStoragePath.cs b/src/Recyclarr.TrashLib/Cache/ICacheStoragePath.cs index 180e98b1..5a0c4f4b 100644 --- a/src/Recyclarr.TrashLib/Cache/ICacheStoragePath.cs +++ b/src/Recyclarr.TrashLib/Cache/ICacheStoragePath.cs @@ -1,8 +1,9 @@ using System.IO.Abstractions; +using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Cache; public interface ICacheStoragePath { - IFileInfo CalculatePath(string cacheObjectName); + IFileInfo CalculatePath(IServiceConfiguration config, string cacheObjectName); } diff --git a/src/Recyclarr.TrashLib/Cache/IServiceCache.cs b/src/Recyclarr.TrashLib/Cache/IServiceCache.cs index dadd6598..84c55d18 100644 --- a/src/Recyclarr.TrashLib/Cache/IServiceCache.cs +++ b/src/Recyclarr.TrashLib/Cache/IServiceCache.cs @@ -1,7 +1,9 @@ +using Recyclarr.TrashLib.Config.Services; + namespace Recyclarr.TrashLib.Cache; public interface IServiceCache { - T? Load() where T : class; - void Save(T obj) where T : class; + T? Load(IServiceConfiguration config) where T : class; + void Save(T obj, IServiceConfiguration config) where T : class; } diff --git a/src/Recyclarr.TrashLib/Cache/ServiceCache.cs b/src/Recyclarr.TrashLib/Cache/ServiceCache.cs index 0a25fbb0..3df4749d 100644 --- a/src/Recyclarr.TrashLib/Cache/ServiceCache.cs +++ b/src/Recyclarr.TrashLib/Cache/ServiceCache.cs @@ -4,6 +4,7 @@ using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Recyclarr.Common.Extensions; +using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Cache; @@ -29,9 +30,9 @@ public class ServiceCache : IServiceCache private ILogger Log { get; } - public T? Load() where T : class + public T? Load(IServiceConfiguration config) where T : class { - var path = PathFromAttribute(); + var path = PathFromAttribute(config); if (!path.Exists) { return null; @@ -52,9 +53,9 @@ public class ServiceCache : IServiceCache return null; } - public void Save(T obj) where T : class + public void Save(T obj, IServiceConfiguration config) where T : class { - var path = PathFromAttribute(); + var path = PathFromAttribute(config); path.CreateParentDirectory(); var serializer = JsonSerializer.Create(_jsonSettings); @@ -74,7 +75,7 @@ public class ServiceCache : IServiceCache return attribute.Name; } - private IFileInfo PathFromAttribute() + private IFileInfo PathFromAttribute(IServiceConfiguration config) { var objectName = GetCacheObjectNameAttribute(); if (!AllowedObjectNameCharacters.IsMatch(objectName)) @@ -82,6 +83,6 @@ public class ServiceCache : IServiceCache throw new ArgumentException($"Object name '{objectName}' has unacceptable characters"); } - return _storagePath.CalculatePath(objectName); + return _storagePath.CalculatePath(config, objectName); } } diff --git a/src/Recyclarr.TrashLib/Config/Services/IServiceConfiguration.cs b/src/Recyclarr.TrashLib/Config/Services/IServiceConfiguration.cs index 1074f6ea..d1825a8e 100644 --- a/src/Recyclarr.TrashLib/Config/Services/IServiceConfiguration.cs +++ b/src/Recyclarr.TrashLib/Config/Services/IServiceConfiguration.cs @@ -7,4 +7,6 @@ public interface IServiceConfiguration Uri BaseUrl { get; } string ApiKey { get; } bool DeleteOldCustomFormats { get; } + ICollection CustomFormats { get; init; } + QualityDefinitionConfig? QualityDefinition { get; init; } } diff --git a/src/Recyclarr.TrashLib/Http/IServiceRequestBuilder.cs b/src/Recyclarr.TrashLib/Http/IServiceRequestBuilder.cs index 3142cc34..13767f7c 100644 --- a/src/Recyclarr.TrashLib/Http/IServiceRequestBuilder.cs +++ b/src/Recyclarr.TrashLib/Http/IServiceRequestBuilder.cs @@ -1,8 +1,9 @@ using Flurl.Http; +using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Http; public interface IServiceRequestBuilder { - IFlurlRequest Request(params object[] path); + IFlurlRequest Request(IServiceConfiguration config, params object[] path); } diff --git a/src/Recyclarr.TrashLib/Http/ServiceRequestBuilder.cs b/src/Recyclarr.TrashLib/Http/ServiceRequestBuilder.cs index b3002b83..7d8c2096 100644 --- a/src/Recyclarr.TrashLib/Http/ServiceRequestBuilder.cs +++ b/src/Recyclarr.TrashLib/Http/ServiceRequestBuilder.cs @@ -5,19 +5,17 @@ namespace Recyclarr.TrashLib.Http; public class ServiceRequestBuilder : IServiceRequestBuilder { - private readonly IServiceConfiguration _config; private readonly IFlurlClientFactory _clientFactory; - public ServiceRequestBuilder(IServiceConfiguration config, IFlurlClientFactory clientFactory) + public ServiceRequestBuilder(IFlurlClientFactory clientFactory) { - _config = config; _clientFactory = clientFactory; } - public IFlurlRequest Request(params object[] path) + public IFlurlRequest Request(IServiceConfiguration config, params object[] path) { - var client = _clientFactory.BuildClient(_config.BaseUrl); + var client = _clientFactory.BuildClient(config.BaseUrl); return client.Request(new[] {"api", "v3"}.Concat(path).ToArray()) - .SetQueryParams(new {apikey = _config.ApiKey}); + .SetQueryParams(new {apikey = config.ApiKey}); } } diff --git a/src/Recyclarr.TrashLib/Services/Common/ServiceCapabilityChecker.cs b/src/Recyclarr.TrashLib/Services/Common/ServiceCapabilityChecker.cs index f4993728..cff6d27b 100644 --- a/src/Recyclarr.TrashLib/Services/Common/ServiceCapabilityChecker.cs +++ b/src/Recyclarr.TrashLib/Services/Common/ServiceCapabilityChecker.cs @@ -1,24 +1,21 @@ -using System.Reactive.Linq; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.System; namespace Recyclarr.TrashLib.Services.Common; public abstract class ServiceCapabilityChecker where T : class { - private readonly IObservable _capabilities; + private readonly IServiceInformation _info; - public T? GetCapabilities() + protected ServiceCapabilityChecker(IServiceInformation info) { - return _capabilities.Wait(); + _info = info; } - protected ServiceCapabilityChecker(IServiceInformation info) + public async Task GetCapabilities(IServiceConfiguration config) { - _capabilities = info.Version - .Select(x => x is null ? null : BuildCapabilitiesObject(x)) - .Replay(1) - .AutoConnect() - .LastAsync(); + var version = await _info.GetVersion(config); + return version is not null ? BuildCapabilitiesObject(version) : null; } protected abstract T BuildCapabilitiesObject(Version version); diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Api/CustomFormatService.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Api/CustomFormatService.cs index 38b350b4..2a7288e1 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Api/CustomFormatService.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Api/CustomFormatService.cs @@ -1,5 +1,6 @@ using Flurl.Http; using Newtonsoft.Json.Linq; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Http; using Recyclarr.TrashLib.Services.CustomFormat.Models; @@ -14,15 +15,15 @@ internal class CustomFormatService : ICustomFormatService _service = service; } - public async Task> GetCustomFormats() + public async Task> GetCustomFormats(IServiceConfiguration config) { - return await _service.Request("customformat") + return await _service.Request(config, "customformat") .GetJsonAsync>(); } - public async Task CreateCustomFormat(ProcessedCustomFormatData cf) + public async Task CreateCustomFormat(IServiceConfiguration config, ProcessedCustomFormatData cf) { - var response = await _service.Request("customformat") + var response = await _service.Request(config, "customformat") .PostJsonAsync(cf.Json) .ReceiveJson(); @@ -32,16 +33,16 @@ internal class CustomFormatService : ICustomFormatService } } - public async Task UpdateCustomFormat(ProcessedCustomFormatData cf) + public async Task UpdateCustomFormat(IServiceConfiguration config, ProcessedCustomFormatData cf) { - await _service.Request("customformat", cf.FormatId) + await _service.Request(config, "customformat", cf.FormatId) .PutJsonAsync(cf.Json) .ReceiveJson(); } - public async Task DeleteCustomFormat(int customFormatId) + public async Task DeleteCustomFormat(IServiceConfiguration config, int customFormatId) { - await _service.Request("customformat", customFormatId) + await _service.Request(config, "customformat", customFormatId) .DeleteAsync(); } } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Api/ICustomFormatService.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Api/ICustomFormatService.cs index d413d144..3ccee682 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Api/ICustomFormatService.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Api/ICustomFormatService.cs @@ -1,12 +1,13 @@ using Newtonsoft.Json.Linq; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Models; namespace Recyclarr.TrashLib.Services.CustomFormat.Api; public interface ICustomFormatService { - Task> GetCustomFormats(); - Task CreateCustomFormat(ProcessedCustomFormatData cf); - Task UpdateCustomFormat(ProcessedCustomFormatData cf); - Task DeleteCustomFormat(int customFormatId); + Task> GetCustomFormats(IServiceConfiguration config); + Task CreateCustomFormat(IServiceConfiguration config, ProcessedCustomFormatData cf); + Task UpdateCustomFormat(IServiceConfiguration config, ProcessedCustomFormatData cf); + Task DeleteCustomFormat(IServiceConfiguration config, int customFormatId); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Api/IQualityProfileService.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Api/IQualityProfileService.cs index b3fd141d..ef696f49 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Api/IQualityProfileService.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Api/IQualityProfileService.cs @@ -1,9 +1,10 @@ using Newtonsoft.Json.Linq; +using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Services.CustomFormat.Api; public interface IQualityProfileService { - Task> GetQualityProfiles(); - Task UpdateQualityProfile(JObject profileJson, int id); + Task> GetQualityProfiles(IServiceConfiguration config); + Task UpdateQualityProfile(IServiceConfiguration config, JObject profileJson, int id); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Api/QualityProfileService.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Api/QualityProfileService.cs index f44c838f..408e6a97 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Api/QualityProfileService.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Api/QualityProfileService.cs @@ -1,5 +1,6 @@ using Flurl.Http; using Newtonsoft.Json.Linq; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Http; namespace Recyclarr.TrashLib.Services.CustomFormat.Api; @@ -13,15 +14,15 @@ internal class QualityProfileService : IQualityProfileService _service = service; } - public async Task> GetQualityProfiles() + public async Task> GetQualityProfiles(IServiceConfiguration config) { - return await _service.Request("qualityprofile") + return await _service.Request(config, "qualityprofile") .GetJsonAsync>(); } - public async Task UpdateQualityProfile(JObject profileJson, int id) + public async Task UpdateQualityProfile(IServiceConfiguration config, JObject profileJson, int id) { - return await _service.Request("qualityprofile", id) + return await _service.Request(config, "qualityprofile", id) .PutJsonAsync(profileJson) .ReceiveJson(); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/CachePersister.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/CachePersister.cs index e48b97cd..5b73d89a 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/CachePersister.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/CachePersister.cs @@ -1,5 +1,6 @@ using Recyclarr.Common.Extensions; using Recyclarr.TrashLib.Cache; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; @@ -18,9 +19,9 @@ public class CachePersister : ICachePersister private ILogger Log { get; } public CustomFormatCache? CfCache { get; private set; } - public void Load() + public void Load(IServiceConfiguration config) { - CfCache = _cache.Load(); + CfCache = _cache.Load(config); // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (CfCache != null) { @@ -41,7 +42,7 @@ public class CachePersister : ICachePersister } } - public void Save() + public void Save(IServiceConfiguration config) { if (CfCache == null) { @@ -50,7 +51,7 @@ public class CachePersister : ICachePersister } Log.Debug("Saving Cache"); - _cache.Save(CfCache); + _cache.Save(CfCache, config); } public void Update(IEnumerable customFormats) diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs index 15f3b3d1..563c753d 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs @@ -1,5 +1,4 @@ using Recyclarr.Common.Extensions; -using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Processors; using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; @@ -29,11 +28,11 @@ internal class CustomFormatUpdater : ICustomFormatUpdater _console = console; } - public async Task Process(bool isPreview, IEnumerable configs, SupportedServices serviceType) + public async Task Process(bool isPreview, IServiceConfiguration config) { - _cache.Load(); + _cache.Load(config); - await _guideProcessor.BuildGuideDataAsync(configs, _cache.CfCache, serviceType); + await _guideProcessor.BuildGuideDataAsync(config.CustomFormats, _cache.CfCache, config.ServiceType); if (!ValidateGuideDataAndCheckShouldProceed()) { @@ -48,6 +47,7 @@ internal class CustomFormatUpdater : ICustomFormatUpdater } await _persistenceProcessor.PersistCustomFormats( + config, _guideProcessor.ProcessedCustomFormats, _guideProcessor.DeletedCustomFormatsInCache, _guideProcessor.ProfileScores); @@ -57,7 +57,7 @@ internal class CustomFormatUpdater : ICustomFormatUpdater // Cache all the custom formats (using ID from API response). _cache.Update(_guideProcessor.ProcessedCustomFormats); - _cache.Save(); + _cache.Save(config); } private void PrintQualityProfileUpdates() diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/ICachePersister.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/ICachePersister.cs index c1ddc4e1..7a7cfe79 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/ICachePersister.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/ICachePersister.cs @@ -1,3 +1,4 @@ +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; @@ -6,7 +7,7 @@ namespace Recyclarr.TrashLib.Services.CustomFormat; public interface ICachePersister { CustomFormatCache? CfCache { get; } - void Load(); - void Save(); + void Load(IServiceConfiguration config); + void Save(IServiceConfiguration config); void Update(IEnumerable customFormats); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs index eaad2498..f830b958 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs @@ -1,9 +1,8 @@ -using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Services.CustomFormat; public interface ICustomFormatUpdater { - Task Process(bool isPreview, IEnumerable configs, SupportedServices serviceType); + Task Process(bool isPreview, IServiceConfiguration config); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IPersistenceProcessor.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IPersistenceProcessor.cs index a67b3a53..74a60269 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IPersistenceProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IPersistenceProcessor.cs @@ -1,3 +1,4 @@ +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; @@ -10,7 +11,9 @@ public interface IPersistenceProcessor IReadOnlyCollection InvalidProfileNames { get; } CustomFormatTransactionData Transactions { get; } - Task PersistCustomFormats(IReadOnlyCollection guideCfs, + Task PersistCustomFormats( + IServiceConfiguration config, + IReadOnlyCollection guideCfs, IEnumerable deletedCfsInCache, IDictionary profileScores); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceProcessor.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceProcessor.cs index 4c1f10a9..5cc30d00 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceProcessor.cs @@ -15,20 +15,14 @@ public interface IPersistenceProcessorSteps internal class PersistenceProcessor : IPersistenceProcessor { - private readonly IServiceConfiguration _config; private readonly ICustomFormatService _customFormatService; - private readonly IQualityProfileService _qualityProfileService; private readonly IPersistenceProcessorSteps _steps; public PersistenceProcessor( ICustomFormatService customFormatService, - IQualityProfileService qualityProfileService, - IServiceConfiguration config, IPersistenceProcessorSteps steps) { _customFormatService = customFormatService; - _qualityProfileService = qualityProfileService; - _config = config; _steps = steps; } @@ -42,11 +36,12 @@ internal class PersistenceProcessor : IPersistenceProcessor => _steps.ProfileQualityProfileApiPersister.InvalidProfileNames; public async Task PersistCustomFormats( + IServiceConfiguration config, IReadOnlyCollection guideCfs, IEnumerable deletedCfsInCache, IDictionary profileScores) { - var serviceCfs = await _customFormatService.GetCustomFormats(); + var serviceCfs = await _customFormatService.GetCustomFormats(config); // Step 1: Match CFs between the guide & Radarr and merge the data. The goal is to retain as much of the // original data from Radarr as possible. There are many properties in the response JSON that we don't @@ -54,17 +49,16 @@ internal class PersistenceProcessor : IPersistenceProcessor _steps.JsonTransactionStep.Process(guideCfs, serviceCfs); // Step 1.1: Optionally record deletions of custom formats in cache but not in the guide - if (_config.DeleteOldCustomFormats) + if (config.DeleteOldCustomFormats) { _steps.JsonTransactionStep.RecordDeletions(deletedCfsInCache, serviceCfs); } // Step 2: For each merged CF, persist it to Radarr via its API. This will involve a combination of updates // to existing CFs and creation of brand new ones, depending on what's already available in Radarr. - await _steps.CustomFormatCustomFormatApiPersister.Process(_customFormatService, - _steps.JsonTransactionStep.Transactions); + await _steps.CustomFormatCustomFormatApiPersister.Process(config, _steps.JsonTransactionStep.Transactions); // Step 3: Update all quality profiles with the scores from the guide for the uploaded custom formats - await _steps.ProfileQualityProfileApiPersister.Process(_qualityProfileService, profileScores); + await _steps.ProfileQualityProfileApiPersister.Process(config, profileScores); } } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStep.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStep.cs index 8dff3636..32107c38 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStep.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStep.cs @@ -1,24 +1,32 @@ +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Api; namespace Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; internal class CustomFormatApiPersistenceStep : ICustomFormatApiPersistenceStep { - public async Task Process(ICustomFormatService api, CustomFormatTransactionData transactions) + private readonly ICustomFormatService _api; + + public CustomFormatApiPersistenceStep(ICustomFormatService api) + { + _api = api; + } + + public async Task Process(IServiceConfiguration config, CustomFormatTransactionData transactions) { foreach (var cf in transactions.NewCustomFormats) { - await api.CreateCustomFormat(cf); + await _api.CreateCustomFormat(config, cf); } foreach (var cf in transactions.UpdatedCustomFormats) { - await api.UpdateCustomFormat(cf); + await _api.UpdateCustomFormat(config, cf); } foreach (var cfId in transactions.DeletedCustomFormatIds) { - await api.DeleteCustomFormat(cfId.CustomFormatId); + await _api.DeleteCustomFormat(config, cfId.CustomFormatId); } } } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/ICustomFormatApiPersistenceStep.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/ICustomFormatApiPersistenceStep.cs index 8c43afa2..ea2c7c75 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/ICustomFormatApiPersistenceStep.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/ICustomFormatApiPersistenceStep.cs @@ -1,8 +1,8 @@ -using Recyclarr.TrashLib.Services.CustomFormat.Api; +using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; public interface ICustomFormatApiPersistenceStep { - Task Process(ICustomFormatService api, CustomFormatTransactionData transactions); + Task Process(IServiceConfiguration config, CustomFormatTransactionData transactions); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/IQualityProfileApiPersistenceStep.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/IQualityProfileApiPersistenceStep.cs index 9e475b0d..e71a26b4 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/IQualityProfileApiPersistenceStep.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/IQualityProfileApiPersistenceStep.cs @@ -1,4 +1,4 @@ -using Recyclarr.TrashLib.Services.CustomFormat.Api; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Models; namespace Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; @@ -8,6 +8,7 @@ public interface IQualityProfileApiPersistenceStep IDictionary> UpdatedScores { get; } IReadOnlyCollection InvalidProfileNames { get; } - Task Process(IQualityProfileService api, + Task Process( + IServiceConfiguration config, IDictionary cfScores); } diff --git a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStep.cs b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStep.cs index 55ee6a66..3612b3ff 100644 --- a/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStep.cs +++ b/src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStep.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json.Linq; using Recyclarr.Common.Extensions; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.CustomFormat.Api; using Recyclarr.TrashLib.Services.CustomFormat.Models; @@ -7,16 +8,23 @@ namespace Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; internal class QualityProfileApiPersistenceStep : IQualityProfileApiPersistenceStep { + private readonly IQualityProfileService _api; private readonly List _invalidProfileNames = new(); private readonly Dictionary> _updatedScores = new(); public IDictionary> UpdatedScores => _updatedScores; public IReadOnlyCollection InvalidProfileNames => _invalidProfileNames; - public async Task Process(IQualityProfileService api, + public QualityProfileApiPersistenceStep(IQualityProfileService api) + { + _api = api; + } + + public async Task Process( + IServiceConfiguration config, IDictionary cfScores) { - var serviceProfiles = await api.GetQualityProfiles(); + var serviceProfiles = await _api.GetQualityProfiles(config); // Match quality profiles in Radarr to ones the user put in their config. // For each match, we return a tuple including the list of custom format scores ("formatItems"). @@ -71,7 +79,7 @@ internal class QualityProfileApiPersistenceStep : IQualityProfileApiPersistenceS } var jsonRoot = (JObject) formatItems.First().Root; - await api.UpdateQualityProfile(jsonRoot, jsonRoot.Value("id")); + await _api.UpdateQualityProfile(config, jsonRoot, jsonRoot.Value("id")); } } diff --git a/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs b/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs index fcf3dad3..3fd89917 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs @@ -1,4 +1,3 @@ -using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.QualitySize; using Recyclarr.TrashLib.Services.Radarr.Config; @@ -7,18 +6,15 @@ namespace Recyclarr.TrashLib.Services.Processors; public class RadarrProcessor : IServiceProcessor { - private readonly ILogger _log; private readonly ICustomFormatUpdater _cfUpdater; private readonly IQualitySizeUpdater _qualityUpdater; private readonly RadarrConfiguration _config; public RadarrProcessor( - ILogger log, ICustomFormatUpdater cfUpdater, IQualitySizeUpdater qualityUpdater, RadarrConfiguration config) { - _log = log; _cfUpdater = cfUpdater; _qualityUpdater = qualityUpdater; _config = config; @@ -26,23 +22,14 @@ public class RadarrProcessor : IServiceProcessor public async Task Process(ISyncSettings settings) { - var didWork = false; - if (_config.QualityDefinition != null) { - await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, SupportedServices.Radarr); - didWork = true; + await _qualityUpdater.Process(settings.Preview, _config); } if (_config.CustomFormats.Count > 0) { - await _cfUpdater.Process(settings.Preview, _config.CustomFormats, SupportedServices.Radarr); - didWork = true; - } - - if (!didWork) - { - _log.Information("Nothing to do"); + await _cfUpdater.Process(settings.Preview, _config); } } } diff --git a/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs b/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs index d81b2cc9..0ef49f94 100644 --- a/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs +++ b/src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs @@ -1,4 +1,3 @@ -using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.QualitySize; using Recyclarr.TrashLib.Services.ReleaseProfile; @@ -35,7 +34,7 @@ public class SonarrProcessor : IServiceProcessor public async Task Process(ISyncSettings settings) { // Any compatibility failures will be thrown as exceptions - _compatibilityEnforcer.Check(_config); + await _compatibilityEnforcer.Check(_config); var didWork = false; @@ -47,13 +46,13 @@ public class SonarrProcessor : IServiceProcessor if (_config.QualityDefinition != null) { - await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, SupportedServices.Sonarr); + await _qualityUpdater.Process(settings.Preview, _config); didWork = true; } if (_config.CustomFormats.Count > 0) { - await _cfUpdater.Process(settings.Preview, _config.CustomFormats, SupportedServices.Sonarr); + await _cfUpdater.Process(settings.Preview, _config); didWork = true; } diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/Api/IQualityDefinitionService.cs b/src/Recyclarr.TrashLib/Services/QualitySize/Api/IQualityDefinitionService.cs index 6ac12d38..ad3578ce 100644 --- a/src/Recyclarr.TrashLib/Services/QualitySize/Api/IQualityDefinitionService.cs +++ b/src/Recyclarr.TrashLib/Services/QualitySize/Api/IQualityDefinitionService.cs @@ -1,7 +1,12 @@ +using Recyclarr.TrashLib.Config.Services; + namespace Recyclarr.TrashLib.Services.QualitySize.Api; public interface IQualityDefinitionService { - Task> GetQualityDefinition(); - Task> UpdateQualityDefinition(IList newQuality); + Task> GetQualityDefinition(IServiceConfiguration config); + + Task> UpdateQualityDefinition( + IServiceConfiguration config, + IList newQuality); } diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/Api/QualityDefinitionService.cs b/src/Recyclarr.TrashLib/Services/QualitySize/Api/QualityDefinitionService.cs index 86c7f0a7..aa6bf923 100644 --- a/src/Recyclarr.TrashLib/Services/QualitySize/Api/QualityDefinitionService.cs +++ b/src/Recyclarr.TrashLib/Services/QualitySize/Api/QualityDefinitionService.cs @@ -1,4 +1,5 @@ using Flurl.Http; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Http; namespace Recyclarr.TrashLib.Services.QualitySize.Api; @@ -12,16 +13,17 @@ internal class QualityDefinitionService : IQualityDefinitionService _service = service; } - public async Task> GetQualityDefinition() + public async Task> GetQualityDefinition(IServiceConfiguration config) { - return await _service.Request("qualitydefinition") + return await _service.Request(config, "qualitydefinition") .GetJsonAsync>(); } public async Task> UpdateQualityDefinition( + IServiceConfiguration config, IList newQuality) { - return await _service.Request("qualityDefinition", "update") + return await _service.Request(config, "qualityDefinition", "update") .PutJsonAsync(newQuality) .ReceiveJson>(); } diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs b/src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs index b13b7e9a..6d8d438b 100644 --- a/src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs @@ -1,9 +1,8 @@ -using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; namespace Recyclarr.TrashLib.Services.QualitySize; public interface IQualitySizeUpdater { - Task Process(bool isPreview, QualityDefinitionConfig config, SupportedServices serviceType); + Task Process(bool isPreview, IServiceConfiguration config); } diff --git a/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs b/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs index 2fddac2e..30260e76 100644 --- a/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs @@ -1,5 +1,4 @@ using Recyclarr.Common.Extensions; -using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.QualitySize.Api; using Recyclarr.TrashLib.Services.QualitySize.Guide; @@ -26,40 +25,45 @@ internal class QualitySizeUpdater : IQualitySizeUpdater _guide = guide; } - public async Task Process(bool isPreview, QualityDefinitionConfig config, SupportedServices serviceType) + public async Task Process(bool isPreview, IServiceConfiguration config) { - _log.Information("Processing Quality Definition: {QualityDefinition}", config.Type); - var qualityDefinitions = _guide.GetQualitySizeData(serviceType); - var qualityTypeInConfig = config.Type; + var qualityDef = config.QualityDefinition; + if (qualityDef is null) + { + return; + } + var qualityDefinitions = _guide.GetQualitySizeData(config.ServiceType); var selectedQuality = qualityDefinitions - .FirstOrDefault(x => x.Type.EqualsIgnoreCase(qualityTypeInConfig)); + .FirstOrDefault(x => x.Type.EqualsIgnoreCase(qualityDef.Type)); if (selectedQuality == null) { - _log.Error("The specified quality definition type does not exist: {Type}", qualityTypeInConfig); + _log.Error("The specified quality definition type does not exist: {Type}", qualityDef.Type); return; } - if (config.PreferredRatio is not null) + _log.Information("Processing Quality Definition: {QualityDefinition}", qualityDef.Type); + + if (qualityDef.PreferredRatio is not null) { _log.Information("Using an explicit preferred ratio which will override values from the guide"); // Fix an out of range ratio and warn the user - if (config.PreferredRatio is < 0 or > 1) + if (qualityDef.PreferredRatio is < 0 or > 1) { - var clampedRatio = Math.Clamp(config.PreferredRatio.Value, 0, 1); + var clampedRatio = Math.Clamp(qualityDef.PreferredRatio.Value, 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}", - config.PreferredRatio, clampedRatio); + qualityDef.PreferredRatio, clampedRatio); - config.PreferredRatio = clampedRatio; + qualityDef.PreferredRatio = clampedRatio; } // Apply a calculated preferred size foreach (var quality in selectedQuality.Qualities) { - quality.Preferred = quality.InterpolatedPreferred(config.PreferredRatio.Value); + quality.Preferred = quality.InterpolatedPreferred(qualityDef.PreferredRatio.Value); } } @@ -69,7 +73,7 @@ internal class QualitySizeUpdater : IQualitySizeUpdater return; } - await ProcessQualityDefinition(selectedQuality.Qualities); + await ProcessQualityDefinition(config, selectedQuality.Qualities); } private void PrintQualityPreview(IReadOnlyCollection qualitySizeItems) @@ -98,9 +102,11 @@ internal class QualitySizeUpdater : IQualitySizeUpdater a.PreferredSize is not null && b.IsPreferredDifferent(a.PreferredSize); } - private async Task ProcessQualityDefinition(IReadOnlyCollection guideQuality) + private async Task ProcessQualityDefinition( + IServiceConfiguration config, + IReadOnlyCollection guideQuality) { - var serverQuality = await _api.GetQualityDefinition(); + var serverQuality = await _api.GetQualityDefinition(config); var newQuality = new List(); foreach (var qualityData in guideQuality) @@ -129,7 +135,7 @@ internal class QualitySizeUpdater : IQualitySizeUpdater serverEntry.PreferredSize); } - await _api.UpdateQualityDefinition(newQuality); + await _api.UpdateQualityDefinition(config, newQuality); _log.Information("Number of updated qualities: {Count}", newQuality.Count); } } diff --git a/src/Recyclarr.TrashLib/Services/Radarr/IRadarrCapabilityChecker.cs b/src/Recyclarr.TrashLib/Services/Radarr/IRadarrCapabilityChecker.cs deleted file mode 100644 index d7307613..00000000 --- a/src/Recyclarr.TrashLib/Services/Radarr/IRadarrCapabilityChecker.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Recyclarr.TrashLib.Services.Radarr; - -public interface IRadarrCapabilityChecker -{ - RadarrCapabilities? GetCapabilities(); -} diff --git a/src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs b/src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs index 286c3748..28828ede 100644 --- a/src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs +++ b/src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs @@ -6,7 +6,6 @@ public class RadarrAutofacModule : Module { protected override void Load(ContainerBuilder builder) { - builder.RegisterType().As() - .InstancePerLifetimeScope(); + builder.RegisterType().InstancePerLifetimeScope(); } } diff --git a/src/Recyclarr.TrashLib/Services/Radarr/RadarrCapabilityChecker.cs b/src/Recyclarr.TrashLib/Services/Radarr/RadarrCapabilityChecker.cs index 2464cfd6..7e2e21d0 100644 --- a/src/Recyclarr.TrashLib/Services/Radarr/RadarrCapabilityChecker.cs +++ b/src/Recyclarr.TrashLib/Services/Radarr/RadarrCapabilityChecker.cs @@ -3,7 +3,7 @@ using Recyclarr.TrashLib.Services.System; namespace Recyclarr.TrashLib.Services.Radarr; -public class RadarrCapabilityChecker : ServiceCapabilityChecker, IRadarrCapabilityChecker +public class RadarrCapabilityChecker : ServiceCapabilityChecker { public RadarrCapabilityChecker(IServiceInformation info) : base(info) diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/IReleaseProfileApiService.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/IReleaseProfileApiService.cs index f14cdffa..09e34050 100644 --- a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/IReleaseProfileApiService.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/IReleaseProfileApiService.cs @@ -1,11 +1,12 @@ +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api; public interface IReleaseProfileApiService { - Task UpdateReleaseProfile(SonarrReleaseProfile profile); - Task CreateReleaseProfile(SonarrReleaseProfile profile); - Task> GetReleaseProfiles(); - Task DeleteReleaseProfile(int releaseProfileId); + Task UpdateReleaseProfile(IServiceConfiguration config, SonarrReleaseProfile profile); + Task CreateReleaseProfile(IServiceConfiguration config, SonarrReleaseProfile profile); + Task> GetReleaseProfiles(IServiceConfiguration config); + Task DeleteReleaseProfile(IServiceConfiguration config, int releaseProfileId); } diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs index d1a9da94..7a87ecd9 100644 --- a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs @@ -1,10 +1,13 @@ using Newtonsoft.Json.Linq; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api; public interface ISonarrReleaseProfileCompatibilityHandler { - object CompatibleReleaseProfileForSending(SonarrReleaseProfile profile); + Task CompatibleReleaseProfileForSending(IServiceConfiguration config, + SonarrReleaseProfile profile); + SonarrReleaseProfile CompatibleReleaseProfileForReceiving(JObject profile); } diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ReleaseProfileApiService.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ReleaseProfileApiService.cs index 13ad1552..91ebe509 100644 --- a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ReleaseProfileApiService.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/ReleaseProfileApiService.cs @@ -1,5 +1,6 @@ using Flurl.Http; using Newtonsoft.Json.Linq; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Http; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; @@ -18,27 +19,29 @@ public class ReleaseProfileApiService : IReleaseProfileApiService _service = service; } - public async Task UpdateReleaseProfile(SonarrReleaseProfile profile) + public async Task UpdateReleaseProfile(IServiceConfiguration config, SonarrReleaseProfile profile) { - var profileToSend = _profileHandler.CompatibleReleaseProfileForSending(profile); - await _service.Request("releaseprofile", profile.Id) + var profileToSend = await _profileHandler.CompatibleReleaseProfileForSending(config, profile); + await _service.Request(config, "releaseprofile", profile.Id) .PutJsonAsync(profileToSend); } - public async Task CreateReleaseProfile(SonarrReleaseProfile profile) + public async Task CreateReleaseProfile( + IServiceConfiguration config, + SonarrReleaseProfile profile) { - var profileToSend = _profileHandler.CompatibleReleaseProfileForSending(profile); + var profileToSend = _profileHandler.CompatibleReleaseProfileForSending(config, profile); - var response = await _service.Request("releaseprofile") + var response = await _service.Request(config, "releaseprofile") .PostJsonAsync(profileToSend) .ReceiveJson(); return _profileHandler.CompatibleReleaseProfileForReceiving(response); } - public async Task> GetReleaseProfiles() + public async Task> GetReleaseProfiles(IServiceConfiguration config) { - var response = await _service.Request("releaseprofile") + var response = await _service.Request(config, "releaseprofile") .GetJsonAsync>(); return response @@ -46,9 +49,9 @@ public class ReleaseProfileApiService : IReleaseProfileApiService .ToList(); } - public async Task DeleteReleaseProfile(int releaseProfileId) + public async Task DeleteReleaseProfile(IServiceConfiguration config, int releaseProfileId) { - await _service.Request("releaseprofile", releaseProfileId) + await _service.Request(config, "releaseprofile", releaseProfileId) .DeleteAsync(); } } diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs index d00afbcf..043565ab 100644 --- a/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs @@ -1,6 +1,7 @@ using AutoMapper; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.ExceptionTypes; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Schemas; @@ -24,9 +25,11 @@ public class SonarrReleaseProfileCompatibilityHandler : ISonarrReleaseProfileCom _mapper = mapper; } - public object CompatibleReleaseProfileForSending(SonarrReleaseProfile profile) + public async Task CompatibleReleaseProfileForSending( + IServiceConfiguration config, + SonarrReleaseProfile profile) { - var capabilities = _capabilityChecker.GetCapabilities(); + var capabilities = await _capabilityChecker.GetCapabilities(config); if (capabilities is null) { throw new ServiceIncompatibilityException("Capabilities could not be obtained"); diff --git a/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileUpdater.cs b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileUpdater.cs index 9f461335..7d711354 100644 --- a/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileUpdater.cs +++ b/src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileUpdater.cs @@ -1,4 +1,5 @@ using Recyclarr.Common.Extensions; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.ReleaseProfile.Api; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects; using Recyclarr.TrashLib.Services.ReleaseProfile.Filters; @@ -65,7 +66,7 @@ public class ReleaseProfileUpdater : IReleaseProfileUpdater return; } - await ProcessReleaseProfiles(filteredProfiles); + await ProcessReleaseProfiles(config, filteredProfiles); } private void PreviewReleaseProfiles(IEnumerable profiles) @@ -139,40 +140,42 @@ public class ReleaseProfileUpdater : IReleaseProfileUpdater } private async Task ProcessReleaseProfiles( + IServiceConfiguration config, List<(ReleaseProfileData Profile, IReadOnlyCollection Tags)> profilesAndTags) { // Obtain all of the existing release profiles first. If any were previously created by our program // here, we favor replacing those instead of creating new ones, which would just be mostly duplicates // (but with some differences, since there have likely been updates since the last run). - var existingProfiles = await _releaseProfileApi.GetReleaseProfiles(); + var existingProfiles = await _releaseProfileApi.GetReleaseProfiles(config); foreach (var (profile, tags) in profilesAndTags) { // If tags were provided, ensure they exist. Tags that do not exist are added first, so that we // may specify them with the release profile request payload. - var tagIds = await CreateTagsInSonarr(tags); + var tagIds = await CreateTagsInSonarr(config, tags); var title = BuildProfileTitle(profile.Name); var profileToUpdate = GetProfileToUpdate(existingProfiles, title); if (profileToUpdate != null) { _log.Information("Update existing profile: {ProfileName}", title); - await UpdateExistingProfile(profileToUpdate, profile, tagIds); + await UpdateExistingProfile(config, profileToUpdate, profile, tagIds); } else { _log.Information("Create new profile: {ProfileName}", title); - await CreateNewProfile(title, profile, tagIds); + await CreateNewProfile(config, title, profile, tagIds); } } // Any profiles with `[Trash]` in front of their name are managed exclusively by Recyclarr. As such, if // there are any still in Sonarr that we didn't update, those are most certainly old and shouldn't be kept // around anymore. - await DeleteOldManagedProfiles(profilesAndTags, existingProfiles); + await DeleteOldManagedProfiles(config, profilesAndTags, existingProfiles); } private async Task DeleteOldManagedProfiles( + IServiceConfiguration config, IEnumerable<(ReleaseProfileData Profile, IReadOnlyCollection Tags)> profilesAndTags, IEnumerable sonarrProfiles) { @@ -187,32 +190,37 @@ public class ReleaseProfileUpdater : IReleaseProfileUpdater foreach (var profile in sonarrProfilesToDelete) { _log.Information("Deleting old Trash release profile: {ProfileName}", profile.Name); - await _releaseProfileApi.DeleteReleaseProfile(profile.Id); + await _releaseProfileApi.DeleteReleaseProfile(config, profile.Id); } } - private async Task> CreateTagsInSonarr(IReadOnlyCollection tags) + private async Task> CreateTagsInSonarr( + IServiceConfiguration config, + IReadOnlyCollection tags) { if (!tags.Any()) { return Array.Empty(); } - var sonarrTags = await _tagApiService.GetTags(); - await CreateMissingTags(sonarrTags, tags); + var sonarrTags = await _tagApiService.GetTags(config); + await CreateMissingTags(config, sonarrTags, tags); return sonarrTags .Where(t => tags.Any(ct => ct.EqualsIgnoreCase(t.Label))) .Select(t => t.Id) .ToList(); } - private async Task CreateMissingTags(ICollection sonarrTags, IEnumerable configTags) + private async Task CreateMissingTags( + IServiceConfiguration config, + ICollection sonarrTags, + IEnumerable configTags) { var missingTags = configTags.Where(t => !sonarrTags.Any(t2 => t2.Label.EqualsIgnoreCase(t))); foreach (var tag in missingTags) { _log.Debug("Creating Tag: {Tag}", tag); - var newTag = await _tagApiService.CreateTag(tag); + var newTag = await _tagApiService.CreateTag(config, tag); sonarrTags.Add(newTag); } } @@ -243,18 +251,25 @@ public class ReleaseProfileUpdater : IReleaseProfileUpdater profileToUpdate.Tags = tagIds; } - private async Task UpdateExistingProfile(SonarrReleaseProfile profileToUpdate, ReleaseProfileData profile, + private async Task UpdateExistingProfile( + IServiceConfiguration config, + SonarrReleaseProfile profileToUpdate, + ReleaseProfileData profile, IReadOnlyCollection tagIds) { _log.Debug("Update existing profile with id {ProfileId}", profileToUpdate.Id); SetupProfileRequestObject(profileToUpdate, profile, tagIds); - await _releaseProfileApi.UpdateReleaseProfile(profileToUpdate); + await _releaseProfileApi.UpdateReleaseProfile(config, profileToUpdate); } - private async Task CreateNewProfile(string title, ReleaseProfileData profile, IReadOnlyCollection tagIds) + private async Task CreateNewProfile( + IServiceConfiguration config, + string title, + ReleaseProfileData profile, + IReadOnlyCollection tagIds) { var newProfile = new SonarrReleaseProfile {Name = title, Enabled = true}; SetupProfileRequestObject(newProfile, profile, tagIds); - await _releaseProfileApi.CreateReleaseProfile(newProfile); + await _releaseProfileApi.CreateReleaseProfile(config, newProfile); } } diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrTagApiService.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrTagApiService.cs index e815e335..f281b80e 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrTagApiService.cs +++ b/src/Recyclarr.TrashLib/Services/Sonarr/Api/ISonarrTagApiService.cs @@ -1,9 +1,10 @@ +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; namespace Recyclarr.TrashLib.Services.Sonarr.Api; public interface ISonarrTagApiService { - Task> GetTags(); - Task CreateTag(string tag); + Task> GetTags(IServiceConfiguration config); + Task CreateTag(IServiceConfiguration config, string tag); } diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrTagApiService.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrTagApiService.cs index 142f2618..2c9c5651 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrTagApiService.cs +++ b/src/Recyclarr.TrashLib/Services/Sonarr/Api/SonarrTagApiService.cs @@ -1,4 +1,5 @@ using Flurl.Http; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Http; using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; @@ -13,15 +14,15 @@ public class SonarrTagApiService : ISonarrTagApiService _service = service; } - public async Task> GetTags() + public async Task> GetTags(IServiceConfiguration config) { - return await _service.Request("tag") + return await _service.Request(config, "tag") .GetJsonAsync>(); } - public async Task CreateTag(string tag) + public async Task CreateTag(IServiceConfiguration config, string tag) { - return await _service.Request("tag") + return await _service.Request(config, "tag") .PostJsonAsync(new {label = tag}) .ReceiveJson(); } diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Capabilities/ISonarrCapabilityChecker.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Capabilities/ISonarrCapabilityChecker.cs index 36574b78..ccc4a64e 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Capabilities/ISonarrCapabilityChecker.cs +++ b/src/Recyclarr.TrashLib/Services/Sonarr/Capabilities/ISonarrCapabilityChecker.cs @@ -1,6 +1,8 @@ +using Recyclarr.TrashLib.Config.Services; + namespace Recyclarr.TrashLib.Services.Sonarr.Capabilities; public interface ISonarrCapabilityChecker { - SonarrCapabilities? GetCapabilities(); + Task GetCapabilities(IServiceConfiguration config); } diff --git a/src/Recyclarr.TrashLib/Services/Sonarr/Capabilities/SonarrCapabilityEnforcer.cs b/src/Recyclarr.TrashLib/Services/Sonarr/Capabilities/SonarrCapabilityEnforcer.cs index e43b0fc9..cf252565 100644 --- a/src/Recyclarr.TrashLib/Services/Sonarr/Capabilities/SonarrCapabilityEnforcer.cs +++ b/src/Recyclarr.TrashLib/Services/Sonarr/Capabilities/SonarrCapabilityEnforcer.cs @@ -13,9 +13,9 @@ public class SonarrCapabilityEnforcer _capabilityChecker = capabilityChecker; } - public void Check(SonarrConfiguration config) + public async Task Check(SonarrConfiguration config) { - var capabilities = _capabilityChecker.GetCapabilities(); + var capabilities = await _capabilityChecker.GetCapabilities(config); if (capabilities is null) { throw new ServiceIncompatibilityException("Capabilities could not be obtained"); diff --git a/src/Recyclarr.TrashLib/Services/System/IServiceInformation.cs b/src/Recyclarr.TrashLib/Services/System/IServiceInformation.cs index 26c2ac4c..4049b51b 100644 --- a/src/Recyclarr.TrashLib/Services/System/IServiceInformation.cs +++ b/src/Recyclarr.TrashLib/Services/System/IServiceInformation.cs @@ -1,6 +1,8 @@ +using Recyclarr.TrashLib.Config.Services; + namespace Recyclarr.TrashLib.Services.System; public interface IServiceInformation { - IObservable Version { get; } + public Task GetVersion(IServiceConfiguration config); } diff --git a/src/Recyclarr.TrashLib/Services/System/ISystemApiService.cs b/src/Recyclarr.TrashLib/Services/System/ISystemApiService.cs index ec07e798..dd1744ec 100644 --- a/src/Recyclarr.TrashLib/Services/System/ISystemApiService.cs +++ b/src/Recyclarr.TrashLib/Services/System/ISystemApiService.cs @@ -1,8 +1,9 @@ +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Services.System.Dto; namespace Recyclarr.TrashLib.Services.System; public interface ISystemApiService { - Task GetStatus(); + Task GetStatus(IServiceConfiguration config); } diff --git a/src/Recyclarr.TrashLib/Services/System/ServiceInformation.cs b/src/Recyclarr.TrashLib/Services/System/ServiceInformation.cs index 4e8aaa6c..b9ff77b5 100644 --- a/src/Recyclarr.TrashLib/Services/System/ServiceInformation.cs +++ b/src/Recyclarr.TrashLib/Services/System/ServiceInformation.cs @@ -1,40 +1,33 @@ -using System.Reactive.Concurrency; -using System.Reactive.Linq; using Flurl.Http; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Http; -using Recyclarr.TrashLib.Services.System.Dto; namespace Recyclarr.TrashLib.Services.System; public class ServiceInformation : IServiceInformation { + private readonly ISystemApiService _api; private readonly ILogger _log; public ServiceInformation(ISystemApiService api, ILogger log) { + _api = api; _log = log; - Version = Observable.FromAsync(async () => await api.GetStatus(), ThreadPoolScheduler.Instance) - .Timeout(TimeSpan.FromSeconds(15)) - .Do(LogServiceInfo) - .Select(x => new Version(x.Version)) - .Catch((Exception ex) => - { - log.Error("Exception trying to obtain service version: {Message}", ex switch - { - FlurlHttpException flex => flex.SanitizedExceptionMessage(), - _ => ex.Message - }); - - return Observable.Return((Version?) null); - }) - .Replay(1) - .AutoConnect(); } - public IObservable Version { get; } - - private void LogServiceInfo(SystemStatus status) + public async Task GetVersion(IServiceConfiguration config) { - _log.Debug("{Service} Version: {Version}", status.AppName, status.Version); + try + { + var status = await _api.GetStatus(config); + _log.Debug("{Service} Version: {Version}", status.AppName, status.Version); + return new Version(status.Version); + } + catch (FlurlHttpException ex) + { + _log.Error("Exception trying to obtain service version: {Message}", ex.SanitizedExceptionMessage()); + } + + return null; } } diff --git a/src/Recyclarr.TrashLib/Services/System/SystemApiService.cs b/src/Recyclarr.TrashLib/Services/System/SystemApiService.cs index 350f6d24..0ddb79f8 100644 --- a/src/Recyclarr.TrashLib/Services/System/SystemApiService.cs +++ b/src/Recyclarr.TrashLib/Services/System/SystemApiService.cs @@ -1,4 +1,5 @@ using Flurl.Http; +using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Http; using Recyclarr.TrashLib.Services.System.Dto; @@ -13,9 +14,9 @@ public class SystemApiService : ISystemApiService _service = service; } - public async Task GetStatus() + public async Task GetStatus(IServiceConfiguration config) { - return await _service.Request("system", "status") + return await _service.Request(config, "system", "status") .GetJsonAsync(); } } diff --git a/src/Recyclarr.sln.DotSettings b/src/Recyclarr.sln.DotSettings index 0fe82f0f..d86cf58c 100644 --- a/src/Recyclarr.sln.DotSettings +++ b/src/Recyclarr.sln.DotSettings @@ -112,5 +112,6 @@ &lt;/Language&gt; &lt;/profile&gt;</RIDER_SETTINGS><CSharpFormatDocComments>True</CSharpFormatDocComments><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly></Profile> Recyclarr Cleanup + True True True \ No newline at end of file From cffb8d783a0f17767d36940979fba045f87601e4 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Mon, 23 Jan 2023 07:52:09 -0600 Subject: [PATCH 08/10] refactor: Convert processors/updaters to pipelines --- .editorconfig | 9 +- schemas/config-schema.json | 13 +- .../.idea.Recyclarr/.idea/indexLayout.xml | 4 +- .../list release-profiles --terms.run.xml | 2 +- .../radarr___list_custom_formats.xml | 20 - .../radarr___list_qualities.xml | 20 - .../runConfigurations/radarr___preview.xml | 20 - .../.idea/runConfigurations/sonarr.xml | 20 - .../sonarr___list_custom_formats.xml | 20 - .../sonarr___list_qualities.xml | 20 - .../sonarr___list_release_profiles.xml | 20 - .../runConfigurations/sonarr___preview.xml | 20 - .../sonarr__custom_config_dir_.xml | 23 - .../{radarr.xml => sync___preview.xml} | 4 +- src/Directory.Build.props | 9 + src/Directory.Packages.props | 2 + .../IntegrationFixture.cs | 8 +- .../AutoMapperConfigurationTest.cs | 1 - .../BaseCommandSetupIntegrationTest.cs | 3 - .../CompositionRootTest.cs | 2 - .../Console/Helpers/CacheStoragePathTest.cs | 3 - src/Recyclarr.Cli.Tests/LogJanitorTest.cs | 4 - .../Migration/MigrationExecutorTest.cs | 3 - .../MigrateTrashUpdaterAppDataDirTest.cs | 4 - .../Migration/Steps/MigrateTrashYmlTest.cs | 4 - .../ServiceCompatibilityIntegrationTest.cs | 3 - src/Recyclarr.Cli/CompositionRoot.cs | 50 ++- .../Console/Commands/ConfigCreateCommand.cs | 2 +- .../Commands/ListCustomFormatsCommand.cs | 2 +- .../Console/Commands/ListQualitiesCommand.cs | 2 +- .../Commands/ListReleaseProfilesCommand.cs | 2 +- .../Console/Commands/RadarrCommand.cs | 6 +- .../Console/Commands/SonarrCommand.cs | 8 +- .../Console/Commands/SyncCommand.cs | 2 +- .../CommonMockFileSystemExtensions.cs | 38 +- .../Extensions/DictionaryExtensionsTest.cs | 2 - .../Extensions/FileSystemExtensionsTest.cs | 10 +- .../Extensions/StringExtensionsTest.cs | 2 - src/Recyclarr.Common.Tests/JsonUtilsTest.cs | 5 - .../ResourceDataReaderTest.cs | 3 - .../Autofac/LifetimeScopedValue.cs | 21 - .../Extensions/CollectionExtensions.cs | 16 +- .../Extensions/FileSystemExtensions.cs | 5 +- .../Extensions/JsonNetExtensions.cs | 7 + .../Extensions/StringExtensions.cs | 5 + .../FluentValidationExtensions.cs | 11 +- .../NullableChildValidatorAdaptor.cs | 4 +- .../GenericEqualityComparer.cs | 43 ++ .../YamlDotNet/ForceEmptySequences.cs | 7 +- .../YamlDotNet/ValidatingDeserializer.cs | 7 +- src/Recyclarr.Gui/Pages/Error.cshtml | 2 + .../StreamBuilderTest.cs | 3 - .../StringUtilsTest.cs | 3 - .../AutoFixture/AutoMockDataAttribute.cs | 1 - .../InlineAutoMockDataAttribute.cs | 1 - .../MockFileSystemSpecimenBuilder.cs | 1 - .../AutofacTestExtensions.cs | 1 - src/Recyclarr.TestLibrary/FileUtils.cs | 1 - .../FluentAssertions/JsonEquivalencyStep.cs | 4 +- src/Recyclarr.TestLibrary/MockData.cs | 1 - .../MockFileSystemExtensions.cs | 1 - .../CfTestUtils.cs | 16 - .../MockSyncSettings.cs | 30 ++ src/Recyclarr.TrashLib.TestLibrary/NewCf.cs | 48 +- src/Recyclarr.TrashLib.TestLibrary/NewQp.cs | 25 ++ .../Cache/ServiceCacheTest.cs | 10 +- ...ironmentVariableNotDefinedExceptionTest.cs | 2 - .../Config/Parsing/ConfigRegistryTest.cs | 57 ++- .../Parsing/ConfigValidationExecutorTest.cs | 2 - .../Config/Parsing/ConfigurationFinderTest.cs | 5 - .../Parsing/ConfigurationLoaderEnvVarTest.cs | 17 +- .../Parsing/ConfigurationLoaderSecretsTest.cs | 9 +- .../Config/Parsing/ConfigurationLoaderTest.cs | 17 +- .../Secrets/SecretNotFoundExceptionTest.cs | 2 - .../ServiceConfigurationValidatorTest.cs | 1 - .../Config/Settings/SettingsPersisterTest.cs | 5 - .../CustomFormat/CachePersisterTest.cs | 93 ++-- .../Guide/CustomFormatCategoryParserTest.cs | 7 +- .../Guide/CustomFormatLoaderTest.cs | 34 +- .../CustomFormat/GuideDataListerTest.cs | 7 +- .../Data/ImportableCustomFormat1.json | 15 - .../ImportableCustomFormat1_Processed.json | 13 - .../Data/ImportableCustomFormat2.json | 15 - .../ImportableCustomFormat2_Processed.json | 13 - .../CustomFormat/Processors/Data/NoScore.json | 4 - .../Processors/Data/WontBeInConfig.json | 5 - .../Processors/GuideProcessorTest.cs | 167 ------- .../Processors/GuideSteps/ConfigStepTest.cs | 110 ----- .../GuideSteps/CustomFormatStepTest.cs | 155 ------- .../GuideSteps/QualityProfileStepTest.cs | 130 ------ .../Processors/PersistenceProcessorTest.cs | 53 --- .../CustomFormatApiPersistenceStepTest.cs | 43 -- .../JsonTransactionStepTest.cs | 359 --------------- .../QualityProfileApiPersistenceStepTest.cs | 257 ----------- .../GlobalTestSetup.cs | 2 - src/Recyclarr.TrashLib.Tests/GlobalUsings.cs | 3 + .../Guide/CustomFormatParserTest.cs | 116 +++++ .../Models/CustomFormatDataComparerTest.cs | 371 ++++++++++++++++ .../CustomFormatConfigPhaseTest.cs | 77 ++++ .../CustomFormatTransactionPhaseTest.cs | 413 ++++++++++++++++++ .../QualityProfileConfigPhaseTest.cs | 294 +++++++++++++ .../QualityProfileTransactionPhaseTest.cs | 245 +++++++++++ .../Guide/Data/invalid_quality_size.json | 3 + .../QualitySize/Guide/Data/quality_size.json | 18 + .../Guide/QualityGuideServiceTest.cs | 32 ++ .../Guide/QualitySizeGuideParserTest.cs | 53 +++ .../QualitySizeGuidePhaseTest.cs | 140 ++++++ .../QualitySizeTransactionPhaseTest.cs | 108 +++++ .../PipelinePhases/TagApiFetchPhaseTest.cs | 35 ++ .../TagApiPersistencePhaseTest.cs | 40 ++ .../Tags/PipelinePhases/TagConfigPhaseTest.cs | 39 ++ .../PipelinePhases/TagTransactionPhaseTest.cs | 50 +++ .../QualityDefinition/QualitySizeDataTest.cs | 12 +- .../Repo/VersionControl/GitPathTest.cs | 5 - .../Services/GuideServiceTest.cs | 10 +- .../Processors/ConfigCreationProcessorTest.cs | 4 +- .../Services/ReleaseProfileDataListerTest.cs | 9 +- .../SonarrQualityDataTest.cs | 12 +- .../Filters/ReleaseProfileDataFiltererTest.cs | 9 +- ...eleaseProfileDataValidationFiltererTest.cs | 7 +- .../Filters/StrictNegativeScoresFilterTest.cs | 9 +- .../ReleaseProfileDataValidatorTest.cs | 4 +- .../Sonarr/ReleaseProfile/ScopedStateTest.cs | 4 +- .../Sonarr/SonarrCapabilityEnforcerTest.cs | 9 +- .../Sonarr/SonarrCompatibilityTest.cs | 9 +- .../SonarrConfigurationValidatorTest.cs | 3 +- .../Startup/DefaultAppDataSetupTest.cs | 6 - .../ApiServicesAutofacModule.cs} | 5 +- .../System/Dto/SystemStatus.cs | 2 +- .../System/IServiceInformation.cs | 2 +- .../System/ISystemApiService.cs | 4 +- .../System/ServiceInformation.cs | 2 +- .../System/SystemApiService.cs | 4 +- .../CompatibilityAutofacModule.cs | 21 + .../Radarr/RadarrCapabilities.cs | 2 +- .../Radarr/RadarrCapabilityChecker.cs | 5 +- .../ServiceCapabilityChecker.cs | 4 +- .../Sonarr}/ISonarrCapabilityChecker.cs | 2 +- .../Sonarr}/SonarrCapabilities.cs | 2 +- .../Sonarr}/SonarrCapabilityChecker.cs | 5 +- .../Sonarr}/SonarrCapabilityEnforcer.cs | 4 +- .../Config/ConfigAutofacModule.cs | 3 - .../EnvironmentVariablesDeserializer.cs | 5 +- .../Config/Parsing/ConfigParser.cs | 4 +- .../Config/Parsing/ConfigRegistry.cs | 17 +- .../Config/Parsing/ConfigurationException.cs | 4 +- .../Config/Parsing/ConfigurationLoader.cs | 26 +- .../Config/Parsing/IConfigRegistry.cs | 4 +- .../Config/Secrets/SecretsDeserializer.cs | 5 +- .../Config/Services/IServiceConfiguration.cs | 7 +- .../Services/Radarr}/RadarrConfiguration.cs | 5 +- .../Radarr}/RadarrConfigurationValidator.cs | 2 +- .../Config/Services/ServiceConfiguration.cs | 27 +- .../Services/ServiceConfigurationValidator.cs | 4 +- .../Services/Sonarr}/SonarrConfiguration.cs | 4 +- .../Sonarr}/SonarrConfigurationValidator.cs | 2 +- .../Http/FlurlClientFactory.cs | 9 +- .../Json/ServiceContractResolver.cs | 51 +++ .../Json/ServiceJsonSerializerFactory.cs | 23 + .../CustomFormat/Api/CustomFormatService.cs | 41 ++ .../CustomFormat/Api/ICustomFormatService.cs | 12 + .../CustomFormat/Cache/CacheException.cs | 9 + .../CustomFormat/Cache/CachePersister.cs | 46 ++ .../CustomFormat/Cache/ICachePersister.cs | 10 + .../Cache/ProcessedCustomFormatCache.cs | 24 + .../CustomFormat/CustomFormatAutofacModule.cs | 31 ++ .../CustomFormat/CustomFormatSyncPipeline.cs | 74 ++++ .../Guide/CustomFormatCategoryItem.cs | 2 +- .../Guide/CustomFormatCategoryParser.cs | 2 +- .../Guide/CustomFormatDataLister.cs | 2 +- .../Guide/CustomFormatGuideService.cs | 12 +- .../CustomFormat/Guide/CustomFormatLoader.cs | 7 +- .../CustomFormat/Guide/CustomFormatParser.cs | 18 + .../CustomFormat/Guide/CustomFormatPaths.cs | 2 +- .../Guide/ICustomFormatCategoryParser.cs | 2 +- .../Guide/ICustomFormatGuideService.cs | 4 +- .../CustomFormat/Guide/ICustomFormatLoader.cs | 11 + .../CustomFormat/Guide/ICustomFormatParser.cs | 4 +- .../Models/ConflictingCustomFormat.cs | 6 + .../CustomFormat/Models/CustomFormatCache.cs | 40 ++ .../CustomFormat/Models/CustomFormatData.cs | 52 +++ .../Models/CustomFormatDataComparer.cs | 57 +++ .../Models/FieldsArrayJsonConverter.cs | 52 +++ .../Models/ProcessedConfigData.cs | 6 +- .../CustomFormatApiFetchPhase.cs | 21 + .../CustomFormatApiPersistencePhase.cs | 39 ++ .../PipelinePhases/CustomFormatConfigPhase.cs | 50 +++ .../CustomFormatPreviewPhase.cs | 85 ++++ .../CustomFormatTransactionPhase.cs | 116 +++++ .../Pipelines/IPipelineCache.cs | 9 + .../Pipelines/ISyncPipeline.cs | 9 + .../Api/IQualityProfileService.cs | 9 + .../QualityProfile/Api/QualityProfileDto.cs | 32 ++ .../Api/QualityProfileService.cs | 15 +- .../QualityProfileApiFetchPhase.cs | 19 + .../QualityProfileApiPersistencePhase.cs | 62 +++ .../QualityProfileConfigPhase.cs | 137 ++++++ .../QualityProfilePreviewPhase.cs | 57 +++ .../QualityProfileTransactionPhase.cs | 99 +++++ .../QualityProfileAutofacModule.cs | 23 + .../QualityProfileSyncPipeline.cs | 47 ++ .../QualityProfile}/UpdatedFormatScore.cs | 5 +- .../Api/IQualityDefinitionService.cs | 4 +- .../Api/QualityDefinitionService.cs | 4 +- .../Api/ServiceQualityDefinitionItem.cs | 2 +- .../QualitySize/Guide/IQualityGuideService.cs | 2 +- .../QualitySize/Guide/QualityGuideService.cs | 10 +- .../QualitySize/Guide/QualityItem.cs | 2 +- .../Guide/QualitySizeDataLister.cs | 2 +- .../Guide/QualitySizeGuideParser.cs | 22 +- .../QualitySize/Guide/QualitySizePaths.cs | 2 +- .../QualitySizeApiFetchPhase.cs | 19 + .../QualitySizeApiPersistencePhase.cs | 22 + .../PipelinePhases/QualitySizeGuidePhase.cs | 68 +++ .../PipelinePhases/QualitySizePreviewPhase.cs | 33 ++ .../QualitySizeTransactionPhase.cs | 54 +++ .../QualitySize/QualitySizeAutofacModule.cs | 26 ++ .../Pipelines/QualitySize/QualitySizeData.cs | 7 + .../QualitySize/QualitySizeItem.cs | 4 +- .../QualitySize/QualitySizeSyncPipeline.cs | 46 ++ .../Api/IReleaseProfileApiService.cs | 4 +- ...onarrReleaseProfileCompatibilityHandler.cs | 7 +- .../Mappings/SonarrApiObjectMappingProfile.cs | 4 +- .../Api/Objects/SonarrReleaseProfile.cs | 2 +- .../Api/ReleaseProfileApiService.cs | 6 +- .../Api/Schemas/SonarrReleaseProfileSchema.cs | 2 +- ...onarrReleaseProfileCompatibilityHandler.cs | 8 +- .../Filters/IReleaseProfileFilter.cs | 4 +- .../Filters/IReleaseProfileFilterPipeline.cs | 4 +- .../Filters/IncludeExcludeFilter.cs | 4 +- .../Filters/ReleaseProfileDataFilterer.cs | 19 +- .../ReleaseProfileDataValidationFilterer.cs | 2 +- .../Filters/ReleaseProfileFilterPipeline.cs | 4 +- .../Filters/StrictNegativeScoresFilter.cs | 4 +- .../Guide/IReleaseProfileGuideService.cs | 2 +- .../Guide/ReleaseProfileDataLister.cs | 2 +- .../Guide/ReleaseProfileGuideParser.cs | 5 +- .../Guide/ReleaseProfileGuideService.cs | 23 + .../Models/ReleaseProfileTransactionData.cs | 9 + .../ReleaseProfileApiFetchPhase.cs | 21 + .../ReleaseProfileApiPersistencePhase.cs | 38 ++ .../ReleaseProfileConfigPhase.cs | 60 +++ .../ReleaseProfilePreviewPhase.cs | 91 ++++ .../ReleaseProfileTransactionPhase.cs | 74 ++++ .../ReleaseProfileAutofacModule.cs | 26 +- .../ReleaseProfile/ReleaseProfileData.cs | 2 +- .../ReleaseProfileDataValidator.cs | 2 +- .../ReleaseProfileSyncPipeline.cs | 55 +++ .../ReleaseProfile/ScopedState.cs | 2 +- .../ReleaseProfile/TermDataConverter.cs | 7 +- .../Tags}/Api/ISonarrTagApiService.cs | 3 +- .../Pipelines/Tags/Api/SonarrTag.cs | 13 + .../Tags}/Api/SonarrTagApiService.cs | 3 +- .../Tags/PipelinePhases/TagApiFetchPhase.cs | 24 + .../PipelinePhases/TagApiPersistencePhase.cs | 33 ++ .../Tags/PipelinePhases/TagConfigPhase.cs | 18 + .../Tags/PipelinePhases/TagPreviewPhase.cs | 35 ++ .../PipelinePhases/TagTransactionPhase.cs | 19 + .../Pipelines/Tags/ServiceTagCache.cs | 27 ++ .../Pipelines/Tags/TagSyncPipeline.cs | 56 +++ .../Pipelines/Tags/TagsAutofacModule.cs | 27 ++ .../Processors/ConfigCreationProcessor.cs | 2 +- .../{Services => }/Processors/ExitStatus.cs | 2 +- .../Processors/IConfigCreationProcessor.cs | 2 +- .../Processors/IServiceProcessor.cs | 8 + .../Processors/ISyncProcessor.cs | 2 +- .../Processors/ISyncSettings.cs | 4 +- .../ServiceProcessorsAutofacModule.cs | 14 + .../Processors/SyncPipelineExecutor.cs | 29 ++ .../Processors/SyncProcessor.cs | 54 +-- .../Recyclarr.TrashLib.csproj | 1 + .../CustomFormat/Api/CustomFormatService.cs | 48 -- .../CustomFormat/Api/ICustomFormatService.cs | 13 - .../Api/IQualityProfileService.cs | 10 - .../Services/CustomFormat/CachePersister.cs | 67 --- .../CustomFormat/CustomFormatAutofacModule.cs | 36 -- .../CustomFormat/CustomFormatUpdater.cs | 297 ------------- .../CustomFormat/Guide/CustomFormatParser.cs | 34 -- .../CustomFormat/Guide/ICustomFormatLoader.cs | 10 - .../Services/CustomFormat/ICachePersister.cs | 13 - .../CustomFormat/ICustomFormatUpdater.cs | 8 - .../Models/Cache/CustomFormatCache.cs | 15 - .../Models/ConflictingCustomFormat.cs | 6 - .../CustomFormat/Models/CustomFormatData.cs | 15 - .../Models/ProcessedCustomFormatData.cs | 22 - .../QualityProfileCustomFormatScoreMapping.cs | 14 - .../CustomFormat/Processors/GuideProcessor.cs | 81 ---- .../Processors/GuideSteps/ConfigStep.cs | 50 --- .../Processors/GuideSteps/CustomFormatStep.cs | 59 --- .../Processors/GuideSteps/IConfigStep.cs | 13 - .../GuideSteps/ICustomFormatStep.cs | 14 - .../GuideSteps/IQualityProfileStep.cs | 11 - .../GuideSteps/QualityProfileStep.cs | 65 --- .../Processors/IGuideProcessor.cs | 22 - .../Processors/IPersistenceProcessor.cs | 19 - .../Processors/PersistenceProcessor.cs | 64 --- .../CustomFormatApiPersistenceStep.cs | 32 -- .../ICustomFormatApiPersistenceStep.cs | 8 - .../PersistenceSteps/IJsonTransactionStep.cs | 15 - .../IQualityProfileApiPersistenceStep.cs | 14 - .../PersistenceSteps/JsonTransactionStep.cs | 206 --------- .../QualityProfileApiPersistenceStep.cs | 92 ---- .../Services/Processors/IServiceProcessor.cs | 6 - .../Services/Processors/RadarrProcessor.cs | 35 -- .../Processors/ServiceProcessorFactory.cs | 29 -- .../ServiceProcessorsAutofacModule.cs | 16 - .../Services/Processors/SonarrProcessor.cs | 64 --- .../QualitySize/IQualitySizeUpdater.cs | 8 - .../QualitySize/QualitySizeAutofacModule.cs | 16 - .../Services/QualitySize/QualitySizeData.cs | 8 - .../QualitySize/QualitySizeUpdater.cs | 141 ------ .../Services/Radarr/RadarrAutofacModule.cs | 11 - .../Guide/ReleaseProfileGuideService.cs | 31 -- .../Guide/ReleaseProfilePaths.cs | 7 - .../ReleaseProfile/IReleaseProfileUpdater.cs | 8 - .../ReleaseProfile/ReleaseProfileUpdater.cs | 275 ------------ .../Services/Sonarr/Api/Objects/SonarrTag.cs | 9 - .../Services/Sonarr/SonarrAutofacModule.cs | 16 - src/Recyclarr.sln.DotSettings | 1 + 319 files changed, 5077 insertions(+), 4218 deletions(-) delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/radarr___list_custom_formats.xml delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/radarr___list_qualities.xml delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/radarr___preview.xml delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/sonarr.xml delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/sonarr___list_custom_formats.xml delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/sonarr___list_qualities.xml delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/sonarr___list_release_profiles.xml delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/sonarr___preview.xml delete mode 100644 src/.idea/.idea.Recyclarr/.idea/runConfigurations/sonarr__custom_config_dir_.xml rename src/.idea/.idea.Recyclarr/.idea/runConfigurations/{radarr.xml => sync___preview.xml} (83%) delete mode 100644 src/Recyclarr.Common/Autofac/LifetimeScopedValue.cs create mode 100644 src/Recyclarr.Common/GenericEqualityComparer.cs delete mode 100644 src/Recyclarr.TrashLib.TestLibrary/CfTestUtils.cs create mode 100644 src/Recyclarr.TrashLib.TestLibrary/MockSyncSettings.cs create mode 100644 src/Recyclarr.TrashLib.TestLibrary/NewQp.cs delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/Data/ImportableCustomFormat1.json delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/Data/ImportableCustomFormat1_Processed.json delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/Data/ImportableCustomFormat2.json delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/Data/ImportableCustomFormat2_Processed.json delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/Data/NoScore.json delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/Data/WontBeInConfig.json delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/GuideProcessorTest.cs delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/GuideSteps/ConfigStepTest.cs delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/GuideSteps/CustomFormatStepTest.cs delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/GuideSteps/QualityProfileStepTest.cs delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceProcessorTest.cs delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStepTest.cs delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/JsonTransactionStepTest.cs delete mode 100644 src/Recyclarr.TrashLib.Tests/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStepTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/GlobalUsings.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/CustomFormat/Guide/CustomFormatParserTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/CustomFormat/Models/CustomFormatDataComparerTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/CustomFormat/PipelinePhases/CustomFormatConfigPhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/CustomFormat/PipelinePhases/CustomFormatTransactionPhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/QualityProfile/PipelinePhases/QualityProfileConfigPhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/QualitySize/Guide/Data/invalid_quality_size.json create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/QualitySize/Guide/Data/quality_size.json create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/QualitySize/Guide/QualityGuideServiceTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/QualitySize/Guide/QualitySizeGuideParserTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/QualitySize/PipelinePhases/QualitySizeGuidePhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/QualitySize/PipelinePhases/QualitySizeTransactionPhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/Tags/PipelinePhases/TagApiFetchPhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/Tags/PipelinePhases/TagApiPersistencePhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/Tags/PipelinePhases/TagConfigPhaseTest.cs create mode 100644 src/Recyclarr.TrashLib.Tests/Pipelines/Tags/PipelinePhases/TagTransactionPhaseTest.cs rename src/Recyclarr.TrashLib/{Services/System/SystemServiceAutofacModule.cs => ApiServices/ApiServicesAutofacModule.cs} (69%) rename src/Recyclarr.TrashLib/{Services => ApiServices}/System/Dto/SystemStatus.cs (57%) rename src/Recyclarr.TrashLib/{Services => ApiServices}/System/IServiceInformation.cs (75%) rename src/Recyclarr.TrashLib/{Services => ApiServices}/System/ISystemApiService.cs (60%) rename src/Recyclarr.TrashLib/{Services => ApiServices}/System/ServiceInformation.cs (94%) rename src/Recyclarr.TrashLib/{Services => ApiServices}/System/SystemApiService.cs (83%) create mode 100644 src/Recyclarr.TrashLib/Compatibility/CompatibilityAutofacModule.cs rename src/Recyclarr.TrashLib/{Services => Compatibility}/Radarr/RadarrCapabilities.cs (85%) rename src/Recyclarr.TrashLib/{Services => Compatibility}/Radarr/RadarrCapabilityChecker.cs (71%) rename src/Recyclarr.TrashLib/{Services/Common => Compatibility}/ServiceCapabilityChecker.cs (86%) rename src/Recyclarr.TrashLib/{Services/Sonarr/Capabilities => Compatibility/Sonarr}/ISonarrCapabilityChecker.cs (73%) rename src/Recyclarr.TrashLib/{Services/Sonarr/Capabilities => Compatibility/Sonarr}/SonarrCapabilities.cs (90%) rename src/Recyclarr.TrashLib/{Services/Sonarr/Capabilities => Compatibility/Sonarr}/SonarrCapabilityChecker.cs (82%) rename src/Recyclarr.TrashLib/{Services/Sonarr/Capabilities => Compatibility/Sonarr}/SonarrCapabilityEnforcer.cs (93%) rename src/Recyclarr.TrashLib/{Services/Radarr/Config => Config/Services/Radarr}/RadarrConfiguration.cs (52%) rename src/Recyclarr.TrashLib/{Services/Radarr/Config => Config/Services/Radarr}/RadarrConfigurationValidator.cs (75%) rename src/Recyclarr.TrashLib/{Services/Sonarr/Config => Config/Services/Sonarr}/SonarrConfiguration.cs (88%) rename src/Recyclarr.TrashLib/{Services/Sonarr/Config => Config/Services/Sonarr}/SonarrConfigurationValidator.cs (96%) create mode 100644 src/Recyclarr.TrashLib/Json/ServiceContractResolver.cs create mode 100644 src/Recyclarr.TrashLib/Json/ServiceJsonSerializerFactory.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Api/CustomFormatService.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Api/ICustomFormatService.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Cache/CacheException.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Cache/CachePersister.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Cache/ICachePersister.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Cache/ProcessedCustomFormatCache.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/CustomFormatAutofacModule.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/CustomFormatSyncPipeline.cs rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/CustomFormatCategoryItem.cs (61%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/CustomFormatCategoryParser.cs (97%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/CustomFormatDataLister.cs (95%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/CustomFormatGuideService.cs (80%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/CustomFormatLoader.cs (91%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Guide/CustomFormatParser.cs rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/CustomFormatPaths.cs (75%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/ICustomFormatCategoryParser.cs (74%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/ICustomFormatGuideService.cs (59%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Guide/ICustomFormatLoader.cs rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Guide/ICustomFormatParser.cs (51%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Models/ConflictingCustomFormat.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Models/CustomFormatCache.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Models/CustomFormatData.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Models/CustomFormatDataComparer.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/Models/FieldsArrayJsonConverter.cs rename src/Recyclarr.TrashLib/{Services => Pipelines}/CustomFormat/Models/ProcessedConfigData.cs (53%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/PipelinePhases/CustomFormatApiFetchPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/PipelinePhases/CustomFormatApiPersistencePhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/PipelinePhases/CustomFormatConfigPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/PipelinePhases/CustomFormatPreviewPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/CustomFormat/PipelinePhases/CustomFormatTransactionPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/IPipelineCache.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/ISyncPipeline.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/Api/IQualityProfileService.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/Api/QualityProfileDto.cs rename src/Recyclarr.TrashLib/{Services/CustomFormat => Pipelines/QualityProfile}/Api/QualityProfileService.cs (52%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/PipelinePhases/QualityProfileApiFetchPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/PipelinePhases/QualityProfileApiPersistencePhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/PipelinePhases/QualityProfileConfigPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/PipelinePhases/QualityProfilePreviewPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/PipelinePhases/QualityProfileTransactionPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/QualityProfileAutofacModule.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualityProfile/QualityProfileSyncPipeline.cs rename src/Recyclarr.TrashLib/{Services/CustomFormat/Models => Pipelines/QualityProfile}/UpdatedFormatScore.cs (64%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Api/IQualityDefinitionService.cs (62%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Api/QualityDefinitionService.cs (83%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Api/ServiceQualityDefinitionItem.cs (92%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Guide/IQualityGuideService.cs (73%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Guide/QualityGuideService.cs (77%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Guide/QualityItem.cs (95%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Guide/QualitySizeDataLister.cs (93%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Guide/QualitySizeGuideParser.cs (59%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/Guide/QualitySizePaths.cs (69%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualitySize/PipelinePhases/QualitySizeApiFetchPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualitySize/PipelinePhases/QualitySizeApiPersistencePhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualitySize/PipelinePhases/QualitySizeGuidePhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualitySize/PipelinePhases/QualitySizePreviewPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualitySize/PipelinePhases/QualitySizeTransactionPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualitySize/QualitySizeAutofacModule.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualitySize/QualitySizeData.cs rename src/Recyclarr.TrashLib/{Services => Pipelines}/QualitySize/QualitySizeItem.cs (90%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/QualitySize/QualitySizeSyncPipeline.cs rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Api/IReleaseProfileApiService.cs (79%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Api/ISonarrReleaseProfileCompatibilityHandler.cs (54%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Api/Mappings/SonarrApiObjectMappingProfile.cs (86%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Api/Objects/SonarrReleaseProfile.cs (96%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Api/ReleaseProfileApiService.cs (89%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Api/Schemas/SonarrReleaseProfileSchema.cs (97%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Api/SonarrReleaseProfileCompatibilityHandler.cs (90%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Filters/IReleaseProfileFilter.cs (54%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Filters/IReleaseProfileFilterPipeline.cs (55%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Filters/IncludeExcludeFilter.cs (85%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Filters/ReleaseProfileDataFilterer.cs (80%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Filters/ReleaseProfileDataValidationFilterer.cs (96%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Filters/ReleaseProfileFilterPipeline.cs (81%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Filters/StrictNegativeScoresFilter.cs (86%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Guide/IReleaseProfileGuideService.cs (64%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Guide/ReleaseProfileDataLister.cs (98%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/Guide/ReleaseProfileGuideParser.cs (92%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/ReleaseProfile/Guide/ReleaseProfileGuideService.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/ReleaseProfile/Models/ReleaseProfileTransactionData.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileApiFetchPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileApiPersistencePhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileConfigPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfilePreviewPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/ReleaseProfile/PipelinePhases/ReleaseProfileTransactionPhase.cs rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/ReleaseProfileAutofacModule.cs (52%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/ReleaseProfileData.cs (95%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/ReleaseProfileDataValidator.cs (93%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/ReleaseProfile/ReleaseProfileSyncPipeline.cs rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/ScopedState.cs (96%) rename src/Recyclarr.TrashLib/{Services => Pipelines}/ReleaseProfile/TermDataConverter.cs (79%) rename src/Recyclarr.TrashLib/{Services/Sonarr => Pipelines/Tags}/Api/ISonarrTagApiService.cs (68%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/Api/SonarrTag.cs rename src/Recyclarr.TrashLib/{Services/Sonarr => Pipelines/Tags}/Api/SonarrTagApiService.cs (87%) create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/PipelinePhases/TagApiFetchPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/PipelinePhases/TagApiPersistencePhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/PipelinePhases/TagConfigPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/PipelinePhases/TagPreviewPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/PipelinePhases/TagTransactionPhase.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/ServiceTagCache.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/TagSyncPipeline.cs create mode 100644 src/Recyclarr.TrashLib/Pipelines/Tags/TagsAutofacModule.cs rename src/Recyclarr.TrashLib/{Services => }/Processors/ConfigCreationProcessor.cs (95%) rename src/Recyclarr.TrashLib/{Services => }/Processors/ExitStatus.cs (53%) rename src/Recyclarr.TrashLib/{Services => }/Processors/IConfigCreationProcessor.cs (64%) create mode 100644 src/Recyclarr.TrashLib/Processors/IServiceProcessor.cs rename src/Recyclarr.TrashLib/{Services => }/Processors/ISyncProcessor.cs (66%) rename src/Recyclarr.TrashLib/{Services => }/Processors/ISyncSettings.cs (68%) create mode 100644 src/Recyclarr.TrashLib/Processors/ServiceProcessorsAutofacModule.cs create mode 100644 src/Recyclarr.TrashLib/Processors/SyncPipelineExecutor.cs rename src/Recyclarr.TrashLib/{Services => }/Processors/SyncProcessor.cs (67%) delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Api/CustomFormatService.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Api/ICustomFormatService.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Api/IQualityProfileService.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/CachePersister.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatAutofacModule.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/CustomFormatUpdater.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Guide/CustomFormatParser.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Guide/ICustomFormatLoader.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/ICachePersister.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/ICustomFormatUpdater.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Models/Cache/CustomFormatCache.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Models/ConflictingCustomFormat.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Models/CustomFormatData.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Models/ProcessedCustomFormatData.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Models/QualityProfileCustomFormatScoreMapping.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideProcessor.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideSteps/ConfigStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideSteps/CustomFormatStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideSteps/IConfigStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideSteps/ICustomFormatStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideSteps/IQualityProfileStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/GuideSteps/QualityProfileStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IGuideProcessor.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/IPersistenceProcessor.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceProcessor.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/CustomFormatApiPersistenceStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/ICustomFormatApiPersistenceStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/IJsonTransactionStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/IQualityProfileApiPersistenceStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/JsonTransactionStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/CustomFormat/Processors/PersistenceSteps/QualityProfileApiPersistenceStep.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Processors/IServiceProcessor.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Processors/RadarrProcessor.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorFactory.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Processors/ServiceProcessorsAutofacModule.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Processors/SonarrProcessor.cs delete mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/IQualitySizeUpdater.cs delete mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeAutofacModule.cs delete mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeData.cs delete mode 100644 src/Recyclarr.TrashLib/Services/QualitySize/QualitySizeUpdater.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Radarr/RadarrAutofacModule.cs delete mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfileGuideService.cs delete mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/Guide/ReleaseProfilePaths.cs delete mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/IReleaseProfileUpdater.cs delete mode 100644 src/Recyclarr.TrashLib/Services/ReleaseProfile/ReleaseProfileUpdater.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/Api/Objects/SonarrTag.cs delete mode 100644 src/Recyclarr.TrashLib/Services/Sonarr/SonarrAutofacModule.cs diff --git a/.editorconfig b/.editorconfig index 32463602..086e1e78 100644 --- a/.editorconfig +++ b/.editorconfig @@ -340,6 +340,7 @@ resharper_csharp_space_after_unary_operator = false resharper_csharp_stick_comment = false resharper_csharp_wrap_after_declaration_lpar = true resharper_csharp_wrap_lines = true +resharper_csharp_wrap_parameters_style = chop_if_long resharper_cxxcli_property_declaration_braces = next_line resharper_default_exception_variable_name = e resharper_default_value_when_type_evident = default_literal @@ -442,8 +443,8 @@ resharper_line_break_before_requires_clause = do_not_change resharper_linkage_specification_braces = end_of_line resharper_linkage_specification_indentation = none resharper_local_function_body = block_body -resharper_macro_block_begin = -resharper_macro_block_end = +resharper_macro_block_begin = +resharper_macro_block_end = resharper_max_array_initializer_elements_on_line = 10000 resharper_max_attribute_length_for_same_line = 60 resharper_max_enum_members_on_line = 3 @@ -510,7 +511,7 @@ resharper_remove_blank_lines_near_braces_in_declarations = true resharper_remove_this_qualifier = true resharper_requires_expression_braces = next_line resharper_resx_attribute_indent = single_indent -resharper_resx_linebreak_before_elements = +resharper_resx_linebreak_before_elements = resharper_resx_max_blank_lines_between_tags = 0 resharper_resx_max_line_length = 2147483647 resharper_resx_pi_attribute_style = do_not_touch @@ -741,7 +742,7 @@ resharper_xmldoc_wrap_lines = true resharper_xmldoc_wrap_tags_and_pi = true resharper_xmldoc_wrap_text = true resharper_xml_attribute_indent = align_by_first_attribute -resharper_xml_linebreak_before_elements = +resharper_xml_linebreak_before_elements = resharper_xml_max_blank_lines_between_tags = 2 resharper_xml_max_line_length = 120 resharper_xml_pi_attribute_style = do_not_touch diff --git a/schemas/config-schema.json b/schemas/config-schema.json index b8d889ce..7c8185e0 100644 --- a/schemas/config-schema.json +++ b/schemas/config-schema.json @@ -67,6 +67,11 @@ "description": "If enabled, custom formats that you remove from your YAML configuration OR that are removed from the guide will be deleted from your Radarr instance.", "default": false }, + "replace_existing_custom_formats": { + "type": "boolean", + "description": "If disabled, custom formats that Recyclarr didn't explicitly create or know about will not be replaced.", + "default": true + }, "custom_formats": { "type": "array", "minItems": 1, @@ -136,7 +141,7 @@ "api_key": { "type": "string", "minLength": 1, - "description": "The API key from Radarr." + "description": "The API key from Radarr" }, "quality_definition": { "$ref": "#/$defs/quality_definition" @@ -144,6 +149,9 @@ "delete_old_custom_formats": { "$ref": "#/$defs/delete_old_custom_formats" }, + "replace_existing_custom_formats": { + "$ref": "#/$defs/replace_existing_custom_formats" + }, "custom_formats": { "$ref": "#/$defs/custom_formats" } @@ -173,6 +181,9 @@ "delete_old_custom_formats": { "$ref": "#/$defs/delete_old_custom_formats" }, + "replace_existing_custom_formats": { + "$ref": "#/$defs/replace_existing_custom_formats" + }, "custom_formats": { "$ref": "#/$defs/custom_formats" }, diff --git a/src/.idea/.idea.Recyclarr/.idea/indexLayout.xml b/src/.idea/.idea.Recyclarr/.idea/indexLayout.xml index 7b08163c..063a7aec 100644 --- a/src/.idea/.idea.Recyclarr/.idea/indexLayout.xml +++ b/src/.idea/.idea.Recyclarr/.idea/indexLayout.xml @@ -1,7 +1,9 @@ - + + ../../../code + diff --git a/src/.idea/.idea.Recyclarr/.idea/runConfigurations/list release-profiles --terms.run.xml b/src/.idea/.idea.Recyclarr/.idea/runConfigurations/list release-profiles --terms.run.xml index 1b44937d..00785080 100644 --- a/src/.idea/.idea.Recyclarr/.idea/runConfigurations/list release-profiles --terms.run.xml +++ b/src/.idea/.idea.Recyclarr/.idea/runConfigurations/list release-profiles --terms.run.xml @@ -1,7 +1,7 @@