FF-1615: Move and Copy now use common method to get additional files

This commit is contained in:
John Andrews
2024-06-17 08:41:13 +12:00
parent dc70c91ea9
commit 4ee50eeb20
12 changed files with 328 additions and 158 deletions

View File

@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
using FileFlows.BasicNodes.Helpers;
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
using FileFlows.Plugin.Helpers;
@@ -151,35 +152,29 @@ public class CopyFile : Node
if (AdditionalFiles?.Any() == true)
{
try
string shortNameLookup = FileHelper.GetShortFileName(AdditionalFilesFromOriginal ? args.FileName : inputFile);
if (shortNameLookup.LastIndexOf(".", StringComparison.InvariantCulture) > 0)
shortNameLookup = shortNameLookup[..shortNameLookup.LastIndexOf(".", StringComparison.Ordinal)];
args.Logger?.ILog("Additional Files: " + string.Join(", ", AdditionalFiles));
var addFiles = FolderHelper.GetAdditionalFiles(args.Logger, args.FileService, args.ReplaceVariables,
shortNameLookup, srcDir, AdditionalFiles);
foreach (var addFile in addFiles)
{
foreach (var additionalOrig in AdditionalFiles)
try
{
string additional = args.ReplaceVariables(additionalOrig, stripMissing: true);
foreach(var addFile in args.FileService.GetFiles(srcDir, additional).ValueOrDefault ?? new string[] {})
{
try
{
string shortName = FileHelper.GetShortFileName(addFile);
string addFileDest = destDir + args.FileService.PathSeparator + shortName;
args.FileService.FileCopy(addFile, addFileDest, true);
//args.CopyFile(addFile, addFileDest, updateWorkingFile: false);
string shortName = FileHelper.GetShortFileName(addFile);
//FileHelper.ChangeOwner(args.Logger, addFileDest, file: true);
string addFileDest = destDir + args.FileService.PathSeparator + shortName;
args.FileService.FileCopy(addFile, addFileDest, true);
args.Logger?.ILog("Copied file: \"" + addFile + "\" to \"" + addFileDest + "\"");
}
catch (Exception ex)
{
args.Logger?.ILog("Failed copying file: \"" + addFile + "\": " + ex.Message);
}
}
args.Logger?.ILog("Copied file: \"" + addFile + "\" to \"" + addFileDest + "\"");
}
catch (Exception ex)
{
args.Logger?.ILog("Failed copying file: \"" + addFile + "\": " + ex.Message);
}
}
catch (Exception ex)
{
args.Logger.WLog("Error copying additional files: " + ex.Message);
}
}

View File

@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using FileFlows.BasicNodes.Helpers;
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
using FileFlows.Plugin.Helpers;
@@ -156,49 +157,24 @@ public class MoveFile : Node
if(AdditionalFiles?.Any() == true)
{
args.Logger?.ILog("Additional Files: " + string.Join(", ", AdditionalFiles));
var addFiles = FolderHelper.GetAdditionalFiles(args.Logger, args.FileService, args.ReplaceVariables,
shortNameLookup, srcDir, AdditionalFiles);
try
string destDir = FileHelper.GetDirectory(dest);
foreach (var addFile in addFiles)
{
string destDir = FileHelper.GetDirectory(dest);
args.FileService.DirectoryCreate(destDir);
args.Logger?.ILog("Looking for additional files in directory: " + srcDir);
foreach (var additionalOrig in AdditionalFiles)
try
{
string additional = args.ReplaceVariables(additionalOrig, stripMissing: true);
if (Regex.IsMatch(additionalOrig, @"\.[a-z0-9A-Z]+$") == false)
additional = "*" + additional; // add the leading start for the search
args.Logger?.ILog("Looking for additional files: " + additional);
var srcDirFiles = args.FileService.GetFiles(srcDir, additional).ValueOrDefault ?? new string[] { };
foreach(var addFile in srcDirFiles)
{
try
{
if (Regex.IsMatch(additional, @"\*\.[a-z0-9A-Z]+$"))
{
// make sure the file starts with same name
var addFileName = FileHelper.GetShortFileName(addFile);
if (addFileName.ToLowerInvariant().StartsWith(shortNameLookup.ToLowerInvariant()) ==
false)
continue;
}
args.Logger?.ILog("Additional files: " + addFile);
args.Logger?.ILog("Additional files: " + addFile);
string addFileDest = destDir + args.FileService.PathSeparator + FileHelper.GetShortFileName(addFile);
args.FileService.FileMove(addFile, addFileDest, true);
args.Logger?.ILog("Moved file: \"" + addFile + "\" to \"" + addFileDest + "\"");
}
catch(Exception ex)
{
args.Logger?.ILog("Failed moving file: \"" + addFile + "\": " + ex.Message);
}
}
string addFileDest = FileHelper.Combine(destDir, FileHelper.GetShortFileName(addFile));
args.FileService.FileMove(addFile, addFileDest, true);
args.Logger?.ILog("Moved file: \"" + addFile + "\" to \"" + addFileDest + "\"");
}
catch(Exception ex)
{
args.Logger?.ILog("Failed moving file: \"" + addFile + "\": " + ex.Message);
}
}
catch(Exception ex)
{
args.Logger.WLog("Error moving additional files: " + ex.Message);
}
}
else

