refactor: Make release profile sync pipeline generic

pull/231/head
Robert Dailey 11 months ago
parent 5c27c6bf56
commit 647e0280ec

@ -84,7 +84,7 @@ public static class CompositionRoot
typeof(CustomFormatSyncPipeline),
typeof(QualityProfileSyncPipeline),
typeof(QualitySizeSyncPipeline),
typeof(ReleaseProfileSyncPipeline),
typeof(GenericSyncPipeline<ReleaseProfilePipelineContext>),
typeof(GenericSyncPipeline<MediaNamingPipelineContext>))
.As<ISyncPipeline>()
.OrderByRegistration();

@ -1,12 +1,14 @@
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.ReleaseProfile;
namespace Recyclarr.Cli.Pipelines.ReleaseProfile.PipelinePhases;
public class ReleaseProfileApiFetchPhase(IReleaseProfileApiService rpService)
: IApiFetchPipelinePhase<ReleaseProfilePipelineContext>
{
public async Task<IList<SonarrReleaseProfile>> Execute(IServiceConfiguration config)
public async Task Execute(ReleaseProfilePipelineContext context, IServiceConfiguration config)
{
return await rpService.GetReleaseProfiles(config);
context.ApiFetchOutput = await rpService.GetReleaseProfiles(config);
}
}

