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.
pull/201/head
Robert Dailey 1 year ago
parent 7dec45a07a
commit bcc65857df

@ -14,10 +14,11 @@ using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Http; using Recyclarr.TrashLib.Http;
using Recyclarr.TrashLib.Repo; using Recyclarr.TrashLib.Repo;
using Recyclarr.TrashLib.Repo.VersionControl; using Recyclarr.TrashLib.Repo.VersionControl;
using Recyclarr.TrashLib.Services.Common;
using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.CustomFormat;
using Recyclarr.TrashLib.Services.Processors; using Recyclarr.TrashLib.Services.Processors;
using Recyclarr.TrashLib.Services.QualitySize;
using Recyclarr.TrashLib.Services.Radarr; using Recyclarr.TrashLib.Services.Radarr;
using Recyclarr.TrashLib.Services.ReleaseProfile;
using Recyclarr.TrashLib.Services.Sonarr; using Recyclarr.TrashLib.Services.Sonarr;
using Recyclarr.TrashLib.Services.System; using Recyclarr.TrashLib.Services.System;
using Recyclarr.TrashLib.Startup; using Recyclarr.TrashLib.Startup;
@ -38,11 +39,12 @@ public static class CompositionRoot
builder.RegisterModule<SonarrAutofacModule>(); builder.RegisterModule<SonarrAutofacModule>();
builder.RegisterModule<RadarrAutofacModule>(); builder.RegisterModule<RadarrAutofacModule>();
builder.RegisterModule<QualitySizeAutofacModule>();
builder.RegisterModule<CustomFormatAutofacModule>();
builder.RegisterModule<ReleaseProfileAutofacModule>();
builder.RegisterModule<VersionControlAutofacModule>(); builder.RegisterModule<VersionControlAutofacModule>();
builder.RegisterModule<MigrationAutofacModule>(); builder.RegisterModule<MigrationAutofacModule>();
builder.RegisterModule<RepoAutofacModule>(); builder.RegisterModule<RepoAutofacModule>();
builder.RegisterModule<CustomFormatAutofacModule>();
builder.RegisterModule<GuideServicesAutofacModule>();
builder.RegisterModule<SystemServiceAutofacModule>(); builder.RegisterModule<SystemServiceAutofacModule>();
builder.RegisterModule(new ConfigAutofacModule(assemblies)); builder.RegisterModule(new ConfigAutofacModule(assemblies));
builder.RegisterModule<ServiceProcessorsAutofacModule>(); builder.RegisterModule<ServiceProcessorsAutofacModule>();

@ -1,11 +1,10 @@
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Autofac.Features.Indexed;
using JetBrains.Annotations; using JetBrains.Annotations;
using Recyclarr.Cli.Console.Helpers; using Recyclarr.Cli.Console.Helpers;
using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Repo; using Recyclarr.TrashLib.Repo;
using Recyclarr.TrashLib.Services.Common; using Recyclarr.TrashLib.Services.CustomFormat.Guide;
using Spectre.Console.Cli; using Spectre.Console.Cli;
#pragma warning disable CS8765 #pragma warning disable CS8765
@ -16,8 +15,7 @@ namespace Recyclarr.Cli.Console.Commands;
[Description("List custom formats in the guide for a particular service.")] [Description("List custom formats in the guide for a particular service.")]
internal class ListCustomFormatsCommand : AsyncCommand<ListCustomFormatsCommand.CliSettings> internal class ListCustomFormatsCommand : AsyncCommand<ListCustomFormatsCommand.CliSettings>
{ {
private readonly IGuideDataLister _lister; private readonly CustomFormatDataLister _lister;
private readonly IIndex<SupportedServices, IGuideService> _guideService;
private readonly IRepoUpdater _repoUpdater; private readonly IRepoUpdater _repoUpdater;
[UsedImplicitly] [UsedImplicitly]
@ -31,20 +29,17 @@ internal class ListCustomFormatsCommand : AsyncCommand<ListCustomFormatsCommand.
} }
public ListCustomFormatsCommand( public ListCustomFormatsCommand(
IGuideDataLister lister, CustomFormatDataLister lister,
IIndex<SupportedServices, IGuideService> guideService,
IRepoUpdater repoUpdater) IRepoUpdater repoUpdater)
{ {
_lister = lister; _lister = lister;
_guideService = guideService;
_repoUpdater = repoUpdater; _repoUpdater = repoUpdater;
} }
public override async Task<int> ExecuteAsync(CommandContext context, CliSettings settings) public override async Task<int> ExecuteAsync(CommandContext context, CliSettings settings)
{ {
await _repoUpdater.UpdateRepo(); await _repoUpdater.UpdateRepo();
var guideService = _guideService[settings.Service]; _lister.ListCustomFormats(settings.Service);
_lister.ListCustomFormats(guideService.GetCustomFormatData());
return 0; return 0;
} }
} }

@ -1,11 +1,10 @@
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Autofac.Features.Indexed;
using JetBrains.Annotations; using JetBrains.Annotations;
using Recyclarr.Cli.Console.Helpers; using Recyclarr.Cli.Console.Helpers;
using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Repo; using Recyclarr.TrashLib.Repo;
using Recyclarr.TrashLib.Services.Common; using Recyclarr.TrashLib.Services.QualitySize.Guide;
using Spectre.Console.Cli; using Spectre.Console.Cli;
namespace Recyclarr.Cli.Console.Commands; 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.")] [Description("List quality definitions in the guide for a particular service.")]
internal class ListQualitiesCommand : AsyncCommand<ListQualitiesCommand.CliSettings> internal class ListQualitiesCommand : AsyncCommand<ListQualitiesCommand.CliSettings>
{ {
private readonly IGuideDataLister _lister; private readonly QualitySizeDataLister _lister;
private readonly IIndex<SupportedServices, IGuideService> _guideService;
private readonly IRepoUpdater _repoUpdater; private readonly IRepoUpdater _repoUpdater;
[UsedImplicitly] [UsedImplicitly]
@ -29,21 +27,16 @@ internal class ListQualitiesCommand : AsyncCommand<ListQualitiesCommand.CliSetti
public required SupportedServices Service { get; init; } public required SupportedServices Service { get; init; }
} }
public ListQualitiesCommand( public ListQualitiesCommand(QualitySizeDataLister lister, IRepoUpdater repoUpdater)
IGuideDataLister lister,
IIndex<SupportedServices, IGuideService> guideService,
IRepoUpdater repoUpdater)
{ {
_lister = lister; _lister = lister;
_guideService = guideService;
_repoUpdater = repoUpdater; _repoUpdater = repoUpdater;
} }
public override async Task<int> ExecuteAsync(CommandContext context, CliSettings settings) public override async Task<int> ExecuteAsync(CommandContext context, CliSettings settings)
{ {
await _repoUpdater.UpdateRepo(); await _repoUpdater.UpdateRepo();
var guideService = _guideService[settings.Service]; _lister.ListQualities(settings.Service);
_lister.ListQualities(guideService.GetQualities());
return 0; return 0;
} }
} }