View File

@@ -1,26 +0,0 @@
using System.Diagnostics;
namespace FileFlows.BasicNodes.Helpers;
/// <summary>
/// File Helper
/// </summary>
public static class FileHelper
{
/// <summary>
/// Sets the last write time of a file
/// </summary>
/// <param name="filePath">the file path</param>
/// <param name="utcDate">the UTC to set</param>
public static void SetLastWriteTime(string filePath, DateTime utcDate)
=> System.IO.File.SetLastWriteTimeUtc(filePath, utcDate);
/// <summary>
/// Sets the last write time of a file
/// </summary>
/// <param name="filePath">the file path</param>
/// <param name="utcDate">the UTC to set</param>
public static void SetCreationTime(string filePath, DateTime utcDate)
=> System.IO.File.SetCreationTimeUtc(filePath, utcDate);
}

View File

@@ -0,0 +1,74 @@
using System.Text.RegularExpressions;
using FileFlows.Plugin;
using FileFlows.Plugin.Helpers;
using FileFlows.Plugin.Services;
namespace FileFlows.BasicNodes.Helpers;
/// <summary>
/// Folder Helper
/// </summary>
public static class FolderHelper
{
/// <summary>
/// Gets additional files matching the criteria
/// </summary>
/// <param name="logger">the logger to use</param>
/// <param name="fileService">the file serverice to use</param>
/// <param name="replaceVariables">the function to replace variables in the patterns</param>
/// <param name="shortNameLookup">the shortname of the source file to match against</param>
/// <param name="directory">the directory to search in</param>
/// <param name="patterns">the patterns of the additional files</param>
/// <returns>a list of additional files found</returns>
public static List<string> GetAdditionalFiles(ILogger logger, IFileService fileService,
Func<string, bool, bool, string> replaceVariables, string shortNameLookup, string directory, string[] patterns)
{
List<string> results = new();
if (string.IsNullOrWhiteSpace(directory) || patterns == null || patterns.Length < 1)
return results;
logger?.ILog("Additional Files: " + string.Join(", ", patterns));
try
{
logger?.ILog("Looking for additional files in directory: " + directory);
foreach (var additionalOrig in patterns)
{
string additional = replaceVariables(additionalOrig, true, true);
if (Regex.IsMatch(additionalOrig, @"^\.[a-z0-9A-Z]+$"))
additional = "*" + additional; // add the leading start for the search
logger?.ILog("Looking for additional files: " + additional);
var srcDirFiles = fileService.GetFiles(directory, additional).ValueOrDefault ?? new string[] { };
foreach (var addFile in srcDirFiles)
{
try
{
if (Regex.IsMatch(additional, @"\*\.[a-z0-9A-Z]+$"))
{
// make sure the file starts with same name
var addFileName = FileHelper.GetShortFileName(addFile);
if (addFileName.ToLowerInvariant().StartsWith(shortNameLookup.ToLowerInvariant()) ==
false)
continue;
}
logger?.ILog("Additional files: " + addFile);
results.Add(addFile);
}
catch (Exception ex)
{
logger?.ILog("Failed moving file: \"" + addFile + "\": " + ex.Message);
}
}
}
}
catch (Exception ex)
{
logger.WLog("Error moving additional files: " + ex.Message);
}
return results;
}
}

View File

