diff --git a/BasicNodes/File/CopyFile.cs b/BasicNodes/File/CopyFile.cs
index 3d07a25e..78dbb0b4 100644
--- a/BasicNodes/File/CopyFile.cs
+++ b/BasicNodes/File/CopyFile.cs
@@ -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);
}
}
diff --git a/BasicNodes/File/MoveFile.cs b/BasicNodes/File/MoveFile.cs
index fe68a91b..37d3a6b2 100644
--- a/BasicNodes/File/MoveFile.cs
+++ b/BasicNodes/File/MoveFile.cs
@@ -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
diff --git a/BasicNodes/Helpers/FileHelper.cs b/BasicNodes/Helpers/FileHelper.cs
deleted file mode 100644
index 06167ea6..00000000
--- a/BasicNodes/Helpers/FileHelper.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Diagnostics;
-
-namespace FileFlows.BasicNodes.Helpers;
-
-///
-/// File Helper
-///
-public static class FileHelper
-{
- ///
- /// Sets the last write time of a file
- ///
- /// the file path
- /// the UTC to set
- public static void SetLastWriteTime(string filePath, DateTime utcDate)
- => System.IO.File.SetLastWriteTimeUtc(filePath, utcDate);
-
- ///
- /// Sets the last write time of a file
- ///
- /// the file path
- /// the UTC to set
- public static void SetCreationTime(string filePath, DateTime utcDate)
- => System.IO.File.SetCreationTimeUtc(filePath, utcDate);
-
-}
\ No newline at end of file
diff --git a/BasicNodes/Helpers/FolderHelper.cs b/BasicNodes/Helpers/FolderHelper.cs
new file mode 100644
index 00000000..09cff5e4
--- /dev/null
+++ b/BasicNodes/Helpers/FolderHelper.cs
@@ -0,0 +1,74 @@
+using System.Text.RegularExpressions;
+using FileFlows.Plugin;
+using FileFlows.Plugin.Helpers;
+using FileFlows.Plugin.Services;
+
+namespace FileFlows.BasicNodes.Helpers;
+
+///
+/// Folder Helper
+///
+public static class FolderHelper
+{
+ ///
+ /// Gets additional files matching the criteria
+ ///
+ /// the logger to use
+ /// the file serverice to use
+ /// the function to replace variables in the patterns
+ /// the shortname of the source file to match against
+ /// the directory to search in
+ /// the patterns of the additional files
+ /// a list of additional files found
+ public static List GetAdditionalFiles(ILogger logger, IFileService fileService,
+ Func replaceVariables, string shortNameLookup, string directory, string[] patterns)
+ {
+ List 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;
+ }
+
+}
\ No newline at end of file
diff --git a/BasicNodes/Tests/AdditionalFilesTests.cs b/BasicNodes/Tests/AdditionalFilesTests.cs
new file mode 100644
index 00000000..2c519afe
--- /dev/null
+++ b/BasicNodes/Tests/AdditionalFilesTests.cs
@@ -0,0 +1,49 @@
+#if(DEBUG)
+
+using System.IO;
+using FileFlows.BasicNodes.Helpers;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace BasicNodes.Tests;
+
+///
+/// Tests fot he additional files method
+///
+[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")));
+
+ }
+
+ ///
+ /// Creates a file
+ ///
+ /// the directory to create the file in
+ /// the name of the file
+ private void CreateFile(string directory, string name)
+ {
+ System.IO.File.WriteAllText(Path.Combine(directory, name), "");
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/BasicNodes/Tests/FileSizeCompareTests.cs b/BasicNodes/Tests/FileSizeCompareTests.cs
index d6a74ffb..8ec357c2 100644
--- a/BasicNodes/Tests/FileSizeCompareTests.cs
+++ b/BasicNodes/Tests/FileSizeCompareTests.cs
@@ -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);
diff --git a/BasicNodes/Tests/TestLogger.cs b/BasicNodes/Tests/TestLogger.cs
deleted file mode 100644
index c5bbe7d3..00000000
--- a/BasicNodes/Tests/TestLogger.cs
+++ /dev/null
@@ -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 Messages = new List();
-
- 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
\ No newline at end of file
diff --git a/BasicNodes/Tests/_LocalFileService.cs b/BasicNodes/Tests/_LocalFileService.cs
index 09398e33..9f310169 100644
--- a/BasicNodes/Tests/_LocalFileService.cs
+++ b/BasicNodes/Tests/_LocalFileService.cs
@@ -143,7 +143,7 @@ public class LocalFileService : IFileService
return Result.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.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.Fail("Cannot access protected path: " + path);
try
{
- if (!File.Exists(path))
+ if (!System.IO.File.Exists(path))
return Result.Fail("File not found.");
- File.SetCreationTimeUtc(path, date);
+ System.IO.File.SetCreationTimeUtc(path, date);
return Result.Success(true);
}
catch (Exception ex)
@@ -394,10 +394,10 @@ public class LocalFileService : IFileService
return Result.Fail("Cannot access protected path: " + path);
try
{
- if (!File.Exists(path))
+ if (!System.IO.File.Exists(path))
return Result.Fail("File not found.");
- File.SetLastWriteTimeUtc(path, date);
+ System.IO.File.SetLastWriteTimeUtc(path, date);
return Result.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;
diff --git a/BasicNodes/Tests/_TestBase.cs b/BasicNodes/Tests/_TestBase.cs
new file mode 100644
index 00000000..2f749502
--- /dev/null
+++ b/BasicNodes/Tests/_TestBase.cs
@@ -0,0 +1,66 @@
+#if(DEBUG)
+
+using System.Runtime.InteropServices;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.IO;
+using FileFlows.BasicNodes;
+
+namespace BasicNodes.Tests;
+
+///
+/// Base class for the tests
+///
+[TestClass]
+public abstract class TestBase
+{
+ ///
+ /// The test context instance
+ ///
+ private TestContext testContextInstance;
+
+ internal TestLogger Logger = new();
+
+ ///
+ /// Gets or sets the test context
+ ///
+ 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
\ No newline at end of file
diff --git a/BasicNodes/Tests/_TestLogger.cs b/BasicNodes/Tests/_TestLogger.cs
new file mode 100644
index 00000000..ac3079f0
--- /dev/null
+++ b/BasicNodes/Tests/_TestLogger.cs
@@ -0,0 +1,92 @@
+#if (DEBUG)
+
+namespace BasicNodes.Tests;
+
+using FileFlows.Plugin;
+
+///
+/// A logger for tests that stores the logs in memory
+///
+public class TestLogger : ILogger
+{
+ private readonly List Messages = new();
+
+ ///
+ /// Writes an information log message
+ ///
+ /// the log parameters
+ public void ILog(params object[] args)
+ => Log(LogType.Info, args);
+
+ ///
+ /// Writes an debug log message
+ ///
+ /// the log parameters
+ public void DLog(params object[] args)
+ => Log(LogType.Debug, args);
+
+ ///
+ /// Writes an warning log message
+ ///
+ /// the log parameters
+ public void WLog(params object[] args)
+ => Log(LogType.Warning, args);
+
+ ///
+ /// Writes an error log message
+ ///
+ /// the log parameters
+ public void ELog(params object[] args)
+ => Log(LogType.Error, args);
+
+ ///
+ /// Gets the tail of the log
+ ///
+ /// the number of messages to get
+ public string GetTail(int length = 50)
+ {
+ if (Messages.Count <= length)
+ return string.Join(Environment.NewLine, Messages);
+ return string.Join(Environment.NewLine, Messages.TakeLast(50));
+ }
+
+ ///
+ /// Logs a message
+ ///
+ /// the type of log to record
+ /// the arguments of the message
+ 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);
+ }
+
+ ///
+ /// Gets or sets an optional writer
+ ///
+ public Action Writer { get; set; }
+
+ ///
+ /// Returns the entire log as a string
+ ///
+ /// the entire log
+ public override string ToString()
+ => string.Join(Environment.NewLine, Messages);
+
+ ///
+ /// Checks if the log contains the text
+ ///
+ /// the text to check for
+ /// true if it contains it, otherwise false
+ public bool Contains(string text)
+ => Messages.Any(x => x.Contains(text));
+}
+
+#endif
\ No newline at end of file
diff --git a/FileFlows.Plugin.dll b/FileFlows.Plugin.dll
index 4552ba51..ed56948a 100644
Binary files a/FileFlows.Plugin.dll and b/FileFlows.Plugin.dll differ
diff --git a/FileFlows.Plugin.pdb b/FileFlows.Plugin.pdb
index ebe22bba..1b517708 100644
Binary files a/FileFlows.Plugin.pdb and b/FileFlows.Plugin.pdb differ