Move image save logic from TaskExecutor to IOutputSink
This commit is contained in:
parent
a0f719b8df
commit
13552f1f96
13 changed files with 135 additions and 64 deletions
|
@ -31,7 +31,6 @@ namespace ConsoleInterface
|
|||
private static void RunOptions(ProgramOptions options)
|
||||
{
|
||||
SetupLogging(options.LogLevel);
|
||||
CreateDestinationDirectory(options.DestinationDirectory);
|
||||
var outputFormatter = SimpleOutputSink.Create(options.DestinationDirectory);
|
||||
var executor = TaskExecutor.Create(new TaskExecutorOptions
|
||||
{
|
||||
|
@ -44,15 +43,6 @@ namespace ConsoleInterface
|
|||
executor.ParallelCleanImages(filesRetriever.GetFilenamesFromPath(options.SourceDirectory));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the directory if it doesn't exist.
|
||||
/// </summary>
|
||||
/// <param name="destinationDirectory">The destination directory.</param>
|
||||
private static void CreateDestinationDirectory(string destinationDirectory)
|
||||
{
|
||||
FileSystemHelpers.CreateDestinationDirectory(destinationDirectory);
|
||||
}
|
||||
|
||||
public static void SetupLogging(string logLevel)
|
||||
{
|
||||
_loggerFactory = LoggerFactory.Create(b =>
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
using System;
|
||||
using Image.Files;
|
||||
using Xunit;
|
||||
|
||||
namespace ImageCore.Tests
|
||||
{
|
||||
public class TestSimpleOutputSink
|
||||
{
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "./", @".\.jpg")]
|
||||
[InlineData("", @".\", @".\.jpg")]
|
||||
[InlineData("", "asd", @".\asd.jpg")]
|
||||
[InlineData("dir", "asd", @"dir\asd.jpg")]
|
||||
public void TestGetOutputPath(string directory, string file, string expectedPath)
|
||||
{
|
||||
var outputPathFormatter = SimpleOutputSink.Create(directory);
|
||||
Assert.Equal(expectedPath, outputPathFormatter.GetOutputPath(file));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestGetOutputPathNull()
|
||||
{
|
||||
var outputPathFormatter = SimpleOutputSink.Create("directory");
|
||||
Assert.Throws<ArgumentException>(() => outputPathFormatter.GetOutputPath(""));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ namespace ImageCore.Tests
|
|||
var filePathsList = filePaths.ToList();
|
||||
var expectedFileNames = new List<string>
|
||||
{
|
||||
"IMG_0138.HEIC", "IMG_0140.HEIC",
|
||||
"IMG_0138.HEIC", "IMG_0138.jpg", "IMG_0140.HEIC",
|
||||
};
|
||||
|
||||
Assert.NotEmpty(filePathsList);
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace ImageCore.Tests
|
|||
public class TestMetadataRemover
|
||||
{
|
||||
[Fact]
|
||||
public void TestCleanImage()
|
||||
public void TestExifRemoverAndCompressorCleanImage()
|
||||
{
|
||||
// Setup
|
||||
var magicImageMock = new Mock<IMagickImage>();
|
||||
|
@ -23,5 +23,22 @@ namespace ImageCore.Tests
|
|||
magicImageMock.Verify( i => i.Write("path"));
|
||||
compressorMock.Verify( i => i.Compress("path"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestExifRemoverAndCompressorGetImagePath()
|
||||
{
|
||||
// Setup
|
||||
var magicImageMock = new Mock<IMagickImage>();
|
||||
magicImageMock.Setup(i => i.FileName).Returns("P4th");
|
||||
|
||||
var compressorMock = new Mock<ICompressor>();
|
||||
var metadataRemover = new ExifRemoverAndCompressor(magicImageMock.Object, compressorMock.Object);
|
||||
|
||||
// Test
|
||||
var result = metadataRemover.GetImagePath();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("P4th", result);
|
||||
}
|
||||
}
|
||||
}
|
68
ImageCore.Tests/TestSimpleOutputSink.cs
Normal file
68
ImageCore.Tests/TestSimpleOutputSink.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Image.Core;
|
||||
using Image.Files;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace ImageCore.Tests
|
||||
{
|
||||
public class TestSimpleOutputSink
|
||||
{
|
||||
private readonly string _testsProjectDirectory;
|
||||
|
||||
public TestSimpleOutputSink()
|
||||
{
|
||||
_testsProjectDirectory = Environment.GetEnvironmentVariable("IMAGE_CORE_TESTS");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "./", @".\.jpg")]
|
||||
[InlineData("", @".\", @".\.jpg")]
|
||||
[InlineData("", "asd", @".\asd.jpg")]
|
||||
[InlineData("dir", "asd", @"dir\asd.jpg")]
|
||||
public void TestGetOutputPath(string directory, string file, string expectedPath)
|
||||
{
|
||||
var sink = SimpleOutputSink.Create(directory);
|
||||
Assert.Equal(expectedPath, sink.GetOutputPath(file));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestGetOutputPathNull()
|
||||
{
|
||||
var sink = SimpleOutputSink.Create("directory");
|
||||
Assert.Throws<ArgumentException>(() => sink.GetOutputPath(""));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestSave()
|
||||
{
|
||||
// Setup
|
||||
var sink = SimpleOutputSink.Create("directory");
|
||||
var metadataRemoverMock = new Mock<IMetadataRemover>();
|
||||
metadataRemoverMock.Setup(i => i.GetImagePath()).Returns("alo.wtf");
|
||||
|
||||
// Test
|
||||
sink.Save(metadataRemoverMock.Object);
|
||||
|
||||
// Assert
|
||||
metadataRemoverMock.Verify(i => i.CleanImage("directory\\alo.jpg"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestSaveFileExists()
|
||||
{
|
||||
// Setup
|
||||
var sink = SimpleOutputSink.Create(Path.Join(_testsProjectDirectory, "test_pictures"));
|
||||
var metadataRemoverMock = new Mock<IMetadataRemover>();
|
||||
var sourceFileName = Path.Join(_testsProjectDirectory, "test_pictures\\IMG_0138.HEIC");
|
||||
metadataRemoverMock.Setup(i => i.GetImagePath()).Returns(sourceFileName);
|
||||
|
||||
// Test
|
||||
sink.Save(metadataRemoverMock.Object);
|
||||
|
||||
// Assert
|
||||
metadataRemoverMock.Verify(i => i.CleanImage(It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
}
|
||||
}
|
BIN
ImageCore.Tests/test_pictures/IMG_0138.jpg
Normal file
BIN
ImageCore.Tests/test_pictures/IMG_0138.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
|
@ -31,5 +31,11 @@ namespace Image.Core
|
|||
_magickImage.Write(newFilePath);
|
||||
_compressor.Compress(newFilePath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetImagePath()
|
||||
{
|
||||
return _magickImage.FileName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,9 +6,15 @@
|
|||
public interface IMetadataRemover
|
||||
{
|
||||
/// <summary>
|
||||
/// CleanImage cleans an image and saves it..
|
||||
/// Cleans an image and saves it under a new path.
|
||||
/// </summary>
|
||||
/// <param name="newFilePath">The file path to save the clean image.</param>
|
||||
void CleanImage(string newFilePath);
|
||||
|
||||
/// <summary>
|
||||
/// GetImagePath gets the current image path on the filesystem.
|
||||
/// </summary>
|
||||
/// <returns>A string representing the absolute path.</returns>
|
||||
string GetImagePath();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
namespace Image.Files
|
||||
using Image.Core;
|
||||
|
||||
namespace Image.Files
|
||||
{
|
||||
/// <summary>
|
||||
/// IOutputSink is an interface for generating saving the generated files..
|
||||
|
@ -6,10 +8,10 @@
|
|||
public interface IOutputSink
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates an absolute output path given the initial absolute file path.
|
||||
/// Saves the image.
|
||||
/// </summary>
|
||||
/// <param name="initialFilePath">The initial file path.</param>
|
||||
/// <returns>The formatted absolute output path.</returns>
|
||||
string GetOutputPath(string initialFilePath);
|
||||
/// <param name="metadataRemover">Metadata remover instance.</param>
|
||||
/// <returns>True if the image was saved successfully, false otherwise.</returns>
|
||||
bool Save(IMetadataRemover metadataRemover);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using System.IO;
|
||||
using Ardalis.GuardClauses;
|
||||
using Image.Core;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
|
@ -25,6 +26,10 @@ namespace Image.Files
|
|||
{
|
||||
outputDirectory = ".";
|
||||
}
|
||||
else
|
||||
{
|
||||
FileSystemHelpers.CreateDestinationDirectory(outputDirectory);
|
||||
}
|
||||
_outputDirectory = outputDirectory;
|
||||
}
|
||||
|
||||
|
@ -39,10 +44,23 @@ namespace Image.Files
|
|||
Guard.Against.NullOrEmpty(initialFilePath, nameof(initialFilePath));
|
||||
var fileName = Path.GetFileName(initialFilePath).Split('.')[0];
|
||||
var path = Path.Combine(_outputDirectory, $"{fileName}.jpg");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public bool Save(IMetadataRemover metadataRemover)
|
||||
{
|
||||
var newFilePath = GetOutputPath(metadataRemover.GetImagePath());
|
||||
var fileExists = FileSystemHelpers.CheckIfFileExists(newFilePath);
|
||||
if (fileExists)
|
||||
{
|
||||
Logger.LogWarning($"File {newFilePath} exists, skipping");
|
||||
return false;
|
||||
}
|
||||
// Save the image under the same name in the new directory.
|
||||
metadataRemover.CleanImage(newFilePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of OriginalFilenameFileOutputPathFormatter.
|
||||
/// </summary>
|
||||
|
|
|
@ -41,27 +41,19 @@ namespace Image.Tasks
|
|||
/// Cleans an image. Errors are silenced by default.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file path of the image to be cleaned.</param>
|
||||
/// <param name="newFilePath">The new file path of the cleaned image.</param>
|
||||
/// <param name="outputSink">The output sink for the image..</param>
|
||||
/// <returns>True of the image was cleaned, false otherwise.</returns>
|
||||
public bool CleanImage(string filePath, string newFilePath)
|
||||
public bool CleanImage(string filePath, IOutputSink outputSink)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fileExists = FileSystemHelpers.CheckIfFileExists(newFilePath);
|
||||
if (fileExists)
|
||||
{
|
||||
Logger.LogWarning($"File {newFilePath} exists, skipping");
|
||||
return false;
|
||||
}
|
||||
Logger.LogDebug($"Cleaning {filePath}, compression {_options.EnableCompression}, outputFormatter {nameof(_options.OutputSink)}.");
|
||||
|
||||
ICompressor compressor = NullCompressor.Instance;
|
||||
var imageMagick = new MagickImage(filePath);
|
||||
if (_options.EnableCompression) compressor = LosslessCompressor.Instance;
|
||||
|
||||
Logger.LogDebug(
|
||||
$"Cleaning {filePath}, compression {_options.EnableCompression}, outputFormatter {nameof(_options.OutputSink)}.");
|
||||
var imageMagick = new MagickImage(filePath);
|
||||
IMetadataRemover metadataRemover = new ExifRemoverAndCompressor(imageMagick, compressor);
|
||||
metadataRemover.CleanImage(newFilePath);
|
||||
return true;
|
||||
return outputSink.Save(metadataRemover);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -87,8 +79,7 @@ namespace Image.Tasks
|
|||
var tasks = new List<Task<bool>>();
|
||||
foreach (var fileName in filenamesArray)
|
||||
{
|
||||
var task = new Task<bool>(() =>
|
||||
CleanImage(fileName, _options.OutputSink.GetOutputPath(fileName)));
|
||||
var task = new Task<bool>(() => CleanImage(fileName, _options.OutputSink));
|
||||
tasks.Add(task);
|
||||
task.Start();
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Image.Tasks
|
|||
public IOutputSink OutputSink
|
||||
{
|
||||
get => _outputSink;
|
||||
set => _outputSink = value ?? throw new ArgumentException("Output formatter cannot be null!");
|
||||
set => _outputSink = value ?? throw new ArgumentException("OutputSink cannot be null!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<Assembly Path="C:\Users\nutiu\.nuget\packages\magick.net.core\8.6.1\lib\netstandard21\Magick.NET.Core.dll" />
|
||||
<Assembly Path="C:\Users\nutiu\.nuget\packages\microsoft.extensions.logging.abstractions\6.0.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll" />
|
||||
</AssemblyExplorer></s:String>
|
||||
<s:String x:Key="/Default/Environment/Highlighting/HighlightingSourceSnapshotLocation/@EntryValue">C:\Users\nutiu\AppData\Local\JetBrains\Rider2021.3\resharper-host\temp\Rider\vAny\CoverageData\_ImgMetadataRemover.61395890\Snapshot\snapshot.utdcvr</s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=761234b5_002D46ba_002D4312_002Dab60_002Dd15d5d83a61a/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="TestGetFilenamesFromPath" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="C:\Users\nutiu\RiderProjects\ImgMetadataRemover\ImageCore.Tests" Presentation="&lt;ImageCore.Tests&gt;" />
|
||||
</SessionState></s:String></wpf:ResourceDictionary>
|
Loading…
Reference in a new issue