@@ -0,0 +1,49 @@
#if(DEBUG)
using System.IO;
using FileFlows.BasicNodes.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BasicNodes.Tests;
/// <summary>
/// Tests fot he additional files method
/// </summary>
[TestClass]
public class AdditionalFilesTests: TestBase
{
[TestMethod]
public void Basic()
{
var fileService = new LocalFileService();
var dir = Path.Combine(TempPath, Guid.NewGuid().ToString());
Directory.CreateDirectory(dir);
CreateFile(dir, "my movie.mkv");
CreateFile(dir, "my movie.srt");
CreateFile(dir, "my movie.en.sub");
CreateFile(dir, "my movie.it.srt");
CreateFile(dir, "not my movie.en.sub");
CreateFile(dir, "not my movie.sub");
CreateFile(dir, "not my movie.srt");
var results = FolderHelper.GetAdditionalFiles(Logger, fileService,
(s, b, b2) => s,
"my movie", dir, [".srt", ".sub"]);
Assert.AreEqual(3, results.Count);
Assert.IsTrue(results.Contains(Path.Combine(dir, "my movie.srt")));
Assert.IsTrue(results.Contains(Path.Combine(dir, "my movie.en.sub")));
Assert.IsTrue(results.Contains(Path.Combine(dir, "my movie.it.srt")));
}
/// <summary>
/// Creates a file
/// </summary>
/// <param name="directory">the directory to create the file in</param>
/// <param name="name">the name of the file</param>
private void CreateFile(string directory, string name)
{
System.IO.File.WriteAllText(Path.Combine(directory, name), "");
}
}
#endif

View File

@@ -13,7 +13,7 @@ namespace BasicNodes.Tests
private string CreateFile(int size)
{
string tempFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
File.WriteAllText(tempFile, new string('a', size));
System.IO.File.WriteAllText(tempFile, new string('a', size));
return tempFile;
}
@@ -70,7 +70,7 @@ namespace BasicNodes.Tests
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null);
Assert.IsTrue(args.WorkingFileSize > 0);
File.Delete(tempFile);
System.IO.File.Delete(tempFile);
string wfFile = CreateFile(1);
args.SetWorkingFile(wfFile);
@@ -86,7 +86,7 @@ namespace BasicNodes.Tests
string tempFile = CreateFile(2);
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null);
File.Delete(tempFile);
System.IO.File.Delete(tempFile);
string wfFile = CreateFile(20);
args.SetWorkingFile(wfFile);
@@ -102,7 +102,7 @@ namespace BasicNodes.Tests
string tempFile = CreateFile(2);
var logger = new TestLogger();
var args = new FileFlows.Plugin.NodeParameters(tempFile, logger, false, string.Empty, null);
File.Delete(tempFile);
System.IO.File.Delete(tempFile);
string wfFile = CreateFile(2);
args.SetWorkingFile(wfFile);

View File

@@ -1,56 +0,0 @@
using System.Diagnostics.CodeAnalysis;
#if (DEBUG)
namespace BasicNodes.Tests
{
using FileFlows.Plugin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
internal class TestLogger : ILogger
{
private List<string> Messages = new List<string>();
public void DLog(params object[] args) => Log("DBUG", args);
public void ELog(params object[] args) => Log("ERRR", args);
public void ILog(params object[] args) => Log("INFO", args);
public void WLog(params object[] args) => Log("WARN", args);
private void Log(string type, object[] args)
{
if (args == null || args.Length == 0)
return;
#pragma warning disable IL2026
string message = type + " -> " +
string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message);
}
public bool Contains(string message)
{
if (string.IsNullOrWhiteSpace(message))
return false;
string log = string.Join(Environment.NewLine, Messages);
return log.Contains(message);
}
public string GetTail(int length = 50) => "Not implemented";
public override string ToString()
=> string.Join(Environment.NewLine, this.Messages.ToArray());
}
}
#endif

View File

