refactor: Generic pipelines support for media naming

pull/231/head
Robert Dailey 1 year ago
parent 55daad8db1
commit 532b954456

@ -9,6 +9,7 @@ using Recyclarr.Cli.Logging;
using Recyclarr.Cli.Migration;
using Recyclarr.Cli.Pipelines;
using Recyclarr.Cli.Pipelines.CustomFormat;
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Cli.Pipelines.MediaNaming;
using Recyclarr.Cli.Pipelines.QualityProfile;
using Recyclarr.Cli.Pipelines.QualitySize;
@ -75,13 +76,14 @@ public static class CompositionRoot
builder.RegisterModule<ReleaseProfileAutofacModule>();
builder.RegisterModule<MediaNamingAutofacModule>();
builder.RegisterGeneric(typeof(GenericPipelinePhases<>));
builder.RegisterTypes(
typeof(TagSyncPipeline),
typeof(CustomFormatSyncPipeline),
typeof(QualityProfileSyncPipeline),
typeof(QualitySizeSyncPipeline),
typeof(ReleaseProfileSyncPipeline),
typeof(MediaNamingSyncPipeline))
typeof(GenericSyncPipeline<MediaNamingPipelineContext>))
.As<ISyncPipeline>()
.OrderByRegistration();
}

@ -0,0 +1,11 @@
namespace Recyclarr.Cli.Pipelines.Generic;
public class GenericPipelinePhases<TContext>
{
public required IConfigPipelinePhase<TContext> ConfigPhase { get; init; }
public required ILogPipelinePhase<TContext> LogPhase { get; init; }
public required IApiFetchPipelinePhase<TContext> ApiFetchPhase { get; init; }
public required ITransactionPipelinePhase<TContext> TransactionPhase { get; init; }
public required IPreviewPipelinePhase<TContext> PreviewPhase { get; init; }
public required IApiPersistencePipelinePhase<TContext> ApiPersistencePhase { get; init; }
}

@ -0,0 +1,31 @@
using Recyclarr.Cli.Console.Settings;
using Recyclarr.Config.Models;
namespace Recyclarr.Cli.Pipelines.Generic;
public class GenericSyncPipeline<TContext>(GenericPipelinePhases<TContext> phases) : ISyncPipeline
where TContext : new()
{
public async Task Execute(ISyncSettings settings, IServiceConfiguration config)
{
var context = new TContext();
await phases.ConfigPhase.Execute(context, config);
if (phases.LogPhase.LogConfigPhaseAndExitIfNeeded(context))
{
return;
}
await phases.ApiFetchPhase.Execute(context, config);
phases.TransactionPhase.Execute(context);
if (settings.Preview)
{
phases.PreviewPhase.Execute(context);
return;
}
await phases.ApiPersistencePhase.Execute(context, config);
phases.LogPhase.LogPersistenceResults(context);
}
}

@ -0,0 +1,8 @@
using Recyclarr.Config.Models;
namespace Recyclarr.Cli.Pipelines.Generic;
public interface IApiFetchPipelinePhase<in TContext>
{
Task Execute(TContext context, IServiceConfiguration config);
}

@ -0,0 +1,8 @@
using Recyclarr.Config.Models;
namespace Recyclarr.Cli.Pipelines.Generic;
public interface IApiPersistencePipelinePhase<in TContext>
{
Task Execute(TContext context, IServiceConfiguration config);
}

@ -0,0 +1,8 @@
using Recyclarr.Config.Models;
namespace Recyclarr.Cli.Pipelines.Generic;
public interface IConfigPipelinePhase<in TContext>
{
Task Execute(TContext context, IServiceConfiguration config);
}

@ -0,0 +1,7 @@
namespace Recyclarr.Cli.Pipelines.Generic;
public interface ILogPipelinePhase<in TContext>
{
bool LogConfigPhaseAndExitIfNeeded(TContext context);
void LogPersistenceResults(TContext context);
}