@ -1,28 +1,28 @@
using Recyclarr.Cli.Pipelines.ReleaseProfile.Models;
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.ReleaseProfile;
namespace Recyclarr.Cli.Pipelines.ReleaseProfile.PipelinePhases;
public class ReleaseProfileApiPersistencePhase(ILogger log, IReleaseProfileApiService api)
public class ReleaseProfileApiPersistencePhase(IReleaseProfileApiService api)
: IApiPersistencePipelinePhase<ReleaseProfilePipelineContext>
{
public async Task Execute(IServiceConfiguration config, ReleaseProfileTransactionData transactions)
public async Task Execute(ReleaseProfilePipelineContext context, IServiceConfiguration config)
{
var transactions = context.TransactionOutput;
foreach (var profile in transactions.UpdatedProfiles)
{
log.Information("Update existing profile: {ProfileName}", profile.Name);
await api.UpdateReleaseProfile(config, profile);
}
foreach (var profile in transactions.CreatedProfiles)
{
log.Information("Create new profile: {ProfileName}", profile.Name);
await api.CreateReleaseProfile(config, profile);
}
foreach (var profile in transactions.DeletedProfiles)
{
log.Information("Deleting old release profile: {ProfileName}", profile.Name);
await api.DeleteReleaseProfile(config, profile.Id);
}
}

@ -1,3 +1,4 @@
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Cli.Pipelines.ReleaseProfile.Filters;
using Recyclarr.Common.Extensions;
using Recyclarr.Config.Models;
@ -14,19 +15,21 @@ public class ReleaseProfileConfigPhase(
ILogger log,
IReleaseProfileGuideService guide,
IReleaseProfileFilterPipeline filters)
: IConfigPipelinePhase<ReleaseProfilePipelineContext>
{
public IReadOnlyList<ProcessedReleaseProfileData>? Execute(SonarrConfiguration config)
public Task Execute(ReleaseProfilePipelineContext context, IServiceConfiguration config)
{
if (config.ReleaseProfiles.IsEmpty())
var releaseProfiles = ((SonarrConfiguration) config).ReleaseProfiles;
if (!releaseProfiles.Any())
{
log.Debug("{Instance} has no release profiles", config.InstanceName);
return null;
return Task.CompletedTask;
}
var profilesFromGuide = guide.GetReleaseProfileData();
var filteredProfiles = new List<ProcessedReleaseProfileData>();
var configProfiles = config.ReleaseProfiles.SelectMany(x => x.TrashIds.Select(y => (TrashId: y, Config: x)));
var configProfiles = releaseProfiles.SelectMany(x => x.TrashIds.Select(y => (TrashId: y, Config: x)));
foreach (var (trashId, configProfile) in configProfiles)
{
// For each release profile specified in our YAML config, find the matching profile in the guide.
@ -44,6 +47,7 @@ public class ReleaseProfileConfigPhase(
filteredProfiles.Add(new ProcessedReleaseProfileData(selectedProfile, configProfile.Tags));
}
return filteredProfiles;
context.ConfigOutput = filteredProfiles;
return Task.CompletedTask;
}
}

@ -0,0 +1,46 @@
using Recyclarr.Cli.Pipelines.Generic;
namespace Recyclarr.Cli.Pipelines.ReleaseProfile.PipelinePhases;
public class ReleaseProfileLogPhase(ILogger log) : ILogPipelinePhase<ReleaseProfilePipelineContext>
{
public bool LogConfigPhaseAndExitIfNeeded(ReleaseProfilePipelineContext context)
{
if (context.ConfigOutput.Any())
{
return false;
}
log.Debug("No Release Profiles to process");
return true;
}
public void LogPersistenceResults(ReleaseProfilePipelineContext context)
{
var transactions = context.TransactionOutput;
var somethingChanged = false;
if (transactions.UpdatedProfiles.Count != 0)
{
log.Information("Update existing profiles: {ProfileNames}", transactions.UpdatedProfiles);
somethingChanged = true;
}
if (transactions.CreatedProfiles.Count != 0)
{
log.Information("Create new profiles: {ProfileNames}", transactions.CreatedProfiles);
somethingChanged = true;
}
if (transactions.DeletedProfiles.Count != 0)
{
log.Information("Deleting old release profiles: {ProfileNames}", transactions.DeletedProfiles);
somethingChanged = true;
}
if (!somethingChanged)
{
log.Information("All Release Profiles are up to date!");
}
}
}

@ -1,13 +1,15 @@
using Recyclarr.Cli.Pipelines.ReleaseProfile.Models;
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.ServarrApi.ReleaseProfile;
using Spectre.Console;
namespace Recyclarr.Cli.Pipelines.ReleaseProfile.PipelinePhases;
public class ReleaseProfilePreviewPhase(IAnsiConsole console)
public class ReleaseProfilePreviewPhase(IAnsiConsole console) : IPreviewPipelinePhase<ReleaseProfilePipelineContext>
{
public void Execute(ReleaseProfileTransactionData profiles)
public void Execute(ReleaseProfilePipelineContext context)
{
var profiles = context.TransactionOutput;
var tree = new Tree("Release Profiles [red](Preview)[/]");
PrintCategoryOfChanges("Created Profiles", tree, profiles.CreatedProfiles);

@ -1,3 +1,4 @@
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Cli.Pipelines.ReleaseProfile.Models;
using Recyclarr.Cli.Pipelines.Tags;
using Recyclarr.Common.Extensions;
@ -6,18 +7,17 @@ using Recyclarr.ServarrApi.ReleaseProfile;
namespace Recyclarr.Cli.Pipelines.ReleaseProfile.PipelinePhases;
public class ReleaseProfileTransactionPhase(ServiceTagCache tagCache)
: ITransactionPipelinePhase<ReleaseProfilePipelineContext>
{
public ReleaseProfileTransactionData Execute(
IReadOnlyList<ProcessedReleaseProfileData> configProfiles,
IList<SonarrReleaseProfile> serviceData)
public void Execute(ReleaseProfilePipelineContext context)
{
var created = new List<SonarrReleaseProfile>();
var updated = new List<SonarrReleaseProfile>();
foreach (var configProfile in configProfiles)
foreach (var configProfile in context.ConfigOutput)
{
var title = $"[Trash] {configProfile.Profile.Name}";
var matchingServiceProfile = serviceData.FirstOrDefault(x => x.Name.EqualsIgnoreCase(title));
var matchingServiceProfile = context.ApiFetchOutput.FirstOrDefault(x => x.Name.EqualsIgnoreCase(title));
if (matchingServiceProfile is not null)
{
SetupProfileRequestObject(matchingServiceProfile, configProfile);
@ -31,9 +31,8 @@ public class ReleaseProfileTransactionPhase(ServiceTagCache tagCache)
}
}
var deleted = DeleteOldManagedProfiles(serviceData, configProfiles);
return new ReleaseProfileTransactionData(updated, created, deleted);
var deleted = DeleteOldManagedProfiles(context.ApiFetchOutput, context.ConfigOutput.AsReadOnly());
context.TransactionOutput = new ReleaseProfileTransactionData(updated, created, deleted);
}
private static List<SonarrReleaseProfile> DeleteOldManagedProfiles(

@ -1,5 +1,4 @@
using Autofac;
using Autofac.Extras.AggregateService;
using Autofac.Extras.Ordering;
using Recyclarr.Cli.Pipelines.ReleaseProfile.Filters;
using Recyclarr.Cli.Pipelines.ReleaseProfile.PipelinePhases;
@ -17,18 +16,20 @@ public class ReleaseProfileAutofacModule : Module
builder.RegisterType<ReleaseProfileFilterPipeline>().As<IReleaseProfileFilterPipeline>();
builder.RegisterType<ReleaseProfileDataLister>();
builder.RegisterAggregateService<IReleaseProfilePipelinePhases>();
builder.RegisterType<ReleaseProfileConfigPhase>();
builder.RegisterType<ReleaseProfileApiFetchPhase>();
builder.RegisterType<ReleaseProfileTransactionPhase>();
builder.RegisterType<ReleaseProfilePreviewPhase>();
builder.RegisterType<ReleaseProfileApiPersistencePhase>();
// Release Profile Filters (ORDER MATTERS!)
builder.RegisterTypes(
typeof(IncludeExcludeFilter),
typeof(StrictNegativeScoresFilter))
.As<IReleaseProfileFilter>()
.OrderByRegistration();
builder.RegisterTypes(
typeof(ReleaseProfileConfigPhase),
typeof(ReleaseProfilePreviewPhase),
typeof(ReleaseProfileApiFetchPhase),
typeof(ReleaseProfileTransactionPhase),
typeof(ReleaseProfileApiPersistencePhase),
typeof(ReleaseProfileLogPhase))
.AsImplementedInterfaces();
}
}

@ -0,0 +1,23 @@
using System.Diagnostics.CodeAnalysis;
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Cli.Pipelines.ReleaseProfile.Models;
using Recyclarr.Cli.Pipelines.ReleaseProfile.PipelinePhases;
using Recyclarr.Common;
using Recyclarr.ServarrApi.ReleaseProfile;
namespace Recyclarr.Cli.Pipelines.ReleaseProfile;
[SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification =
"Context objects are similar to DTOs; for usability we want to assign not append")]
public class ReleaseProfilePipelineContext : IPipelineContext
{
public string PipelineDescription => "Release Profile Pipeline";
public IReadOnlyCollection<SupportedServices> SupportedServiceTypes { get; } = new[]
{
SupportedServices.Sonarr
};
public IList<ProcessedReleaseProfileData> ConfigOutput { get; set; } = default!;
public IList<SonarrReleaseProfile> ApiFetchOutput { get; set; } = default!;
public ReleaseProfileTransactionData TransactionOutput { get; set; } = default!;
}

@ -1,45 +0,0 @@
using Recyclarr.Cli.Console.Settings;
using Recyclarr.Cli.Pipelines.ReleaseProfile.PipelinePhases;
using Recyclarr.Config.Models;
namespace Recyclarr.Cli.Pipelines.ReleaseProfile;
public interface IReleaseProfilePipelinePhases
{
ReleaseProfileConfigPhase ConfigPhase { get; }
ReleaseProfileApiFetchPhase ApiFetchPhase { get; }
ReleaseProfileTransactionPhase TransactionPhase { get; }
Lazy<ReleaseProfilePreviewPhase> PreviewPhase { get; }
ReleaseProfileApiPersistencePhase ApiPersistencePhase { get; }
}
public class ReleaseProfileSyncPipeline(ILogger log, IReleaseProfilePipelinePhases phases) : ISyncPipeline
{
public async Task Execute(ISyncSettings settings, IServiceConfiguration config)
{
if (config is not SonarrConfiguration sonarrConfig)
{
log.Debug("Skipping release profile pipeline because {Instance} is not a Sonarr config",
config.InstanceName);
return;
}
var profiles = phases.ConfigPhase.Execute(sonarrConfig);
if (profiles is null)
{
log.Debug("No release profiles to process");
return;
}
var serviceData = await phases.ApiFetchPhase.Execute(config);
var transactions = phases.TransactionPhase.Execute(profiles, serviceData);
if (settings.Preview)
{
phases.PreviewPhase.Value.Execute(transactions);
return;
}
await phases.ApiPersistencePhase.Execute(config, transactions);
}
}
Loading…
Cancel
Save