@@ -143,7 +143,7 @@ public class LocalFileService : IFileService
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
return File.Exists(path);
return System.IO.File.Exists(path);
}
catch (Exception)
{
@@ -309,7 +309,7 @@ public class LocalFileService : IFileService
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
File.AppendAllText(path, text);
System.IO.File.AppendAllText(path, text);
SetPermissions(path);
return true;
}
@@ -349,11 +349,11 @@ public class LocalFileService : IFileService
try
{
if (File.Exists(path))
File.SetLastWriteTimeUtc(path, DateTime.UtcNow);
if (System.IO.File.Exists(path))
System.IO.File.SetLastWriteTimeUtc(path, DateTime.UtcNow);
else
{
File.Create(path);
System.IO.File.Create(path);
SetPermissions(path);
}
@@ -376,10 +376,10 @@ public class LocalFileService : IFileService
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
if (!File.Exists(path))
if (!System.IO.File.Exists(path))
return Result<bool>.Fail("File not found.");
File.SetCreationTimeUtc(path, date);
System.IO.File.SetCreationTimeUtc(path, date);
return Result<bool>.Success(true);
}
catch (Exception ex)
@@ -394,10 +394,10 @@ public class LocalFileService : IFileService
return Result<bool>.Fail("Cannot access protected path: " + path);
try
{
if (!File.Exists(path))
if (!System.IO.File.Exists(path))
return Result<bool>.Fail("File not found.");
File.SetLastWriteTimeUtc(path, date);
System.IO.File.SetLastWriteTimeUtc(path, date);
return Result<bool>.Success(true);
}
catch (Exception ex)
@@ -449,7 +449,7 @@ public class LocalFileService : IFileService
permissions = 777;
if ((File.Exists(path) == false && Directory.Exists(path) == false))
if ((System.IO.File.Exists(path) == false && Directory.Exists(path) == false))
{
logMethod("SetPermissions: File doesnt existing, skipping");
return;

View File

@@ -0,0 +1,66 @@
#if(DEBUG)
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using FileFlows.BasicNodes;
namespace BasicNodes.Tests;
/// <summary>
/// Base class for the tests
/// </summary>
[TestClass]
public abstract class TestBase
{
/// <summary>
/// The test context instance
/// </summary>
private TestContext testContextInstance;
internal TestLogger Logger = new();
/// <summary>
/// Gets or sets the test context
/// </summary>
public TestContext TestContext
{
get => testContextInstance;
set => testContextInstance = value;
}
public string TestPath { get; private set; }
public string TempPath { get; private set; }
public readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public readonly bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
[TestInitialize]
public void TestInitialize()
{
Logger.Writer = (msg) => TestContext.WriteLine(msg);
this.TestPath = this.TestPath?.EmptyAsNull() ?? (IsLinux ? "~/src/ff-files/test-files/videos" : @"d:\videos\testfiles");
this.TempPath = this.TempPath?.EmptyAsNull() ?? (IsLinux ? "~/src/ff-files/temp" : @"d:\videos\temp");
this.TestPath = this.TestPath.Replace("~/", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/");
this.TempPath = this.TempPath.Replace("~/", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/");
if (Directory.Exists(this.TempPath) == false)
Directory.CreateDirectory(this.TempPath);
}
[TestCleanup]
public void CleanUp()
{
TestContext.WriteLine(Logger.ToString());
}
protected virtual void TestStarting()
{
}
}
#endif

View File

@@ -0,0 +1,92 @@
#if (DEBUG)
namespace BasicNodes.Tests;
using FileFlows.Plugin;
/// <summary>
/// A logger for tests that stores the logs in memory
/// </summary>
public class TestLogger : ILogger
{
private readonly List<string> Messages = new();
/// <summary>
/// Writes an information log message
/// </summary>
/// <param name="args">the log parameters</param>
public void ILog(params object[] args)
=> Log(LogType.Info, args);
/// <summary>
/// Writes an debug log message
/// </summary>
/// <param name="args">the log parameters</param>
public void DLog(params object[] args)
=> Log(LogType.Debug, args);
/// <summary>
/// Writes an warning log message
/// </summary>
/// <param name="args">the log parameters</param>
public void WLog(params object[] args)
=> Log(LogType.Warning, args);
/// <summary>
/// Writes an error log message
/// </summary>
/// <param name="args">the log parameters</param>
public void ELog(params object[] args)
=> Log(LogType.Error, args);
/// <summary>
/// Gets the tail of the log
/// </summary>
/// <param name="length">the number of messages to get</param>
public string GetTail(int length = 50)
{
if (Messages.Count <= length)
return string.Join(Environment.NewLine, Messages);
return string.Join(Environment.NewLine, Messages.TakeLast(50));
}
/// <summary>
/// Logs a message
/// </summary>
/// <param name="type">the type of log to record</param>
/// <param name="args">the arguments of the message</param>
private void Log(LogType type, params object[] args)
{
#pragma warning disable IL2026
string message = type + " -> " + string.Join(", ", args.Select(x =>
x == null ? "null" :
x.GetType().IsPrimitive ? x.ToString() :
x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Writer?.Invoke(message);
Messages.Add(message);
}
/// <summary>
/// Gets or sets an optional writer
/// </summary>
public Action<string> Writer { get; set; }
/// <summary>
/// Returns the entire log as a string
/// </summary>
/// <returns>the entire log</returns>
public override string ToString()
=> string.Join(Environment.NewLine, Messages);
/// <summary>
/// Checks if the log contains the text
/// </summary>
/// <param name="text">the text to check for</param>
/// <returns>true if it contains it, otherwise false</returns>
public bool Contains(string text)
=> Messages.Any(x => x.Contains(text));
}
#endif

Binary file not shown.

Binary file not shown.