@ -0,0 +1,6 @@
namespace Recyclarr.Cli.Pipelines.Generic;
public interface IPreviewPipelinePhase<in TContext>
{
void Execute(TContext context);
}

@ -0,0 +1,6 @@
namespace Recyclarr.Cli.Pipelines.Generic;
public interface ITransactionPipelinePhase<in TContext>
{
void Execute(TContext context);
}

@ -1,5 +1,4 @@
using Autofac;
using Autofac.Extras.AggregateService;
using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases.Config;
using Recyclarr.Common;
@ -18,12 +17,13 @@ public class MediaNamingAutofacModule : Module
builder.RegisterType<SonarrMediaNamingConfigPhase>()
.Keyed<IServiceBasedMediaNamingConfigPhase>(SupportedServices.Sonarr);
builder.RegisterAggregateService<IMediaNamingPipelinePhases>();
builder.RegisterType<MediaNamingConfigPhase>();
builder.RegisterType<MediaNamingApiFetchPhase>();
builder.RegisterType<MediaNamingTransactionPhase>();
builder.RegisterType<MediaNamingPreviewPhase>();
builder.RegisterType<MediaNamingApiPersistencePhase>();
builder.RegisterType<MediaNamingPhaseLogger>();
builder.RegisterTypes(
typeof(MediaNamingConfigPhase),
typeof(MediaNamingApiFetchPhase),
typeof(MediaNamingTransactionPhase),
typeof(MediaNamingPreviewPhase),
typeof(MediaNamingApiPersistencePhase),
typeof(MediaNamingLogPhase))
.AsImplementedInterfaces();
}
}

@ -0,0 +1,11 @@
using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
using Recyclarr.ServarrApi.MediaNaming;
namespace Recyclarr.Cli.Pipelines.MediaNaming;
public class MediaNamingPipelineContext
{
public ProcessedNamingConfig ConfigOutput { get; set; } = default!;
public MediaNamingDto ApiFetchOutput { get; set; } = default!;
public MediaNamingDto TransactionOutput { get; set; } = default!;
}

@ -1,40 +0,0 @@
using Recyclarr.Cli.Console.Settings;
using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
using Recyclarr.Config.Models;
namespace Recyclarr.Cli.Pipelines.MediaNaming;
public interface IMediaNamingPipelinePhases
{
MediaNamingConfigPhase ConfigPhase { get; }
MediaNamingPhaseLogger Logger { get; }
MediaNamingApiFetchPhase ApiFetchPhase { get; }
MediaNamingTransactionPhase TransactionPhase { get; }
MediaNamingPreviewPhase PreviewPhase { get; }
MediaNamingApiPersistencePhase ApiPersistencePhase { get; }
}
public class MediaNamingSyncPipeline(IMediaNamingPipelinePhases phases) : ISyncPipeline
{
public async Task Execute(ISyncSettings settings, IServiceConfiguration config)
{
var processedNaming = await phases.ConfigPhase.Execute(config);
if (phases.Logger.LogConfigPhaseAndExitIfNeeded(processedNaming))
{
return;
}
var serviceData = await phases.ApiFetchPhase.Execute(config);
var transactions = phases.TransactionPhase.Execute(serviceData, processedNaming);
if (settings.Preview)
{
phases.PreviewPhase.Execute(transactions);
return;
}
await phases.ApiPersistencePhase.Execute(config, transactions);
phases.Logger.LogPersistenceResults(serviceData, transactions);
}
}