@ -2,7 +2,7 @@ using System.ComponentModel;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations; using JetBrains.Annotations;
using Recyclarr.TrashLib.Repo; using Recyclarr.TrashLib.Repo;
using Recyclarr.TrashLib.Services.Sonarr; using Recyclarr.TrashLib.Services.ReleaseProfile.Guide;
using Spectre.Console.Cli; using Spectre.Console.Cli;
#pragma warning disable CS8765 #pragma warning disable CS8765
@ -14,7 +14,7 @@ namespace Recyclarr.Cli.Console.Commands;
internal class ListReleaseProfilesCommand : AsyncCommand<ListReleaseProfilesCommand.CliSettings> internal class ListReleaseProfilesCommand : AsyncCommand<ListReleaseProfilesCommand.CliSettings>
{ {
private readonly ILogger _log; private readonly ILogger _log;
private readonly ISonarrGuideDataLister _lister; private readonly ReleaseProfileDataLister _lister;
private readonly IRepoUpdater _repoUpdater; private readonly IRepoUpdater _repoUpdater;
[UsedImplicitly] [UsedImplicitly]
@ -31,7 +31,7 @@ internal class ListReleaseProfilesCommand : AsyncCommand<ListReleaseProfilesComm
public ListReleaseProfilesCommand( public ListReleaseProfilesCommand(
ILogger log, ILogger log,
ISonarrGuideDataLister lister, ReleaseProfileDataLister lister,
IRepoUpdater repoUpdater) IRepoUpdater repoUpdater)
{ {
_log = log; _log = log;

@ -6,8 +6,9 @@ using Recyclarr.Cli.Console.Helpers;
using Recyclarr.Cli.Migration; using Recyclarr.Cli.Migration;
using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Repo; using Recyclarr.TrashLib.Repo;
using Recyclarr.TrashLib.Services.CustomFormat.Guide;
using Recyclarr.TrashLib.Services.Processors; using Recyclarr.TrashLib.Services.Processors;
using Recyclarr.TrashLib.Services.Radarr; using Recyclarr.TrashLib.Services.QualitySize.Guide;
using Spectre.Console.Cli; using Spectre.Console.Cli;
namespace Recyclarr.Cli.Console.Commands; namespace Recyclarr.Cli.Console.Commands;
@ -17,7 +18,8 @@ namespace Recyclarr.Cli.Console.Commands;
internal class RadarrCommand : AsyncCommand<RadarrCommand.CliSettings> internal class RadarrCommand : AsyncCommand<RadarrCommand.CliSettings>
{ {
private readonly ILogger _log; private readonly ILogger _log;
private readonly IRadarrGuideDataLister _lister; private readonly CustomFormatDataLister _cfLister;
private readonly QualitySizeDataLister _qualityLister;
private readonly IMigrationExecutor _migration; private readonly IMigrationExecutor _migration;
private readonly IRepoUpdater _repoUpdater; private readonly IRepoUpdater _repoUpdater;
private readonly ISyncProcessor _syncProcessor; private readonly ISyncProcessor _syncProcessor;
@ -56,13 +58,15 @@ internal class RadarrCommand : AsyncCommand<RadarrCommand.CliSettings>
public RadarrCommand( public RadarrCommand(
ILogger log, ILogger log,
IRadarrGuideDataLister lister, CustomFormatDataLister cfLister,
QualitySizeDataLister qualityLister,
IMigrationExecutor migration, IMigrationExecutor migration,
IRepoUpdater repoUpdater, IRepoUpdater repoUpdater,
ISyncProcessor syncProcessor) ISyncProcessor syncProcessor)
{ {
_log = log; _log = log;
_lister = lister; _cfLister = cfLister;
_qualityLister = qualityLister;
_migration = migration; _migration = migration;
_repoUpdater = repoUpdater; _repoUpdater = repoUpdater;
_syncProcessor = syncProcessor; _syncProcessor = syncProcessor;
@ -77,14 +81,14 @@ internal class RadarrCommand : AsyncCommand<RadarrCommand.CliSettings>
if (settings.ListCustomFormats) if (settings.ListCustomFormats)
{ {
_log.Warning("The `radarr` subcommand is DEPRECATED -- Use `list custom-formats radarr` instead!"); _log.Warning("The `radarr` subcommand is DEPRECATED -- Use `list custom-formats radarr` instead!");
_lister.ListCustomFormats(); _cfLister.ListCustomFormats(SupportedServices.Radarr);
return 0; return 0;
} }
if (settings.ListQualities) if (settings.ListQualities)
{ {
_log.Warning("The `radarr` subcommand is DEPRECATED -- Use `list qualities radarr` instead!"); _log.Warning("The `radarr` subcommand is DEPRECATED -- Use `list qualities radarr` instead!");
_lister.ListQualities(); _qualityLister.ListQualities(SupportedServices.Radarr);
return 0; return 0;
} }

@ -6,8 +6,10 @@ using Recyclarr.Cli.Console.Helpers;
using Recyclarr.Cli.Migration; using Recyclarr.Cli.Migration;
using Recyclarr.TrashLib.Config; using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Repo; using Recyclarr.TrashLib.Repo;
using Recyclarr.TrashLib.Services.CustomFormat.Guide;
using Recyclarr.TrashLib.Services.Processors; 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; using Spectre.Console.Cli;
namespace Recyclarr.Cli.Console.Commands; namespace Recyclarr.Cli.Console.Commands;
@ -17,7 +19,9 @@ namespace Recyclarr.Cli.Console.Commands;
internal class SonarrCommand : AsyncCommand<SonarrCommand.CliSettings> internal class SonarrCommand : AsyncCommand<SonarrCommand.CliSettings>
{ {
private readonly ILogger _log; 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 IMigrationExecutor _migration;
private readonly IRepoUpdater _repoUpdater; private readonly IRepoUpdater _repoUpdater;
private readonly ISyncProcessor _syncProcessor; private readonly ISyncProcessor _syncProcessor;
@ -70,13 +74,17 @@ internal class SonarrCommand : AsyncCommand<SonarrCommand.CliSettings>
public SonarrCommand( public SonarrCommand(
ILogger log, ILogger log,
ISonarrGuideDataLister lister, CustomFormatDataLister cfLister,
QualitySizeDataLister qualityLister,
ReleaseProfileDataLister rpLister,
IMigrationExecutor migration, IMigrationExecutor migration,
IRepoUpdater repoUpdater, IRepoUpdater repoUpdater,
ISyncProcessor syncProcessor) ISyncProcessor syncProcessor)
{ {
_log = log; _log = log;
_lister = lister; _cfLister = cfLister;
_qualityLister = qualityLister;
_rpLister = rpLister;
_migration = migration; _migration = migration;
_repoUpdater = repoUpdater; _repoUpdater = repoUpdater;
_syncProcessor = syncProcessor; _syncProcessor = syncProcessor;
@ -91,27 +99,27 @@ internal class SonarrCommand : AsyncCommand<SonarrCommand.CliSettings>
if (settings.ListCustomFormats) if (settings.ListCustomFormats)
{ {
_log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list custom-formats sonarr` instead!"); _log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list custom-formats sonarr` instead!");
_lister.ListCustomFormats(); _cfLister.ListCustomFormats(SupportedServices.Sonarr);
return 0; return 0;
} }
if (settings.ListQualities) if (settings.ListQualities)
{ {
_log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list qualities sonarr` instead!"); _log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list qualities sonarr` instead!");
_lister.ListQualities(); _qualityLister.ListQualities(SupportedServices.Sonarr);
return 0; return 0;
} }
if (settings.ListReleaseProfiles) if (settings.ListReleaseProfiles)
{ {
_log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list release-profiles` instead!"); _log.Warning("The `sonarr` subcommand is DEPRECATED -- Use `list release-profiles` instead!");
_lister.ListReleaseProfiles(); _rpLister.ListReleaseProfiles();
return 0; return 0;
} }
if (settings.ListTerms is not null) if (settings.ListTerms is not null)
{ {
_lister.ListTerms(settings.ListTerms); _rpLister.ListTerms(settings.ListTerms);
return 0; return 0;
} }

@ -1,8 +1,9 @@
using AutoFixture.NUnit3; using AutoFixture.NUnit3;
using FluentAssertions; using FluentAssertions;
using NSubstitute;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TestLibrary.AutoFixture;
using Recyclarr.TrashLib.Services.Common; using Recyclarr.TrashLib.Services.CustomFormat.Guide;
using Recyclarr.TrashLib.TestLibrary; using Recyclarr.TrashLib.TestLibrary;
using Spectre.Console.Testing; using Spectre.Console.Testing;
@ -15,7 +16,8 @@ public class GuideDataListerTest
[Test, AutoMockData] [Test, AutoMockData]
public void Custom_formats_appear_in_console_output( public void Custom_formats_appear_in_console_output(
[Frozen(Matching.ImplementedInterfaces)] TestConsole console, [Frozen(Matching.ImplementedInterfaces)] TestConsole console,
GuideDataLister sut) [Frozen] ICustomFormatGuideService guide,
CustomFormatDataLister sut)
{ {
var testData = new[] var testData = new[]
{ {
@ -23,7 +25,9 @@ public class GuideDataListerTest
NewCf.Data("Second", "456") NewCf.Data("Second", "456")
}; };
sut.ListCustomFormats(testData); guide.GetCustomFormatData(default!).ReturnsForAnyArgs(testData);
sut.ListCustomFormats(default!);
console.Output.Should().ContainAll( console.Output.Should().ContainAll(
testData.SelectMany(x => new[] {x.Name, x.TrashId})); testData.SelectMany(x => new[] {x.Name, x.TrashId}));

@ -10,7 +10,6 @@ using Recyclarr.TrashLib.Services.CustomFormat.Guide;
using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models;
using Recyclarr.TrashLib.Services.CustomFormat.Processors; using Recyclarr.TrashLib.Services.CustomFormat.Processors;
using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps; using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps;
using Recyclarr.TrashLib.Services.Radarr;
using Recyclarr.TrashLib.TestLibrary; using Recyclarr.TrashLib.TestLibrary;
namespace Recyclarr.TrashLib.Tests.CustomFormat.Processors; 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() public async Task Guide_processor_behaves_as_expected_with_normal_guide_data()
{ {
var ctx = new Context(); var ctx = new Context();
var guideService = Substitute.For<RadarrGuideService>(); var guideService = Substitute.For<ICustomFormatGuideService>();
var guideProcessor = new GuideProcessor(new TestGuideProcessorSteps()); var guideProcessor = new GuideProcessor(new TestGuideProcessorSteps(), guideService);
// simulate guide data // simulate guide data
guideService.GetCustomFormatData().Returns(new[] guideService.GetCustomFormatData(default!).ReturnsForAnyArgs(new[]
{ {
ctx.ReadCustomFormat("ImportableCustomFormat1.json"), ctx.ReadCustomFormat("ImportableCustomFormat1.json"),
ctx.ReadCustomFormat("ImportableCustomFormat2.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<ProcessedCustomFormatData> var expectedProcessedCustomFormatData = new List<ProcessedCustomFormatData>
{ {

@ -9,20 +9,20 @@ using NUnit.Framework;
using Recyclarr.TestLibrary; using Recyclarr.TestLibrary;
using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TestLibrary.AutoFixture;
using Recyclarr.TrashLib.Repo; using Recyclarr.TrashLib.Repo;
using Recyclarr.TrashLib.Services.Sonarr; using Recyclarr.TrashLib.Services.ReleaseProfile;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; using Recyclarr.TrashLib.Services.ReleaseProfile.Guide;
namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Guide; namespace Recyclarr.TrashLib.Tests.Services;
[TestFixture] [TestFixture]
[Parallelizable(ParallelScope.All)] [Parallelizable(ParallelScope.All)]
public class LocalRepoSonarrGuideServiceTest public class GuideServiceTest
{ {
[Test, AutoMockData] [Test, AutoMockData]
public void Get_custom_format_json_works( public void Get_release_profile_json_works(
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
[Frozen] IRepoPaths repoPaths, [Frozen] IRepoMetadataBuilder metadataBuilder,
LocalRepoSonarrGuideService sut) ReleaseProfileGuideService sut)
{ {
static ReleaseProfileData MakeMockObject(string term) 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("first.json").FullName, MockFileData(mockData1));
fs.AddFile(baseDir.File("second.json").FullName, MockFileData(mockData2)); fs.AddFile(baseDir.File("second.json").FullName, MockFileData(mockData2));
repoPaths.SonarrReleaseProfilePaths.Returns(new[] {baseDir}); metadataBuilder.ToDirectoryInfoList(default!).ReturnsForAnyArgs(new[] {baseDir});
var results = sut.GetReleaseProfileData(); var results = sut.GetReleaseProfileData();
@ -64,8 +64,8 @@ public class LocalRepoSonarrGuideServiceTest
[Test, AutoMockData] [Test, AutoMockData]
public void Json_exceptions_do_not_interrupt_parsing_other_files( public void Json_exceptions_do_not_interrupt_parsing_other_files(
[Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs, [Frozen(Matching.ImplementedInterfaces)] MockFileSystem fs,
[Frozen] IRepoPaths repoPaths, [Frozen] IRepoMetadataBuilder metadataBuilder,
LocalRepoSonarrGuideService sut) ReleaseProfileGuideService sut)
{ {
var rootPath = fs.CurrentDirectory().SubDirectory("files"); var rootPath = fs.CurrentDirectory().SubDirectory("files");
rootPath.Create(); 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("0_bad_data.json").FullName, MockData.FromString(badData));
fs.AddFile(rootPath.File("1_good_data.json").FullName, MockData.FromJson(goodData)); 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(); var results = sut.GetReleaseProfileData();

@ -3,21 +3,21 @@ using FluentAssertions;
using NSubstitute; using NSubstitute;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TestLibrary.AutoFixture;
using Recyclarr.TrashLib.Services.Sonarr; using Recyclarr.TrashLib.Services.ReleaseProfile;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; using Recyclarr.TrashLib.Services.ReleaseProfile.Guide;
using Spectre.Console.Testing; using Spectre.Console.Testing;
namespace Recyclarr.TrashLib.Tests.Sonarr; namespace Recyclarr.TrashLib.Tests.Services;
[TestFixture] [TestFixture]
[Parallelizable(ParallelScope.All)] [Parallelizable(ParallelScope.All)]
public class SonarrGuideDataListerTest public class ReleaseProfileDataListerTest
{ {
[Test, AutoMockData] [Test, AutoMockData]
public void Release_profiles_appear_in_console_output( public void Release_profiles_appear_in_console_output(
[Frozen] SonarrGuideService guide,
[Frozen(Matching.ImplementedInterfaces)] TestConsole console, [Frozen(Matching.ImplementedInterfaces)] TestConsole console,
SonarrGuideDataLister sut) [Frozen] IReleaseProfileGuideService guide,
ReleaseProfileDataLister sut)
{ {
var testData = new[] var testData = new[]
{ {
@ -35,9 +35,9 @@ public class SonarrGuideDataListerTest
[Test, AutoMockData] [Test, AutoMockData]
public void Terms_appear_in_console_output( public void Terms_appear_in_console_output(
[Frozen] SonarrGuideService guide, [Frozen] IReleaseProfileGuideService guide,
[Frozen(Matching.ImplementedInterfaces)] TestConsole console, [Frozen(Matching.ImplementedInterfaces)] TestConsole console,
SonarrGuideDataLister sut) ReleaseProfileDataLister sut)
{ {
var requiredData = new[] var requiredData = new[]
{ {
@ -57,15 +57,18 @@ public class SonarrGuideDataListerTest
new TermData {Name = "Sixth", TrashId = "666", Term = "term6"} new TermData {Name = "Sixth", TrashId = "666", Term = "term6"}
}; };
guide.GetUnfilteredProfileById(default!).ReturnsForAnyArgs(new ReleaseProfileData guide.GetReleaseProfileData().Returns(new[]
{ {
Name = "Release Profile", new ReleaseProfileData
TrashId = "098",
Required = requiredData,
Ignored = ignoredData,
Preferred = new PreferredTermData[]
{ {
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}) preferredData.SelectMany(x => new[] {x.Name, x.TrashId})
}; };
guide.Received().GetUnfilteredProfileById("098");
console.Output.Should().ContainAll(expectedOutput.SelectMany(x => x)); console.Output.Should().ContainAll(expectedOutput.SelectMany(x => x));
} }
} }

@ -1,9 +1,9 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.TestLibrary.AutoFixture; 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.Config;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters;
namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters;

@ -1,8 +1,8 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.TestLibrary.AutoFixture; using Recyclarr.TestLibrary.AutoFixture;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; using Recyclarr.TrashLib.Services.ReleaseProfile;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; using Recyclarr.TrashLib.Services.ReleaseProfile.Filters;
namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters;

@ -1,9 +1,9 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.TestLibrary.AutoFixture; 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.Config;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters;
namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile.Filters;

@ -1,7 +1,7 @@
using FluentAssertions; using FluentAssertions;
using FluentValidation.TestHelper; using FluentValidation.TestHelper;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; using Recyclarr.TrashLib.Services.ReleaseProfile;
namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile;

@ -1,6 +1,6 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; using Recyclarr.TrashLib.Services.ReleaseProfile;
namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Tests.Sonarr.ReleaseProfile;

@ -7,8 +7,8 @@ using NSubstitute;
using NUnit.Framework; using NUnit.Framework;
using Recyclarr.Cli.TestLibrary; using Recyclarr.Cli.TestLibrary;
using Recyclarr.TestLibrary; using Recyclarr.TestLibrary;
using Recyclarr.TrashLib.Services.Sonarr.Api; using Recyclarr.TrashLib.Services.ReleaseProfile.Api;
using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects;
using Recyclarr.TrashLib.Services.Sonarr.Capabilities; using Recyclarr.TrashLib.Services.Sonarr.Capabilities;
namespace Recyclarr.TrashLib.Tests.Sonarr; namespace Recyclarr.TrashLib.Tests.Sonarr;

@ -0,0 +1,10 @@
using System.IO.Abstractions;
namespace Recyclarr.TrashLib.Repo;
public interface IRepoMetadataBuilder
{
RepoMetadata GetMetadata();
IReadOnlyList<IDirectoryInfo> ToDirectoryInfoList(IEnumerable<string> listOfDirectories);
IDirectoryInfo DocsDirectory { get; }
}

@ -1,14 +0,0 @@
using System.IO.Abstractions;
namespace Recyclarr.TrashLib.Repo;
public interface IRepoPaths
{
IReadOnlyCollection<IDirectoryInfo> RadarrCustomFormatPaths { get; }
IReadOnlyCollection<IDirectoryInfo> SonarrReleaseProfilePaths { get; }
IReadOnlyCollection<IDirectoryInfo> SonarrQualityPaths { get; }
IReadOnlyCollection<IDirectoryInfo> RadarrQualityPaths { get; }
IReadOnlyCollection<IDirectoryInfo> SonarrCustomFormatPaths { get; }
IFileInfo RadarrCollectionOfCustomFormats { get; }
IFileInfo SonarrCollectionOfCustomFormats { get; }
}

@ -1,7 +0,0 @@
namespace Recyclarr.TrashLib.Repo;
public interface IRepoPathsFactory
{
IRepoPaths Create();
RepoMetadata Metadata { get; }
}

@ -9,6 +9,6 @@ public class RepoAutofacModule : Module
base.Load(builder); base.Load(builder);
builder.RegisterType<RepoUpdater>().As<IRepoUpdater>(); builder.RegisterType<RepoUpdater>().As<IRepoUpdater>();
builder.RegisterType<RepoMetadataParser>().As<IRepoMetadataParser>(); builder.RegisterType<RepoMetadataParser>().As<IRepoMetadataParser>();
builder.RegisterType<RepoPathsFactory>().As<IRepoPathsFactory>().InstancePerLifetimeScope(); builder.RegisterType<RepoMetadataBuilder>().As<IRepoMetadataBuilder>().InstancePerLifetimeScope();
} }
} }

@ -1,21 +1,25 @@
namespace Recyclarr.TrashLib.Repo; namespace Recyclarr.TrashLib.Repo;
public record RadarrMetadata( public record RadarrMetadata
IReadOnlyCollection<string> CustomFormats, {
IReadOnlyCollection<string> Qualities public IReadOnlyCollection<string> CustomFormats { get; init; } = Array.Empty<string>();
); public IReadOnlyCollection<string> Qualities { get; init; } = Array.Empty<string>();
}
public record SonarrMetadata( public record SonarrMetadata
IReadOnlyCollection<string> ReleaseProfiles, {
IReadOnlyCollection<string> Qualities, public IReadOnlyCollection<string> ReleaseProfiles { get; init; } = Array.Empty<string>();
IReadOnlyCollection<string> CustomFormats public IReadOnlyCollection<string> Qualities { get; init; } = Array.Empty<string>();
); public IReadOnlyCollection<string> CustomFormats { get; init; } = Array.Empty<string>();
}
public record JsonPaths( public record JsonPaths
RadarrMetadata Radarr, {
SonarrMetadata Sonarr public RadarrMetadata Radarr { get; init; } = new();
); public SonarrMetadata Sonarr { get; init; } = new();
}
public record RepoMetadata( public record RepoMetadata
JsonPaths JsonPaths {
); public JsonPaths JsonPaths { get; init; } = new();
}

@ -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<RepoMetadata> _metadata;
public RepoMetadataBuilder(
IRepoMetadataParser parser,
IAppPaths paths)
{
_paths = paths;
_metadata = new Lazy<RepoMetadata>(parser.Deserialize);
}
public IReadOnlyList<IDirectoryInfo> ToDirectoryInfoList(IEnumerable<string> listOfDirectories)
{
return listOfDirectories.Select(x => _paths.RepoDirectory.SubDirectory(x)).ToList();
}
public IDirectoryInfo DocsDirectory => _paths.RepoDirectory.SubDirectory("docs");
public RepoMetadata GetMetadata()
{
return _metadata.Value;
}
}

@ -1,13 +0,0 @@
using System.IO.Abstractions;
namespace Recyclarr.TrashLib.Repo;
public record RepoPaths(
IReadOnlyCollection<IDirectoryInfo> RadarrCustomFormatPaths,
IReadOnlyCollection<IDirectoryInfo> SonarrReleaseProfilePaths,
IReadOnlyCollection<IDirectoryInfo> RadarrQualityPaths,
IReadOnlyCollection<IDirectoryInfo> SonarrQualityPaths,
IReadOnlyCollection<IDirectoryInfo> SonarrCustomFormatPaths,
IFileInfo RadarrCollectionOfCustomFormats,
IFileInfo SonarrCollectionOfCustomFormats
) : IRepoPaths;

@ -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<RepoMetadata> _metadata;
public RepoMetadata Metadata => _metadata.Value;
public RepoPathsFactory(IRepoMetadataParser parser, IAppPaths paths)
{
_paths = paths;
_metadata = new Lazy<RepoMetadata>(parser.Deserialize);
}
private List<IDirectoryInfo> ToDirectoryInfoList(IEnumerable<string> 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")
);
}
}

@ -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<CustomFormatData> 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<QualitySizeData> 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.");
}
}

@ -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<GuideDataLister>().As<IGuideDataLister>();
}
}

@ -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<CustomFormatData> customFormats);
void ListQualities(IEnumerable<QualitySizeData> qualityData);
}

@ -1,10 +0,0 @@
using Recyclarr.TrashLib.Services.CustomFormat.Models;
using Recyclarr.TrashLib.Services.QualitySize;
namespace Recyclarr.TrashLib.Services.Common;
public interface IGuideService
{
ICollection<CustomFormatData> GetCustomFormatData();
ICollection<QualitySizeData> GetQualities();
}

@ -15,6 +15,7 @@ public class CustomFormatAutofacModule : Module
builder.RegisterType<CustomFormatService>().As<ICustomFormatService>(); builder.RegisterType<CustomFormatService>().As<ICustomFormatService>();
builder.RegisterType<QualityProfileService>().As<IQualityProfileService>(); builder.RegisterType<QualityProfileService>().As<IQualityProfileService>();
builder.RegisterType<CustomFormatUpdater>().As<ICustomFormatUpdater>(); builder.RegisterType<CustomFormatUpdater>().As<ICustomFormatUpdater>();
builder.RegisterType<CustomFormatGuideService>().As<ICustomFormatGuideService>();
builder.RegisterType<CachePersister>().As<ICachePersister>(); builder.RegisterType<CachePersister>().As<ICachePersister>();
builder.RegisterType<GuideProcessor>().As<IGuideProcessor>(); builder.RegisterType<GuideProcessor>().As<IGuideProcessor>();
builder.RegisterType<CustomFormatLoader>().As<ICustomFormatLoader>(); builder.RegisterType<CustomFormatLoader>().As<ICustomFormatLoader>();

@ -1,6 +1,6 @@
using Recyclarr.Common.Extensions; using Recyclarr.Common.Extensions;
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Services.Common;
using Recyclarr.TrashLib.Services.CustomFormat.Processors; using Recyclarr.TrashLib.Services.CustomFormat.Processors;
using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps; using Recyclarr.TrashLib.Services.CustomFormat.Processors.PersistenceSteps;
using Spectre.Console; using Spectre.Console;
@ -29,11 +29,11 @@ internal class CustomFormatUpdater : ICustomFormatUpdater
_console = console; _console = console;
} }
public async Task Process(bool isPreview, IEnumerable<CustomFormatConfig> configs, IGuideService guideService) public async Task Process(bool isPreview, IEnumerable<CustomFormatConfig> configs, SupportedServices serviceType)
{ {
_cache.Load(); _cache.Load();
await _guideProcessor.BuildGuideDataAsync(configs, _cache.CfCache, guideService); await _guideProcessor.BuildGuideDataAsync(configs, _cache.CfCache, serviceType);
if (!ValidateGuideDataAndCheckShouldProceed()) if (!ValidateGuideDataAndCheckShouldProceed())
{ {

@ -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.");
}
}

@ -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<CustomFormatData> GetCustomFormatData(SupportedServices serviceType)
{
var paths = CreatePaths(serviceType);
return _cfLoader.LoadAllCustomFormatsAtPaths(
paths.CustomFormatDirectories,
paths.CollectionOfCustomFormatsMarkdown);
}
}

@ -0,0 +1,8 @@
using System.IO.Abstractions;
namespace Recyclarr.TrashLib.Services.CustomFormat.Guide;
internal record CustomFormatPaths(
IReadOnlyList<IDirectoryInfo> CustomFormatDirectories,
IFileInfo CollectionOfCustomFormatsMarkdown
);

@ -0,0 +1,9 @@
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Services.CustomFormat.Models;
namespace Recyclarr.TrashLib.Services.CustomFormat.Guide;
public interface ICustomFormatGuideService
{
ICollection<CustomFormatData> GetCustomFormatData(SupportedServices serviceType);
}

@ -1,9 +1,9 @@
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Services.Common;
namespace Recyclarr.TrashLib.Services.CustomFormat; namespace Recyclarr.TrashLib.Services.CustomFormat;
public interface ICustomFormatUpdater public interface ICustomFormatUpdater
{ {
Task Process(bool isPreview, IEnumerable<CustomFormatConfig> configs, IGuideService guideService); Task Process(bool isPreview, IEnumerable<CustomFormatConfig> configs, SupportedServices serviceType);
} }

@ -1,5 +1,6 @@
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services; 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;
using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache;
using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps; using Recyclarr.TrashLib.Services.CustomFormat.Processors.GuideSteps;
@ -17,10 +18,14 @@ internal class GuideProcessor : IGuideProcessor
{ {
private IList<CustomFormatData>? _guideCustomFormatJson; private IList<CustomFormatData>? _guideCustomFormatJson;
private readonly IGuideProcessorSteps _steps; private readonly IGuideProcessorSteps _steps;
private readonly ICustomFormatGuideService _guide;
public GuideProcessor(IGuideProcessorSteps steps) public GuideProcessor(
IGuideProcessorSteps steps,
ICustomFormatGuideService guide)
{ {
_steps = steps; _steps = steps;
_guide = guide;
} }
public IReadOnlyCollection<ProcessedCustomFormatData> ProcessedCustomFormats public IReadOnlyCollection<ProcessedCustomFormatData> ProcessedCustomFormats
@ -44,10 +49,12 @@ internal class GuideProcessor : IGuideProcessor
public IReadOnlyCollection<TrashIdMapping> DeletedCustomFormatsInCache public IReadOnlyCollection<TrashIdMapping> DeletedCustomFormatsInCache
=> _steps.CustomFormat.DeletedCustomFormatsInCache; => _steps.CustomFormat.DeletedCustomFormatsInCache;
public Task BuildGuideDataAsync(IEnumerable<CustomFormatConfig> config, CustomFormatCache? cache, public Task BuildGuideDataAsync(
IGuideService guideService) IEnumerable<CustomFormatConfig> config,
CustomFormatCache? cache,
SupportedServices serviceType)
{ {
_guideCustomFormatJson ??= guideService.GetCustomFormatData().ToList(); _guideCustomFormatJson ??= _guide.GetCustomFormatData(serviceType).ToList();
var listOfConfigs = config.ToList(); var listOfConfigs = config.ToList();

@ -1,5 +1,5 @@
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Services.Common;
using Recyclarr.TrashLib.Services.CustomFormat.Models; using Recyclarr.TrashLib.Services.CustomFormat.Models;
using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache; using Recyclarr.TrashLib.Services.CustomFormat.Models.Cache;
@ -15,6 +15,8 @@ internal interface IGuideProcessor
IReadOnlyCollection<TrashIdMapping> DeletedCustomFormatsInCache { get; } IReadOnlyCollection<TrashIdMapping> DeletedCustomFormatsInCache { get; }
IReadOnlyDictionary<string, Dictionary<string, HashSet<int>>> DuplicateScores { get; } IReadOnlyDictionary<string, Dictionary<string, HashSet<int>>> DuplicateScores { get; }
Task BuildGuideDataAsync(IEnumerable<CustomFormatConfig> config, CustomFormatCache? cache, Task BuildGuideDataAsync(
IGuideService guideService); IEnumerable<CustomFormatConfig> config,
CustomFormatCache? cache,
SupportedServices serviceType);
} }

@ -1,6 +1,6 @@
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.CustomFormat;
using Recyclarr.TrashLib.Services.QualitySize; using Recyclarr.TrashLib.Services.QualitySize;
using Recyclarr.TrashLib.Services.Radarr;
using Recyclarr.TrashLib.Services.Radarr.Config; using Recyclarr.TrashLib.Services.Radarr.Config;
namespace Recyclarr.TrashLib.Services.Processors; namespace Recyclarr.TrashLib.Services.Processors;
@ -10,20 +10,17 @@ public class RadarrProcessor : IServiceProcessor
private readonly ILogger _log; private readonly ILogger _log;
private readonly ICustomFormatUpdater _cfUpdater; private readonly ICustomFormatUpdater _cfUpdater;
private readonly IQualitySizeUpdater _qualityUpdater; private readonly IQualitySizeUpdater _qualityUpdater;
private readonly RadarrGuideService _guideService;
private readonly RadarrConfiguration _config; private readonly RadarrConfiguration _config;
public RadarrProcessor( public RadarrProcessor(
ILogger log, ILogger log,
ICustomFormatUpdater cfUpdater, ICustomFormatUpdater cfUpdater,
IQualitySizeUpdater qualityUpdater, IQualitySizeUpdater qualityUpdater,
RadarrGuideService guideService,
RadarrConfiguration config) RadarrConfiguration config)
{ {
_log = log; _log = log;
_cfUpdater = cfUpdater; _cfUpdater = cfUpdater;
_qualityUpdater = qualityUpdater; _qualityUpdater = qualityUpdater;
_guideService = guideService;
_config = config; _config = config;
} }
@ -33,13 +30,13 @@ public class RadarrProcessor : IServiceProcessor
if (_config.QualityDefinition != null) if (_config.QualityDefinition != null)
{ {
await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, _guideService); await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, SupportedServices.Radarr);
didWork = true; 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, SupportedServices.Radarr);
didWork = true; didWork = true;
} }

@ -1,9 +1,9 @@
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Services.CustomFormat; using Recyclarr.TrashLib.Services.CustomFormat;
using Recyclarr.TrashLib.Services.QualitySize; 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.Capabilities;
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile;
namespace Recyclarr.TrashLib.Services.Processors; namespace Recyclarr.TrashLib.Services.Processors;
@ -12,7 +12,6 @@ public class SonarrProcessor : IServiceProcessor
private readonly ILogger _log; private readonly ILogger _log;
private readonly ICustomFormatUpdater _cfUpdater; private readonly ICustomFormatUpdater _cfUpdater;
private readonly IQualitySizeUpdater _qualityUpdater; private readonly IQualitySizeUpdater _qualityUpdater;
private readonly SonarrGuideService _guideService;
private readonly IReleaseProfileUpdater _profileUpdater; private readonly IReleaseProfileUpdater _profileUpdater;
private readonly SonarrCapabilityEnforcer _compatibilityEnforcer; private readonly SonarrCapabilityEnforcer _compatibilityEnforcer;
private readonly SonarrConfiguration _config; private readonly SonarrConfiguration _config;
@ -21,7 +20,6 @@ public class SonarrProcessor : IServiceProcessor
ILogger log, ILogger log,
ICustomFormatUpdater cfUpdater, ICustomFormatUpdater cfUpdater,
IQualitySizeUpdater qualityUpdater, IQualitySizeUpdater qualityUpdater,
SonarrGuideService guideService,
IReleaseProfileUpdater profileUpdater, IReleaseProfileUpdater profileUpdater,
SonarrCapabilityEnforcer compatibilityEnforcer, SonarrCapabilityEnforcer compatibilityEnforcer,
SonarrConfiguration config) SonarrConfiguration config)
@ -29,7 +27,6 @@ public class SonarrProcessor : IServiceProcessor
_log = log; _log = log;
_cfUpdater = cfUpdater; _cfUpdater = cfUpdater;
_qualityUpdater = qualityUpdater; _qualityUpdater = qualityUpdater;
_guideService = guideService;
_profileUpdater = profileUpdater; _profileUpdater = profileUpdater;
_compatibilityEnforcer = compatibilityEnforcer; _compatibilityEnforcer = compatibilityEnforcer;
_config = config; _config = config;
@ -50,13 +47,13 @@ public class SonarrProcessor : IServiceProcessor
if (_config.QualityDefinition != null) if (_config.QualityDefinition != null)
{ {
await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, _guideService); await _qualityUpdater.Process(settings.Preview, _config.QualityDefinition, SupportedServices.Sonarr);
didWork = true; 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, SupportedServices.Sonarr);
didWork = true; didWork = true;
} }

@ -0,0 +1,8 @@
using Recyclarr.TrashLib.Config;
namespace Recyclarr.TrashLib.Services.QualitySize.Guide;
public interface IQualityGuideService
{
IReadOnlyList<QualitySizeData> GetQualitySizeData(SupportedServices serviceType);
}

@ -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<QualitySizeData> GetQualitySizeData(SupportedServices serviceType)
{
var paths = CreatePaths(serviceType);
return _parser.GetQualities(paths.QualitySizeDirectories);
}
}

@ -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.");
}
}

@ -6,7 +6,7 @@ using Recyclarr.Common.Extensions;
namespace Recyclarr.TrashLib.Services.QualitySize.Guide; namespace Recyclarr.TrashLib.Services.QualitySize.Guide;
internal class QualitySizeGuideParser<T> where T : class public class QualitySizeGuideParser
{ {
private readonly ILogger _log; private readonly ILogger _log;
@ -15,7 +15,7 @@ internal class QualitySizeGuideParser<T> where T : class
_log = log; _log = log;
} }
public ICollection<T> GetQualities(IEnumerable<IDirectoryInfo> jsonDirectories) public IReadOnlyList<QualitySizeData> GetQualities(IEnumerable<IDirectoryInfo> jsonDirectories)
{ {
return JsonUtils.GetJsonFilesInDirectories(jsonDirectories, _log) return JsonUtils.GetJsonFilesInDirectories(jsonDirectories, _log)
.Select(ParseQuality) .Select(ParseQuality)
@ -23,7 +23,7 @@ internal class QualitySizeGuideParser<T> where T : class
.ToList(); .ToList();
} }
private T? ParseQuality(IFileInfo jsonFile) private QualitySizeData? ParseQuality(IFileInfo jsonFile)
{ {
var serializer = JsonSerializer.Create(new JsonSerializerSettings var serializer = JsonSerializer.Create(new JsonSerializerSettings
{ {
@ -34,7 +34,7 @@ internal class QualitySizeGuideParser<T> where T : class
}); });
using var json = new JsonTextReader(jsonFile.OpenText()); using var json = new JsonTextReader(jsonFile.OpenText());
var quality = serializer.Deserialize<T>(json); var quality = serializer.Deserialize<QualitySizeData>(json);
if (quality is null) if (quality is null)
{ {
_log.Debug("Failed to parse quality definition JSON file: {Filename}", jsonFile.FullName); _log.Debug("Failed to parse quality definition JSON file: {Filename}", jsonFile.FullName);

@ -0,0 +1,7 @@
using System.IO.Abstractions;
namespace Recyclarr.TrashLib.Services.QualitySize.Guide;
internal record QualitySizePaths(
IReadOnlyCollection<IDirectoryInfo> QualitySizeDirectories
);

@ -1,9 +1,9 @@
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Services.Common;
namespace Recyclarr.TrashLib.Services.QualitySize; namespace Recyclarr.TrashLib.Services.QualitySize;
public interface IQualitySizeUpdater public interface IQualitySizeUpdater
{ {
Task Process(bool isPreview, QualityDefinitionConfig config, IGuideService guideService); Task Process(bool isPreview, QualityDefinitionConfig config, SupportedServices serviceType);
} }

@ -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<QualityDefinitionService>().As<IQualityDefinitionService>();
builder.RegisterType<QualitySizeUpdater>().As<IQualitySizeUpdater>();
builder.RegisterType<QualityGuideService>().As<IQualityGuideService>();
builder.RegisterType<QualitySizeGuideParser>();
}
}

@ -1,7 +1,8 @@
using Recyclarr.Common.Extensions; using Recyclarr.Common.Extensions;
using Recyclarr.TrashLib.Config;
using Recyclarr.TrashLib.Config.Services; using Recyclarr.TrashLib.Config.Services;
using Recyclarr.TrashLib.Services.Common;
using Recyclarr.TrashLib.Services.QualitySize.Api; using Recyclarr.TrashLib.Services.QualitySize.Api;
using Recyclarr.TrashLib.Services.QualitySize.Guide;
using Spectre.Console; using Spectre.Console;
namespace Recyclarr.TrashLib.Services.QualitySize; namespace Recyclarr.TrashLib.Services.QualitySize;
@ -11,21 +12,24 @@ internal class QualitySizeUpdater : IQualitySizeUpdater
private readonly ILogger _log; private readonly ILogger _log;
private readonly IQualityDefinitionService _api; private readonly IQualityDefinitionService _api;
private readonly IAnsiConsole _console; private readonly IAnsiConsole _console;
private readonly IQualityGuideService _guide;
public QualitySizeUpdater( public QualitySizeUpdater(
ILogger logger, ILogger logger,
IQualityDefinitionService api, IQualityDefinitionService api,
IAnsiConsole console) IAnsiConsole console,
IQualityGuideService guide)
{ {
_log = logger; _log = logger;
_api = api; _api = api;
_console = console; _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); _log.Information("Processing Quality Definition: {QualityDefinition}", config.Type);
var qualityDefinitions = guideService.GetQualities(); var qualityDefinitions = _guide.GetQualitySizeData(serviceType);
var qualityTypeInConfig = config.Type; var qualityTypeInConfig = config.Type;
var selectedQuality = qualityDefinitions var selectedQuality = qualityDefinitions

@ -1,7 +0,0 @@
namespace Recyclarr.TrashLib.Services.Radarr;
public interface IRadarrGuideDataLister
{
void ListCustomFormats();
void ListQualities();
}

@ -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<QualitySizeData> _parser;
public LocalRepoRadarrGuideService(IRepoPathsFactory pathsFactory, ILogger log, ICustomFormatLoader cfLoader)
{
_pathsFactory = pathsFactory;
_cfLoader = cfLoader;
_parser = new QualitySizeGuideParser<QualitySizeData>(log);
}
public override ICollection<QualitySizeData> GetQualities()
{
return _parser.GetQualities(_pathsFactory.Create().RadarrQualityPaths);
}
public override ICollection<CustomFormatData> GetCustomFormatData()
{
var paths = _pathsFactory.Create();
return _cfLoader.LoadAllCustomFormatsAtPaths(
paths.RadarrCustomFormatPaths,
paths.RadarrCollectionOfCustomFormats);
}
}

@ -1,8 +1,4 @@
using Autofac; 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; namespace Recyclarr.TrashLib.Services.Radarr;
@ -10,14 +6,6 @@ public class RadarrAutofacModule : Module
{ {
protected override void Load(ContainerBuilder builder) protected override void Load(ContainerBuilder builder)
{ {
builder.RegisterType<QualityDefinitionService>().As<IQualityDefinitionService>();
builder.RegisterType<RadarrGuideDataLister>().As<IRadarrGuideDataLister>();
builder.RegisterType<QualitySizeUpdater>().As<IQualitySizeUpdater>();
builder.RegisterType<LocalRepoRadarrGuideService>()
.As<RadarrGuideService>()
.Keyed<IGuideService>(SupportedServices.Radarr);
builder.RegisterType<RadarrCapabilityChecker>().As<IRadarrCapabilityChecker>() builder.RegisterType<RadarrCapabilityChecker>().As<IRadarrCapabilityChecker>()
.InstancePerLifetimeScope(); .InstancePerLifetimeScope();
} }

@ -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());
}
}

@ -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<CustomFormatData> GetCustomFormatData();
public abstract ICollection<QualitySizeData> GetQualities();
}

@ -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 public interface IReleaseProfileApiService
{ {

@ -1,7 +1,7 @@
using Newtonsoft.Json.Linq; 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 public interface ISonarrReleaseProfileCompatibilityHandler
{ {

@ -1,8 +1,8 @@
using AutoMapper; using AutoMapper;
using JetBrains.Annotations; 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] [UsedImplicitly]
public class SonarrApiObjectMappingProfile : Profile public class SonarrApiObjectMappingProfile : Profile

@ -1,7 +1,7 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Recyclarr.TrashLib.Services.Sonarr.Api.Objects; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects;
[UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Members)] [UsedImplicitly(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Members)]
public class SonarrPreferredTerm public class SonarrPreferredTerm

@ -1,9 +1,9 @@
using Flurl.Http; using Flurl.Http;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Recyclarr.TrashLib.Http; 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 public class ReleaseProfileApiService : IReleaseProfileApiService
{ {

@ -1,4 +1,4 @@
namespace Recyclarr.TrashLib.Services.Sonarr.Api.Schemas; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api.Schemas;
public static class SonarrReleaseProfileSchema public static class SonarrReleaseProfileSchema
{ {

@ -2,11 +2,11 @@ using AutoMapper;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema; using Newtonsoft.Json.Schema;
using Recyclarr.TrashLib.ExceptionTypes; using Recyclarr.TrashLib.ExceptionTypes;
using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Objects;
using Recyclarr.TrashLib.Services.Sonarr.Api.Schemas; using Recyclarr.TrashLib.Services.ReleaseProfile.Api.Schemas;
using Recyclarr.TrashLib.Services.Sonarr.Capabilities; using Recyclarr.TrashLib.Services.Sonarr.Capabilities;
namespace Recyclarr.TrashLib.Services.Sonarr.Api; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Api;
public class SonarrReleaseProfileCompatibilityHandler : ISonarrReleaseProfileCompatibilityHandler public class SonarrReleaseProfileCompatibilityHandler : ISonarrReleaseProfileCompatibilityHandler
{ {

@ -1,6 +1,6 @@
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters;
public interface IReleaseProfileFilter public interface IReleaseProfileFilter
{ {

@ -1,6 +1,6 @@
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters;
public interface IReleaseProfileFilterPipeline public interface IReleaseProfileFilterPipeline
{ {

@ -1,6 +1,6 @@
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters;
public class IncludeExcludeFilter : IReleaseProfileFilter public class IncludeExcludeFilter : IReleaseProfileFilter
{ {

@ -1,7 +1,7 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters;
public class ReleaseProfileDataFilterer public class ReleaseProfileDataFilterer
{ {

@ -1,7 +1,7 @@
using FluentValidation.Results; using FluentValidation.Results;
using Recyclarr.Common.FluentValidation; using Recyclarr.Common.FluentValidation;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters;
public class ReleaseProfileDataValidationFilterer public class ReleaseProfileDataValidationFilterer
{ {

@ -1,6 +1,6 @@
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters;
public class ReleaseProfileFilterPipeline : IReleaseProfileFilterPipeline public class ReleaseProfileFilterPipeline : IReleaseProfileFilterPipeline
{ {

@ -1,6 +1,6 @@
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Filters;
public class StrictNegativeScoresFilter : IReleaseProfileFilter public class StrictNegativeScoresFilter : IReleaseProfileFilter
{ {

@ -0,0 +1,6 @@
namespace Recyclarr.TrashLib.Services.ReleaseProfile.Guide;
public interface IReleaseProfileGuideService
{
IReadOnlyList<ReleaseProfileData> GetReleaseProfileData();
}

@ -1,36 +1,18 @@
using System.Text; using System.Text;
using JetBrains.Annotations; using Recyclarr.Common.Extensions;
using Recyclarr.TrashLib.Services.Common;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile;
using Spectre.Console; using Spectre.Console;
namespace Recyclarr.TrashLib.Services.Sonarr; namespace Recyclarr.TrashLib.Services.ReleaseProfile.Guide;
[UsedImplicitly] public class ReleaseProfileDataLister
public class SonarrGuideDataLister : ISonarrGuideDataLister
{ {
private readonly IAnsiConsole _console; private readonly IAnsiConsole _console;
private readonly SonarrGuideService _guide; private readonly IReleaseProfileGuideService _guide;
private readonly IGuideDataLister _guideLister;
public SonarrGuideDataLister( public ReleaseProfileDataLister(IAnsiConsole console, IReleaseProfileGuideService guide)
IAnsiConsole console,
SonarrGuideService guide,
IGuideDataLister guideLister)
{ {
_console = console; _console = console;
_guide = guide; _guide = guide;
_guideLister = guideLister;
}
public void ListCustomFormats()
{
_guideLister.ListCustomFormats(_guide.GetCustomFormatData());
}
public void ListQualities()
{
_guideLister.ListQualities(_guide.GetQualities());
} }
public void ListReleaseProfiles() public void ListReleaseProfiles()
@ -62,7 +44,9 @@ public class SonarrGuideDataLister : ISonarrGuideDataLister
public void ListTerms(string releaseProfileId) public void ListTerms(string releaseProfileId)
{ {
var profile = _guide.GetUnfilteredProfileById(releaseProfileId); var profile = _guide.GetReleaseProfileData()
.FirstOrDefault(x => x.TrashId.EqualsIgnoreCase(releaseProfileId));
if (profile is null) if (profile is null)
{ {
throw new ArgumentException("No release profile found with that Trash ID"); throw new ArgumentException("No release profile found with that Trash ID");

@ -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<ReleaseProfileData?> LoadAndParseFile(IFileInfo file, params JsonConverter[] converters)
{
try
{
using var stream = file.OpenText();
var json = await stream.ReadToEndAsync();
return JsonConvert.DeserializeObject<ReleaseProfileData>(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<ReleaseProfileData> GetReleaseProfileData(IEnumerable<IDirectoryInfo> 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);
}
}

@ -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<ReleaseProfileData> GetReleaseProfileData()
{
var paths = GetPaths();
return _parser.GetReleaseProfileData(paths.ReleaseProfileDirectories).ToList();
}
}

@ -0,0 +1,7 @@
using System.IO.Abstractions;
namespace Recyclarr.TrashLib.Services.ReleaseProfile.Guide;
public record ReleaseProfilePaths(
IReadOnlyList<IDirectoryInfo> ReleaseProfileDirectories
);

@ -1,6 +1,6 @@
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Services.ReleaseProfile;
public interface IReleaseProfileUpdater public interface IReleaseProfileUpdater
{ {

@ -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<ReleaseProfileApiService>().As<IReleaseProfileApiService>();
builder.RegisterType<ReleaseProfileUpdater>().As<IReleaseProfileUpdater>();
builder.RegisterType<SonarrReleaseProfileCompatibilityHandler>()
.As<ISonarrReleaseProfileCompatibilityHandler>();
builder.RegisterType<ReleaseProfileFilterPipeline>().As<IReleaseProfileFilterPipeline>();
builder.RegisterType<ReleaseProfileGuideParser>();
builder.RegisterType<ReleaseProfileGuideService>().As<IReleaseProfileGuideService>();
// Release Profile Filters (ORDER MATTERS!)
builder.RegisterTypes(
typeof(IncludeExcludeFilter),
typeof(StrictNegativeScoresFilter))
.As<IReleaseProfileFilter>()
.OrderByRegistration();
}
}

@ -1,7 +1,7 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Services.ReleaseProfile;
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public record TermData public record TermData
@ -11,11 +11,6 @@ public record TermData
public string Name { get; init; } = string.Empty; public string Name { get; init; } = string.Empty;
public string Term { 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)] [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
@ -29,11 +24,6 @@ public record PreferredTermData
score = Score; score = Score;
terms = Terms; terms = Terms;
} }
public override string ToString()
{
return $"[Score: {Score}] [Terms: {Terms.Count}]";
}
} }
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)] [UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
@ -47,14 +37,4 @@ public record ReleaseProfileData
public IReadOnlyCollection<TermData> Required { get; init; } = Array.Empty<TermData>(); public IReadOnlyCollection<TermData> Required { get; init; } = Array.Empty<TermData>();
public IReadOnlyCollection<TermData> Ignored { get; init; } = Array.Empty<TermData>(); public IReadOnlyCollection<TermData> Ignored { get; init; } = Array.Empty<TermData>();
public IReadOnlyCollection<PreferredTermData> Preferred { get; init; } = Array.Empty<PreferredTermData>(); public IReadOnlyCollection<PreferredTermData> Preferred { get; init; } = Array.Empty<PreferredTermData>();
public override string ToString()
{
return $"[TrashId: {TrashId}] " +
$"[Name: {Name}] " +
$"[IncludePreferred: {IncludePreferredWhenRenaming}] " +
$"[Required: {Required.Count}] " +
$"[Ignored: {Ignored.Count}] " +
$"[Preferred: {Preferred.Count}]";
}
} }

@ -1,6 +1,6 @@
using FluentValidation; using FluentValidation;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Services.ReleaseProfile;
internal class TermDataValidator : AbstractValidator<TermData> internal class TermDataValidator : AbstractValidator<TermData>
{ {

@ -1,32 +1,35 @@
using Recyclarr.Common.Extensions; 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;
using Recyclarr.TrashLib.Services.Sonarr.Api.Objects; using Recyclarr.TrashLib.Services.Sonarr.Api.Objects;
using Recyclarr.TrashLib.Services.Sonarr.Config; using Recyclarr.TrashLib.Services.Sonarr.Config;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters;
using Spectre.Console; using Spectre.Console;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Services.ReleaseProfile;
public class ReleaseProfileUpdater : IReleaseProfileUpdater public class ReleaseProfileUpdater : IReleaseProfileUpdater
{ {
private readonly IReleaseProfileApiService _releaseProfileApi; private readonly IReleaseProfileApiService _releaseProfileApi;
private readonly IReleaseProfileFilterPipeline _pipeline; private readonly IReleaseProfileFilterPipeline _pipeline;
private readonly IAnsiConsole _console; private readonly IAnsiConsole _console;
private readonly SonarrGuideService _guide; private readonly IReleaseProfileGuideService _guide;
private readonly ISonarrApi _api; private readonly ISonarrTagApiService _tagApiService;
private readonly ILogger _log; private readonly ILogger _log;
public ReleaseProfileUpdater( public ReleaseProfileUpdater(
ILogger logger, ILogger logger,
SonarrGuideService guide, IReleaseProfileGuideService guide,
ISonarrApi api, ISonarrTagApiService tagApiService,
IReleaseProfileApiService releaseProfileApi, IReleaseProfileApiService releaseProfileApi,
IReleaseProfileFilterPipeline pipeline, IReleaseProfileFilterPipeline pipeline,
IAnsiConsole console) IAnsiConsole console)
{ {
_log = logger; _log = logger;
_guide = guide; _guide = guide;
_api = api; _tagApiService = tagApiService;
_releaseProfileApi = releaseProfileApi; _releaseProfileApi = releaseProfileApi;
_pipeline = pipeline; _pipeline = pipeline;
_console = console; _console = console;
@ -195,7 +198,7 @@ public class ReleaseProfileUpdater : IReleaseProfileUpdater
return Array.Empty<int>(); return Array.Empty<int>();
} }
var sonarrTags = await _api.GetTags(); var sonarrTags = await _tagApiService.GetTags();
await CreateMissingTags(sonarrTags, tags); await CreateMissingTags(sonarrTags, tags);
return sonarrTags return sonarrTags
.Where(t => tags.Any(ct => ct.EqualsIgnoreCase(t.Label))) .Where(t => tags.Any(ct => ct.EqualsIgnoreCase(t.Label)))
@ -209,7 +212,7 @@ public class ReleaseProfileUpdater : IReleaseProfileUpdater
foreach (var tag in missingTags) foreach (var tag in missingTags)
{ {
_log.Debug("Creating Tag: {Tag}", tag); _log.Debug("Creating Tag: {Tag}", tag);
var newTag = await _api.CreateTag(tag); var newTag = await _tagApiService.CreateTag(tag);
sonarrTags.Add(newTag); sonarrTags.Add(newTag);
} }
} }

@ -1,4 +1,4 @@
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Services.ReleaseProfile;
public class ScopedState<T> public class ScopedState<T>
{ {

@ -1,7 +1,7 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile; namespace Recyclarr.TrashLib.Services.ReleaseProfile;
internal class TermDataConverter : JsonConverter internal class TermDataConverter : JsonConverter
{ {

@ -1,13 +0,0 @@
using Recyclarr.TrashLib.Services.Sonarr.Api.Objects;
namespace Recyclarr.TrashLib.Services.Sonarr.Api;
public interface ISonarrApi
{
Task<IList<SonarrTag>> GetTags();
Task<SonarrTag> CreateTag(string tag);
Task<IReadOnlyCollection<SonarrQualityDefinitionItem>> GetQualityDefinition();
Task<IList<SonarrQualityDefinitionItem>> UpdateQualityDefinition(
IReadOnlyCollection<SonarrQualityDefinitionItem> newQuality);
}

@ -0,0 +1,9 @@
using Recyclarr.TrashLib.Services.Sonarr.Api.Objects;
namespace Recyclarr.TrashLib.Services.Sonarr.Api;
public interface ISonarrTagApiService
{
Task<IList<SonarrTag>> GetTags();
Task<SonarrTag> CreateTag(string tag);
}

@ -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; }
}

@ -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<IList<SonarrTag>> GetTags()
{
return await _service.Request("tag")
.GetJsonAsync<List<SonarrTag>>();
}
public async Task<SonarrTag> CreateTag(string tag)
{
return await _service.Request("tag")
.PostJsonAsync(new {label = tag})
.ReceiveJson<SonarrTag>();
}
public async Task<IReadOnlyCollection<SonarrQualityDefinitionItem>> GetQualityDefinition()
{
return await _service.Request("qualitydefinition")
.GetJsonAsync<List<SonarrQualityDefinitionItem>>();
}
public async Task<IList<SonarrQualityDefinitionItem>> UpdateQualityDefinition(
IReadOnlyCollection<SonarrQualityDefinitionItem> newQuality)
{
return await _service.Request("qualityDefinition", "update")
.PutJsonAsync(newQuality)
.ReceiveJson<List<SonarrQualityDefinitionItem>>();
}
}

@ -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<IList<SonarrTag>> GetTags()
{
return await _service.Request("tag")
.GetJsonAsync<List<SonarrTag>>();
}
public async Task<SonarrTag> CreateTag(string tag)
{
return await _service.Request("tag")
.PostJsonAsync(new {label = tag})
.ReceiveJson<SonarrTag>();
}
}

@ -1,9 +0,0 @@
namespace Recyclarr.TrashLib.Services.Sonarr;
public interface ISonarrGuideDataLister
{
void ListReleaseProfiles();
void ListTerms(string releaseProfileId);
void ListQualities();
void ListCustomFormats();
}

@ -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<IEnumerable<ReleaseProfileData>> _data;
private readonly QualitySizeGuideParser<QualitySizeData> _parser;
public LocalRepoSonarrGuideService(
IRepoPathsFactory pathsFactory,
ILogger log,
ICustomFormatLoader cfLoader)
{
_pathsFactory = pathsFactory;
_log = log;
_cfLoader = cfLoader;
_data = new Lazy<IEnumerable<ReleaseProfileData>>(GetReleaseProfileDataImpl);
_parser = new QualitySizeGuideParser<QualitySizeData>(log);
}
public override ICollection<QualitySizeData> GetQualities()
{
return _parser.GetQualities(_pathsFactory.Create().SonarrQualityPaths);
}
public override ICollection<CustomFormatData> GetCustomFormatData()
{
var paths = _pathsFactory.Create();
return _cfLoader.LoadAllCustomFormatsAtPaths(
paths.SonarrCustomFormatPaths,
paths.SonarrCollectionOfCustomFormats);
}
private IEnumerable<ReleaseProfileData> 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<ReleaseProfileData?> LoadAndParseFile(IFileInfo file, params JsonConverter[] converters)
{
try
{
using var stream = file.OpenText();
var json = await stream.ReadToEndAsync();
return JsonConvert.DeserializeObject<ReleaseProfileData>(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<ReleaseProfileData> GetReleaseProfileData()
{
return _data.Value.ToList();
}
}

@ -1,11 +1,6 @@
using Autofac; 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.Api;
using Recyclarr.TrashLib.Services.Sonarr.Capabilities; using Recyclarr.TrashLib.Services.Sonarr.Capabilities;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile;
using Recyclarr.TrashLib.Services.Sonarr.ReleaseProfile.Filters;
namespace Recyclarr.TrashLib.Services.Sonarr; namespace Recyclarr.TrashLib.Services.Sonarr;
@ -13,29 +8,9 @@ public class SonarrAutofacModule : Module
{ {
protected override void Load(ContainerBuilder builder) protected override void Load(ContainerBuilder builder)
{ {
builder.RegisterType<SonarrApi>().As<ISonarrApi>(); builder.RegisterType<SonarrTagApiService>().As<ISonarrTagApiService>();
builder.RegisterType<ReleaseProfileApiService>().As<IReleaseProfileApiService>();
builder.RegisterType<SonarrCapabilityEnforcer>(); builder.RegisterType<SonarrCapabilityEnforcer>();
builder.RegisterType<SonarrCapabilityChecker>().As<ISonarrCapabilityChecker>() builder.RegisterType<SonarrCapabilityChecker>().As<ISonarrCapabilityChecker>()
.InstancePerLifetimeScope(); .InstancePerLifetimeScope();
builder.RegisterType<SonarrGuideDataLister>().As<ISonarrGuideDataLister>();
builder.RegisterType<LocalRepoSonarrGuideService>()
.As<SonarrGuideService>()
.Keyed<IGuideService>(SupportedServices.Sonarr);
// Release Profile Support
builder.RegisterType<ReleaseProfileUpdater>().As<IReleaseProfileUpdater>();
builder.RegisterType<SonarrReleaseProfileCompatibilityHandler>()
.As<ISonarrReleaseProfileCompatibilityHandler>();
builder.RegisterType<ReleaseProfileFilterPipeline>().As<IReleaseProfileFilterPipeline>();
// Release Profile Filters (ORDER MATTERS!)
builder.RegisterTypes(
typeof(IncludeExcludeFilter),
typeof(StrictNegativeScoresFilter))
.As<IReleaseProfileFilter>()
.OrderByRegistration();
} }
} }

@ -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<ReleaseProfileData> GetReleaseProfileData();
public abstract ReleaseProfileData? GetUnfilteredProfileById(string trashId);
public abstract ICollection<CustomFormatData> GetCustomFormatData();
public abstract ICollection<QualitySizeData> GetQualities();
}
Loading…
Cancel
Save