@ -5,15 +5,9 @@ using Recyclarr.TrashGuide.MediaNaming;
namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases.Config;
public class SonarrMediaNamingConfigPhase : ServiceBasedMediaNamingConfigPhase<SonarrConfiguration>
public class SonarrMediaNamingConfigPhase(ISonarrCapabilityFetcher sonarrCapabilities)
: ServiceBasedMediaNamingConfigPhase<SonarrConfiguration>
{
private readonly ISonarrCapabilityFetcher _sonarrCapabilities;
public SonarrMediaNamingConfigPhase(ISonarrCapabilityFetcher sonarrCapabilities)
{
_sonarrCapabilities = sonarrCapabilities;
}
protected override async Task<MediaNamingDto> ProcessNaming(
SonarrConfiguration config,
IMediaNamingGuideService guide,
@ -21,7 +15,7 @@ public class SonarrMediaNamingConfigPhase : ServiceBasedMediaNamingConfigPhase<S
{
var guideData = guide.GetSonarrNamingData();
var configData = config.MediaNaming;
var capabilities = await _sonarrCapabilities.GetCapabilities(config);
var capabilities = await sonarrCapabilities.GetCapabilities(config);
var keySuffix = capabilities.SupportsCustomFormats ? ":4" : ":3";
return new SonarrMediaNamingDto

@ -1,12 +1,13 @@
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.MediaNaming;
namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
public class MediaNamingApiFetchPhase(IMediaNamingApiService api)
public class MediaNamingApiFetchPhase(IMediaNamingApiService api) : IApiFetchPipelinePhase<MediaNamingPipelineContext>
{
public async Task<MediaNamingDto> Execute(IServiceConfiguration config)
public async Task Execute(MediaNamingPipelineContext context, IServiceConfiguration config)
{
return await api.GetNaming(config);
context.ApiFetchOutput = await api.GetNaming(config);
}
}

@ -1,12 +1,14 @@
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Config.Models;
using Recyclarr.ServarrApi.MediaNaming;
namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
public class MediaNamingApiPersistencePhase(IMediaNamingApiService api)
: IApiPersistencePipelinePhase<MediaNamingPipelineContext>
{
public async Task Execute(IServiceConfiguration config, MediaNamingDto serviceDto)
public async Task Execute(MediaNamingPipelineContext context, IServiceConfiguration config)
{
await api.UpdateNaming(config, serviceDto);
await api.UpdateNaming(config, context.TransactionOutput);
}
}

@ -1,4 +1,5 @@
using Autofac.Features.Indexed;
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases.Config;
using Recyclarr.Common;
using Recyclarr.Config.Models;
@ -18,13 +19,14 @@ public record ProcessedNamingConfig
public class MediaNamingConfigPhase(
IMediaNamingGuideService guide,
IIndex<SupportedServices, IServiceBasedMediaNamingConfigPhase> configPhaseStrategyFactory)
: IConfigPipelinePhase<MediaNamingPipelineContext>
{
public async Task<ProcessedNamingConfig> Execute(IServiceConfiguration config)
public async Task Execute(MediaNamingPipelineContext context, IServiceConfiguration config)
{
var lookup = new NamingFormatLookup();
var strategy = configPhaseStrategyFactory[config.ServiceType];
var dto = await strategy.ProcessNaming(config, guide, lookup);
return new ProcessedNamingConfig {Dto = dto, InvalidNaming = lookup.Errors};
context.ConfigOutput = new ProcessedNamingConfig {Dto = dto, InvalidNaming = lookup.Errors};
}
}

@ -1,12 +1,15 @@
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.ServarrApi.MediaNaming;
namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
public class MediaNamingPhaseLogger(ILogger log)
public class MediaNamingLogPhase(ILogger log) : ILogPipelinePhase<MediaNamingPipelineContext>
{
// Returning 'true' means to exit. 'false' means to proceed.
public bool LogConfigPhaseAndExitIfNeeded(ProcessedNamingConfig config)
public bool LogConfigPhaseAndExitIfNeeded(MediaNamingPipelineContext context)
{
var config = context.ConfigOutput;
if (config.InvalidNaming.Count != 0)
{
foreach (var (topic, invalidValue) in config.InvalidNaming)
@ -33,12 +36,12 @@ public class MediaNamingPhaseLogger(ILogger log)
return false;
}
public void LogPersistenceResults(MediaNamingDto oldDto, MediaNamingDto newDto)
public void LogPersistenceResults(MediaNamingPipelineContext context)
{
var differences = oldDto switch
var differences = context.ApiFetchOutput switch
{
RadarrMediaNamingDto x => x.GetDifferences(newDto),
SonarrMediaNamingDto x => x.GetDifferences(newDto),
RadarrMediaNamingDto x => x.GetDifferences(context.TransactionOutput),
SonarrMediaNamingDto x => x.GetDifferences(context.TransactionOutput),
_ => throw new ArgumentException("Unsupported configuration type in LogPersistenceResults method")
};

@ -1,19 +1,20 @@
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.ServarrApi.MediaNaming;
using Spectre.Console;
namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
public class MediaNamingPreviewPhase(IAnsiConsole console)
public class MediaNamingPreviewPhase(IAnsiConsole console) : IPreviewPipelinePhase<MediaNamingPipelineContext>
{
private Table? _table;
public void Execute(MediaNamingDto serviceDto)
public void Execute(MediaNamingPipelineContext context)
{
_table = new Table()
.Title("Media Naming [red](Preview)[/]")
.AddColumns("[b]Field[/]", "[b]Value[/]");
switch (serviceDto)
switch (context.TransactionOutput)
{
case RadarrMediaNamingDto dto:
PreviewRadarr(dto);

@ -1,17 +1,16 @@
using System.Diagnostics.CodeAnalysis;
using Recyclarr.Cli.Pipelines.Generic;
using Recyclarr.ServarrApi.MediaNaming;
namespace Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
public class MediaNamingTransactionPhase
public class MediaNamingTransactionPhase : ITransactionPipelinePhase<MediaNamingPipelineContext>
{
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public MediaNamingDto Execute(MediaNamingDto serviceData, ProcessedNamingConfig config)
public void Execute(MediaNamingPipelineContext context)
{
return serviceData switch
context.TransactionOutput = context.ApiFetchOutput switch
{
RadarrMediaNamingDto dto => UpdateRadarrDto(dto, config),
SonarrMediaNamingDto dto => UpdateSonarrDto(dto, config),
RadarrMediaNamingDto dto => UpdateRadarrDto(dto, context.ConfigOutput),
SonarrMediaNamingDto dto => UpdateSonarrDto(dto, context.ConfigOutput),
_ => throw new ArgumentException("Config type not supported in media naming transation phase")
};
}

@ -1,4 +1,5 @@
using Autofac.Core.Registration;
using Recyclarr.Cli.Pipelines.MediaNaming;
using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
using Recyclarr.Common;
using Recyclarr.Config.Models;
@ -18,7 +19,7 @@ internal class MediaNamingConfigPhaseIntegrationTest : CliIntegrationFixture
public async Task Throw_on_unknown_config_type()
{
var sut = Resolve<MediaNamingConfigPhase>();
var act = () => sut.Execute(new UnsupportedConfigType {InstanceName = ""});
var act = () => sut.Execute(new MediaNamingPipelineContext(), new UnsupportedConfigType {InstanceName = ""});
await act.Should().ThrowAsync<ComponentNotRegisteredException>();
}
}

@ -1,103 +1,115 @@
using System.Diagnostics.CodeAnalysis;
using Recyclarr.Cli.Pipelines.MediaNaming;
using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
using Recyclarr.ServarrApi.MediaNaming;
namespace Recyclarr.Cli.Tests.Pipelines.MediaNaming;
[TestFixture]
[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification =
"Do not care about disposal in a testing context")]
public class MediaNamingTransactionPhaseRadarrTest
{
[Test] [AutoMockData]
[Test, AutoMockData]
public void Radarr_left_null(
MediaNamingTransactionPhase sut)
{
var left = new RadarrMediaNamingDto();
var right = new ProcessedNamingConfig
var context = new MediaNamingPipelineContext
{
Dto = new RadarrMediaNamingDto
ApiFetchOutput = new RadarrMediaNamingDto(),
ConfigOutput = new ProcessedNamingConfig
{
RenameMovies = true,
StandardMovieFormat = "file_format",
MovieFolderFormat = "folder_format"
Dto = new RadarrMediaNamingDto
{
RenameMovies = true,
StandardMovieFormat = "file_format",
MovieFolderFormat = "folder_format"
}
}
};
var result = sut.Execute(left, right);
sut.Execute(context);
result.Should().BeEquivalentTo(right.Dto, o => o.RespectingRuntimeTypes());
context.TransactionOutput.Should().BeEquivalentTo(context.ConfigOutput.Dto, o => o.RespectingRuntimeTypes());
}
[Test] [AutoMockData]
[Test, AutoMockData]
public void Radarr_right_null(
MediaNamingTransactionPhase sut)
{
var left = new RadarrMediaNamingDto
var context = new MediaNamingPipelineContext
{
RenameMovies = true,
StandardMovieFormat = "file_format",
MovieFolderFormat = "folder_format"
};
var right = new ProcessedNamingConfig
{
Dto = new RadarrMediaNamingDto()
ApiFetchOutput = new RadarrMediaNamingDto
{
RenameMovies = true,
StandardMovieFormat = "file_format",
MovieFolderFormat = "folder_format"
},
ConfigOutput = new ProcessedNamingConfig
{
Dto = new RadarrMediaNamingDto()
}
};
var result = sut.Execute(left, right);
sut.Execute(context);
result.Should().BeEquivalentTo(left, o => o.RespectingRuntimeTypes());
context.TransactionOutput.Should().BeEquivalentTo(context.ApiFetchOutput, o => o.RespectingRuntimeTypes());
}
[Test] [AutoMockData]
[Test, AutoMockData]
public void Radarr_right_and_left_with_rename(
MediaNamingTransactionPhase sut)
{
var left = new RadarrMediaNamingDto
var context = new MediaNamingPipelineContext
{
RenameMovies = false,
StandardMovieFormat = "file_format",
MovieFolderFormat = "folder_format"
};
var right = new ProcessedNamingConfig
{
Dto = new RadarrMediaNamingDto
ApiFetchOutput = new RadarrMediaNamingDto
{
RenameMovies = true,
StandardMovieFormat = "file_format2",
MovieFolderFormat = "folder_format2"
RenameMovies = false,
StandardMovieFormat = "file_format",
MovieFolderFormat = "folder_format"
},
ConfigOutput = new ProcessedNamingConfig
{
Dto = new RadarrMediaNamingDto
{
RenameMovies = true,
StandardMovieFormat = "file_format2",
MovieFolderFormat = "folder_format2"
}
}
};
var result = sut.Execute(left, right);
sut.Execute(context);
result.Should().BeEquivalentTo(right.Dto, o => o.RespectingRuntimeTypes());
context.TransactionOutput.Should().BeEquivalentTo(context.ConfigOutput.Dto, o => o.RespectingRuntimeTypes());
}
[Test] [AutoMockData]
[Test, AutoMockData]
public void Radarr_right_and_left_without_rename(
MediaNamingTransactionPhase sut)
{
var left = new RadarrMediaNamingDto
{
RenameMovies = true,
StandardMovieFormat = "file_format",
MovieFolderFormat = "folder_format"
};
var right = new ProcessedNamingConfig
var context = new MediaNamingPipelineContext
{
Dto = new RadarrMediaNamingDto
ApiFetchOutput = new RadarrMediaNamingDto
{
RenameMovies = false,
StandardMovieFormat = "file_format2",
MovieFolderFormat = "folder_format2"
RenameMovies = true,
StandardMovieFormat = "file_format",
MovieFolderFormat = "folder_format"
},
ConfigOutput = new ProcessedNamingConfig
{
Dto = new RadarrMediaNamingDto
{
RenameMovies = false,
StandardMovieFormat = "file_format2",
MovieFolderFormat = "folder_format2"
}
}
};
var result = sut.Execute(left, right);
sut.Execute(context);
result.Should().BeEquivalentTo(new RadarrMediaNamingDto
context.TransactionOutput.Should().BeEquivalentTo(new RadarrMediaNamingDto
{
RenameMovies = false,
StandardMovieFormat = "file_format2",

@ -1,121 +1,132 @@
using System.Diagnostics.CodeAnalysis;
using Recyclarr.Cli.Pipelines.MediaNaming;
using Recyclarr.Cli.Pipelines.MediaNaming.PipelinePhases;
using Recyclarr.ServarrApi.MediaNaming;
namespace Recyclarr.Cli.Tests.Pipelines.MediaNaming;
[TestFixture]
[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
public class MediaNamingTransactionPhaseSonarrTest
{
[Test] [AutoMockData]
[Test, AutoMockData]
public void Sonarr_left_null(
MediaNamingTransactionPhase sut)
{
var left = new SonarrMediaNamingDto();
var right = new ProcessedNamingConfig
var context = new MediaNamingPipelineContext
{
Dto = new SonarrMediaNamingDto
ApiFetchOutput = new SonarrMediaNamingDto(),
ConfigOutput = new ProcessedNamingConfig
{
RenameEpisodes = true,
SeasonFolderFormat = "season_default",
SeriesFolderFormat = "series_plex",
StandardEpisodeFormat = "episodes_standard_default_3",
DailyEpisodeFormat = "episodes_daily_default_3",
AnimeEpisodeFormat = "episodes_anime_default_3"
Dto = new SonarrMediaNamingDto
{
RenameEpisodes = true,
SeasonFolderFormat = "season_default",
SeriesFolderFormat = "series_plex",
StandardEpisodeFormat = "episodes_standard_default_3",
DailyEpisodeFormat = "episodes_daily_default_3",
AnimeEpisodeFormat = "episodes_anime_default_3"
}
}
};
var result = sut.Execute(left, right);
sut.Execute(context);
result.Should().BeEquivalentTo(right.Dto, o => o.RespectingRuntimeTypes());
context.TransactionOutput.Should().BeEquivalentTo(context.ConfigOutput.Dto, o => o.RespectingRuntimeTypes());
}
[Test] [AutoMockData]
[Test, AutoMockData]
public void Sonarr_right_null(
MediaNamingTransactionPhase sut)
{
var left = new SonarrMediaNamingDto
{
RenameEpisodes = true,
SeasonFolderFormat = "season_default",
SeriesFolderFormat = "series_plex",
StandardEpisodeFormat = "episodes_standard_default_3",
DailyEpisodeFormat = "episodes_daily_default_3",
AnimeEpisodeFormat = "episodes_anime_default_3"
};
var right = new ProcessedNamingConfig
var context = new MediaNamingPipelineContext
{
Dto = new SonarrMediaNamingDto()
ApiFetchOutput = new SonarrMediaNamingDto
{
RenameEpisodes = true,
SeasonFolderFormat = "season_default",
SeriesFolderFormat = "series_plex",
StandardEpisodeFormat = "episodes_standard_default_3",
DailyEpisodeFormat = "episodes_daily_default_3",
AnimeEpisodeFormat = "episodes_anime_default_3"
},
ConfigOutput = new ProcessedNamingConfig
{
Dto = new SonarrMediaNamingDto()
}
};
var result = sut.Execute(left, right);
sut.Execute(context);
result.Should().BeEquivalentTo(left, o => o.RespectingRuntimeTypes());
context.TransactionOutput.Should().BeEquivalentTo(context.ApiFetchOutput, o => o.RespectingRuntimeTypes());
}
[Test] [AutoMockData]
[Test, AutoMockData]
public void Sonarr_right_and_left_with_rename(
MediaNamingTransactionPhase sut)
{
var left = new SonarrMediaNamingDto
{
RenameEpisodes = false,
SeasonFolderFormat = "season_default",
SeriesFolderFormat = "series_plex",
StandardEpisodeFormat = "episodes_standard_default",
DailyEpisodeFormat = "episodes_daily_default",
AnimeEpisodeFormat = "episodes_anime_default"
};
var right = new ProcessedNamingConfig
var context = new MediaNamingPipelineContext
{
Dto = new SonarrMediaNamingDto
ApiFetchOutput = new SonarrMediaNamingDto
{
RenameEpisodes = true,
SeasonFolderFormat = "season_default2",
SeriesFolderFormat = "series_plex2",
StandardEpisodeFormat = "episodes_standard_default2",
DailyEpisodeFormat = "episodes_daily_default2",
AnimeEpisodeFormat = "episodes_anime_default2"
RenameEpisodes = false,
SeasonFolderFormat = "season_default",
SeriesFolderFormat = "series_plex",
StandardEpisodeFormat = "episodes_standard_default",
DailyEpisodeFormat = "episodes_daily_default",
AnimeEpisodeFormat = "episodes_anime_default"
},
ConfigOutput = new ProcessedNamingConfig
{
Dto = new SonarrMediaNamingDto
{
RenameEpisodes = true,
SeasonFolderFormat = "season_default2",
SeriesFolderFormat = "series_plex2",
StandardEpisodeFormat = "episodes_standard_default2",
DailyEpisodeFormat = "episodes_daily_default2",
AnimeEpisodeFormat = "episodes_anime_default2"
}
}
};
var result = sut.Execute(left, right);
sut.Execute(context);
result.Should().BeEquivalentTo(right.Dto, o => o.RespectingRuntimeTypes());
context.TransactionOutput.Should().BeEquivalentTo(context.ConfigOutput.Dto, o => o.RespectingRuntimeTypes());
}
[Test] [AutoMockData]
[Test, AutoMockData]
public void Sonarr_right_and_left_without_rename(
MediaNamingTransactionPhase sut)
{
var left = new SonarrMediaNamingDto
{
RenameEpisodes = true,
SeasonFolderFormat = "season_default",
SeriesFolderFormat = "series_plex",
StandardEpisodeFormat = "episodes_standard_default",
DailyEpisodeFormat = "episodes_daily_default",
AnimeEpisodeFormat = "episodes_anime_default"
};
var right = new ProcessedNamingConfig
var context = new MediaNamingPipelineContext
{
Dto = new SonarrMediaNamingDto
ApiFetchOutput = new SonarrMediaNamingDto
{
RenameEpisodes = false,
SeasonFolderFormat = "season_default2",
SeriesFolderFormat = "series_plex2",
StandardEpisodeFormat = "episodes_standard_default2",
DailyEpisodeFormat = "episodes_daily_default2",
AnimeEpisodeFormat = "episodes_anime_default2"
RenameEpisodes = true,
SeasonFolderFormat = "season_default",
SeriesFolderFormat = "series_plex",
StandardEpisodeFormat = "episodes_standard_default",
DailyEpisodeFormat = "episodes_daily_default",
AnimeEpisodeFormat = "episodes_anime_default"
},
ConfigOutput = new ProcessedNamingConfig
{
Dto = new SonarrMediaNamingDto
{
RenameEpisodes = false,
SeasonFolderFormat = "season_default2",
SeriesFolderFormat = "series_plex2",
StandardEpisodeFormat = "episodes_standard_default2",
DailyEpisodeFormat = "episodes_daily_default2",
AnimeEpisodeFormat = "episodes_anime_default2"
}
}
};
var result = sut.Execute(left, right);
sut.Execute(context);
result.Should().BeEquivalentTo(new SonarrMediaNamingDto
context.TransactionOutput.Should().BeEquivalentTo(new SonarrMediaNamingDto
{
RenameEpisodes = false,
SeasonFolderFormat = "season_default2",

Loading…
Cancel
Save