diff --git a/AudioNodes/InputNodes/AudioFile.cs b/AudioNodes/InputNodes/AudioFile.cs
index 46dccc67..3cbf854e 100644
--- a/AudioNodes/InputNodes/AudioFile.cs
+++ b/AudioNodes/InputNodes/AudioFile.cs
@@ -43,6 +43,13 @@ namespace FileFlows.AudioNodes
if (string.IsNullOrEmpty(ffmpegExe))
return -1;
+ var fileInfo = new FileInfo(args.WorkingFile);
+ if (fileInfo.Exists)
+ {
+ args.Variables.Add("ORIGINAL_CREATE_UTC", fileInfo.CreationTimeUtc);
+ args.Variables.Add("ORIGINAL_LAST_WRITE_UTC", fileInfo.LastWriteTimeUtc);
+ }
+
try
{
if (ReadAudioFileInfo(args, ffmpegExe, args.WorkingFile))
diff --git a/BasicNodes/BasicNodes.en.json b/BasicNodes/BasicNodes.en.json
index 3f9a7563..351f3ca0 100644
--- a/BasicNodes/BasicNodes.en.json
+++ b/BasicNodes/BasicNodes.en.json
@@ -36,7 +36,9 @@
"AdditionalFiles": "Additional Files",
"AdditionalFiles-Help": "Additional files to copy from the directory to the new directory.\nEach value can contain a combination of valid literal path and wildcard (* and ?) characters, but it doesn''t support regular expressions.",
"AdditionalFilesFromOriginal": "Original Directory",
- "AdditionalFilesFromOriginal-Help": "If the additional files should be copied from the working directory or from the original directory. Turn on for original directory."
+ "AdditionalFilesFromOriginal-Help": "If the additional files should be copied from the working directory or from the original directory. Turn on for original directory.",
+ "PreserverOriginalDates": "Preserve Dates",
+ "PreserverOriginalDates-Help": "If the original last write time of the original input file should be preserved."
}
},
"DeleteSourceDirectory": {
@@ -192,7 +194,9 @@
"AdditionalFiles": "Additional Files",
"AdditionalFiles-Help": "Additional files to move from the directory to the new directory.\nEach value can contain a combination of valid literal path and wildcard (* and ?) characters, but it doesn''t support regular expressions.",
"AdditionalFilesFromOriginal": "Original Directory",
- "AdditionalFilesFromOriginal-Help": "If the additional files should be moved from the working directory or from the original directory. Turn on for original directory."
+ "AdditionalFilesFromOriginal-Help": "If the additional files should be moved from the working directory or from the original directory. Turn on for original directory.",
+ "PreserverOriginalDates": "Preserve Dates",
+ "PreserverOriginalDates-Help": "If the original last write time of the original input file should be preserved."
}
},
"OlderThan": {
@@ -243,6 +247,10 @@
"Description": "Replaces the original file with the working file.\n\nIf the extension is different on the working file, the original file will be deleted and the working file will be moved to the original with the new extension.\nE.g. from File.avi to File.mkv",
"Outputs": {
"1": "Original file replaced"
+ },
+ "Fields": {
+ "PreserverOriginalDates": "Preserve Dates",
+ "PreserverOriginalDates-Help": "If the original last write time of the original input file should be preserved."
}
},
"Renamer": {
diff --git a/BasicNodes/File/CopyFile.cs b/BasicNodes/File/CopyFile.cs
index b1242152..f3dc171e 100644
--- a/BasicNodes/File/CopyFile.cs
+++ b/BasicNodes/File/CopyFile.cs
@@ -61,6 +61,12 @@ namespace FileFlows.BasicNodes.File
[Boolean(5)]
public bool AdditionalFilesFromOriginal { get; set; }
+ ///
+ /// Gets or sets if the original files creation and last write time dates should be preserved
+ ///
+ [Boolean(6)]
+ public bool PreserverOriginalDates { get; set; }
+
private bool Canceled;
public override Task Cancel()
@@ -123,6 +129,15 @@ namespace FileFlows.BasicNodes.File
if (!copied)
return -1;
+
+ if(PreserverOriginalDates && args.Variables.TryGetValue("ORIGINAL_CREATE_UTC", out object oCreateTimeUtc) &&
+ args.Variables.TryGetValue("ORIGINAL_LAST_WRITE_UTC", out object oLastWriteUtc) &&
+ oCreateTimeUtc is DateTime dtCreateTimeUtc && oLastWriteUtc is DateTime dtLastWriteUtc)
+ {
+ Helpers.FileHelper.SetCreationTime(dest, dtCreateTimeUtc);
+ Helpers.FileHelper.SetLastWriteTime(dest, dtLastWriteUtc);
+ }
+
var srcDir = AdditionalFilesFromOriginal ? new FileInfo(args.MapPath(args.FileName)).DirectoryName : new FileInfo(args.MapPath(args.WorkingFile)).DirectoryName;
if (AdditionalFiles?.Any() == true)
diff --git a/BasicNodes/File/InputFile.cs b/BasicNodes/File/InputFile.cs
index b2eadb5b..966e3a04 100644
--- a/BasicNodes/File/InputFile.cs
+++ b/BasicNodes/File/InputFile.cs
@@ -1,34 +1,52 @@
-namespace FileFlows.BasicNodes.File
+using FileFlows.Plugin;
+
+namespace FileFlows.BasicNodes.File;
+
+///
+/// Basic Input file, the default input node
+///
+public class InputFile : Node
{
- using System.ComponentModel;
- using FileFlows.Plugin;
- using FileFlows.Plugin.Attributes;
+ ///
+ /// Gets the number of outputs
+ ///
+ public override int Outputs => 1;
+ ///
+ /// Gets the element type
+ ///
+ public override FlowElementType Type => FlowElementType.Input;
+ ///
+ /// Gets the icon
+ ///
+ public override string Icon => "far fa-file";
+ ///
+ /// Gets the help URL
+ ///
+ public override string HelpUrl => "https://docs.fileflows.com/plugins/basic-nodes/input-file";
- public class InputFile : Node
+ ///
+ /// Executes the node
+ ///
+ /// the node parameters
+ /// the output to call next
+ public override int Execute(NodeParameters args)
{
- public override int Outputs => 1;
- public override FlowElementType Type => FlowElementType.Input;
- public override string Icon => "far fa-file";
-
- public override string HelpUrl => "https://docs.fileflows.com/plugins/basic-nodes/input-file";
-
- public override int Execute(NodeParameters args)
+ try
{
- try
+ FileInfo fileInfo = new FileInfo(args.WorkingFile);
+ if (fileInfo.Exists == false)
{
- FileInfo fileInfo = new FileInfo(args.WorkingFile);
- if (fileInfo.Exists == false)
- {
- args.Logger?.ELog("File not found: " + args.WorkingFile);
- return -1;
- }
- return 1;
- }
- catch (Exception ex)
- {
- args.Logger?.ELog("Failed in InputFile: " + ex.Message + Environment.NewLine + ex.StackTrace);
+ args.Logger?.ELog("File not found: " + args.WorkingFile);
return -1;
}
+ args.Variables.Add("ORIGINAL_CREATE_UTC", fileInfo.CreationTimeUtc);
+ args.Variables.Add("ORIGINAL_LAST_WRITE_UTC", fileInfo.LastWriteTimeUtc);
+ return 1;
+ }
+ catch (Exception ex)
+ {
+ args.Logger?.ELog("Failed in InputFile: " + ex.Message + Environment.NewLine + ex.StackTrace);
+ return -1;
}
}
}
\ No newline at end of file
diff --git a/BasicNodes/File/MoveFile.cs b/BasicNodes/File/MoveFile.cs
index b8d10c77..2ea3a60c 100644
--- a/BasicNodes/File/MoveFile.cs
+++ b/BasicNodes/File/MoveFile.cs
@@ -1,143 +1,194 @@
-namespace FileFlows.BasicNodes.File
+using System.ComponentModel.DataAnnotations;
+using FileFlows.Plugin;
+using FileFlows.Plugin.Attributes;
+
+namespace FileFlows.BasicNodes.File;
+
+///
+/// Moves a file to a new location
+///
+public class MoveFile : Node
{
- using System.ComponentModel;
- using System.ComponentModel.DataAnnotations;
- using System.Threading.Tasks;
- using FileFlows.Plugin;
- using FileFlows.Plugin.Attributes;
+ ///
+ /// Gets the number of inputs
+ ///
+ public override int Inputs => 1;
+ ///
+ /// Gets the number of outputs
+ ///
+ public override int Outputs => 2;
+ ///
+ /// Gets the type of flow element
+ ///
+ public override FlowElementType Type => FlowElementType.Process;
+ ///
+ /// Gets the icon for the flow element
+ ///
+ public override string Icon => "fas fa-file-export";
+ ///
+ /// Gets the help URL
+ ///
+ public override string HelpUrl => "https://docs.fileflows.com/plugins/basic-nodes/move-file";
- public class MoveFile : Node
+ ///
+ /// Gets or sets the destination path
+ ///
+ [Required]
+ [Folder(1)]
+ public string DestinationPath { get; set; }
+
+ ///
+ /// Gets or sets the destination file
+ ///
+ [TextVariable(2)]
+ public string DestinationFile{ get; set; }
+
+ ///
+ /// Gets or sets if the folder should be moved
+ ///
+ [Boolean(3)]
+ public bool MoveFolder { get; set; }
+
+ ///
+ /// Gets or sets if the original should be deleted
+ ///
+ [Boolean(4)]
+ public bool DeleteOriginal { get; set; }
+
+ ///
+ /// Gets or sets additional files that should also be moved
+ ///
+ [StringArray(5)]
+ public string[] AdditionalFiles { get; set; }
+
+ ///
+ /// Gets or sets original files from the original file location that should also be moved
+ ///
+ [Boolean(6)]
+ public bool AdditionalFilesFromOriginal { get; set; }
+
+ ///
+ /// Gets or sets if the original files creation and last write time dates should be preserved
+ ///
+ [Boolean(7)]
+ public bool PreserverOriginalDates { get; set; }
+
+ ///
+ /// Executes the node
+ ///
+ /// the node parameters
+ /// the output to call next
+ public override int Execute(NodeParameters args)
{
- public override int Inputs => 1;
- public override int Outputs => 2;
- public override FlowElementType Type => FlowElementType.Process;
- public override string Icon => "fas fa-file-export";
+ var dest = GetDesitnationPath(args, DestinationPath, DestinationFile, MoveFolder);
+ if (dest == null)
+ return -1;
- public override string HelpUrl => "https://docs.fileflows.com/plugins/basic-nodes/move-file";
+ string destDir = new FileInfo(dest).DirectoryName;
- [Required]
- [Folder(1)]
- public string DestinationPath { get; set; }
+ args.CreateDirectoryIfNotExists(destDir ?? String.Empty);
- [TextVariable(2)]
- public string DestinationFile{ get; set; }
+ var srcDir = AdditionalFilesFromOriginal ? new FileInfo(args.FileName).DirectoryName : new FileInfo(args.WorkingFile).DirectoryName;
- [Boolean(3)]
- public bool MoveFolder { get; set; }
-
- [Boolean(4)]
- public bool DeleteOriginal { get; set; }
-
- [StringArray(5)]
- public string[] AdditionalFiles { get; set; }
-
- [Boolean(6)]
- public bool AdditionalFilesFromOriginal { get; set; }
-
- public override int Execute(NodeParameters args)
+ if (args.MoveFile(dest) == false)
+ return -1;
+
+ if(PreserverOriginalDates && args.Variables.TryGetValue("ORIGINAL_CREATE_UTC", out object oCreateTimeUtc) &&
+ args.Variables.TryGetValue("ORIGINAL_LAST_WRITE_UTC", out object oLastWriteUtc) &&
+ oCreateTimeUtc is DateTime dtCreateTimeUtc && oLastWriteUtc is DateTime dtLastWriteUtc)
{
- var dest = GetDesitnationPath(args, DestinationPath, DestinationFile, MoveFolder);
- if (dest == null)
- return -1;
+ Helpers.FileHelper.SetLastWriteTime(dest, dtLastWriteUtc);
+ Helpers.FileHelper.SetCreationTime(dest, dtCreateTimeUtc);
+ }
- string destDir = new FileInfo(dest).DirectoryName;
-
- args.CreateDirectoryIfNotExists(destDir ?? String.Empty);
-
- var srcDir = AdditionalFilesFromOriginal ? new FileInfo(args.FileName).DirectoryName : new FileInfo(args.WorkingFile).DirectoryName;
-
- if (args.MoveFile(dest) == false)
- return -1;
-
- if(AdditionalFiles?.Any() == true)
+ if(AdditionalFiles?.Any() == true)
+ {
+ try
{
- try
+ var diSrc = new DirectoryInfo(srcDir);
+ foreach (var additional in AdditionalFiles)
{
- var diSrc = new DirectoryInfo(srcDir);
- foreach (var additional in AdditionalFiles)
+ foreach(var addFile in diSrc.GetFiles(additional))
{
- foreach(var addFile in diSrc.GetFiles(additional))
+ try
{
- try
- {
- string addFileDest = Path.Combine(destDir, addFile.Name);
- System.IO.File.Move(addFile.FullName, addFileDest, true);
- args.Logger?.ILog("Moved file: \"" + addFile.FullName + "\" to \"" + addFileDest + "\"");
- }
- catch(Exception ex)
- {
- args.Logger?.ILog("Failed moving file: \"" + addFile.FullName + "\": " + ex.Message);
- }
+ string addFileDest = Path.Combine(destDir, addFile.Name);
+ System.IO.File.Move(addFile.FullName, addFileDest, true);
+ args.Logger?.ILog("Moved file: \"" + addFile.FullName + "\" to \"" + addFileDest + "\"");
+ }
+ catch(Exception ex)
+ {
+ args.Logger?.ILog("Failed moving file: \"" + addFile.FullName + "\": " + ex.Message);
}
}
}
- catch(Exception ex)
- {
- args.Logger.WLog("Error moving additional files: " + ex.Message);
- }
}
-
-
- if (DeleteOriginal && args.FileName != args.WorkingFile)
+ catch(Exception ex)
{
- args.Logger?.ILog("Deleting original file: " + args.FileName);
- try
- {
- System.IO.File.Delete(args.FileName);
- }
- catch(Exception ex)
- {
- args.Logger?.WLog("Failed to delete original file: " + ex.Message);
- return 2;
- }
+ args.Logger.WLog("Error moving additional files: " + ex.Message);
}
- return 1;
}
- internal static string GetDesitnationPath(NodeParameters args, string destinationPath, string destinationFile = null, bool moveFolder = false)
+
+ if (DeleteOriginal && args.FileName != args.WorkingFile)
{
- string dest = args.ReplaceVariables(destinationPath, true);
- dest = dest.Replace("\\", Path.DirectorySeparatorChar.ToString());
- dest = dest.Replace("/", Path.DirectorySeparatorChar.ToString());
- if (string.IsNullOrEmpty(dest))
+ args.Logger?.ILog("Deleting original file: " + args.FileName);
+ try
{
- args.Logger?.ELog("No destination specified");
- args.Result = NodeResult.Failure;
- return null;
+ System.IO.File.Delete(args.FileName);
}
- args.Result = NodeResult.Failure;
-
- if (moveFolder)
- dest = Path.Combine(dest, args.RelativeFile);
- else
- dest = Path.Combine(dest, new FileInfo(args.FileName).Name);
-
- var fiDest = new FileInfo(dest);
- var fiWorking = new FileInfo(args.WorkingFile);
- if (string.IsNullOrEmpty(fiDest.Extension) == false && fiDest.Extension != fiWorking.Extension)
+ catch(Exception ex)
{
- dest = dest.Substring(0, dest.LastIndexOf(".")) + fiWorking.Extension;
+ args.Logger?.WLog("Failed to delete original file: " + ex.Message);
+ return 2;
}
-
- if (string.IsNullOrEmpty(destinationFile) == false)
- {
- // FF-154 - changed file.Name and file.Orig.Filename to be the full short filename including the extension
- destinationFile = destinationFile.Replace("{file.Orig.FileName}{file.Orig.Extension}", "{file.Orig.FileName}");
- destinationFile = destinationFile.Replace("{file.Name}{file.Extension}", "{file.Name}");
- destinationFile = destinationFile.Replace("{file.Name}{ext}", "{file.Name}");
- string destFile = args.ReplaceVariables(destinationFile);
- dest = Path.Combine(new FileInfo(dest).DirectoryName!, destFile);
- }
-
- fiDest = new FileInfo(dest);
- var fiWorkingFile = new FileInfo(args.WorkingFile);
- if (fiDest.Extension != fiWorkingFile.Extension)
- {
- dest = dest.Replace(fiDest.Extension, fiWorkingFile.Extension);
- fiDest = new FileInfo(dest);
- }
-
- return dest;
}
+ return 1;
+ }
+
+ internal static string GetDesitnationPath(NodeParameters args, string destinationPath, string destinationFile = null, bool moveFolder = false)
+ {
+ string dest = args.ReplaceVariables(destinationPath, true);
+ dest = dest.Replace("\\", Path.DirectorySeparatorChar.ToString());
+ dest = dest.Replace("/", Path.DirectorySeparatorChar.ToString());
+ if (string.IsNullOrEmpty(dest))
+ {
+ args.Logger?.ELog("No destination specified");
+ args.Result = NodeResult.Failure;
+ return null;
+ }
+ args.Result = NodeResult.Failure;
+
+ if (moveFolder)
+ dest = Path.Combine(dest, args.RelativeFile);
+ else
+ dest = Path.Combine(dest, new FileInfo(args.FileName).Name);
+
+ var fiDest = new FileInfo(dest);
+ var fiWorking = new FileInfo(args.WorkingFile);
+ if (string.IsNullOrEmpty(fiDest.Extension) == false && fiDest.Extension != fiWorking.Extension)
+ {
+ dest = dest.Substring(0, dest.LastIndexOf(".")) + fiWorking.Extension;
+ }
+
+ if (string.IsNullOrEmpty(destinationFile) == false)
+ {
+ // FF-154 - changed file.Name and file.Orig.Filename to be the full short filename including the extension
+ destinationFile = destinationFile.Replace("{file.Orig.FileName}{file.Orig.Extension}", "{file.Orig.FileName}");
+ destinationFile = destinationFile.Replace("{file.Name}{file.Extension}", "{file.Name}");
+ destinationFile = destinationFile.Replace("{file.Name}{ext}", "{file.Name}");
+ string destFile = args.ReplaceVariables(destinationFile);
+ dest = Path.Combine(new FileInfo(dest).DirectoryName!, destFile);
+ }
+
+ fiDest = new FileInfo(dest);
+ var fiWorkingFile = new FileInfo(args.WorkingFile);
+ if (fiDest.Extension != fiWorkingFile.Extension)
+ {
+ dest = dest.Replace(fiDest.Extension, fiWorkingFile.Extension);
+ fiDest = new FileInfo(dest);
+ }
+
+ return dest;
}
}
\ No newline at end of file
diff --git a/BasicNodes/File/ReplaceOriginal.cs b/BasicNodes/File/ReplaceOriginal.cs
index 8a8ede26..c4f2daea 100644
--- a/BasicNodes/File/ReplaceOriginal.cs
+++ b/BasicNodes/File/ReplaceOriginal.cs
@@ -1,19 +1,47 @@
using FileFlows.Plugin;
+using FileFlows.Plugin.Attributes;
namespace FileFlows.BasicNodes.File;
+///
+/// Replaces the original file
+///
public class ReplaceOriginal : Node
{
+ ///
+ /// Gets the number of inputs
+ ///
public override int Inputs => 1;
+ ///
+ /// Gets the number of outputs
+ ///
public override int Outputs => 1;
+ ///
+ /// Gets the icon to use
+ ///
public override string Icon => "fas fa-file";
-
+ ///
+ /// Gets the help URL
+ ///
public override string HelpUrl => "https://docs.fileflows.com/plugins/basic-nodes/replace-original";
-
- public string _Pattern = string.Empty;
-
+ ///
+ /// Gets the type of flow element
+ ///
public override FlowElementType Type => FlowElementType.Process;
+ public string _Pattern = string.Empty;
+
+ ///
+ /// Gets or sets if the original files creation and last write time dates should be preserved
+ ///
+ [Boolean(1)]
+ public bool PreserverOriginalDates { get; set; }
+
+ ///
+ /// Executes the node
+ ///
+ /// the node parameters
+ /// the next output to execute
public override int Execute(NodeParameters args)
{
if (args.FileName == args.WorkingFile)
@@ -31,6 +59,14 @@ public class ReplaceOriginal : Node
args.Logger?.ELog("Failed to move file to: "+ args.FileName);
return -1;
}
+
+ if(PreserverOriginalDates && args.Variables.TryGetValue("ORIGINAL_CREATE_UTC", out object oCreateTimeUtc) &&
+ args.Variables.TryGetValue("ORIGINAL_LAST_WRITE_UTC", out object oLastWriteUtc) &&
+ oCreateTimeUtc is DateTime dtCreateTimeUtc && oLastWriteUtc is DateTime dtLastWriteUtc)
+ {
+ Helpers.FileHelper.SetCreationTime(args.FileName, dtCreateTimeUtc);
+ Helpers.FileHelper.SetLastWriteTime(args.FileName, dtLastWriteUtc);
+ }
}
else
{
@@ -53,6 +89,15 @@ public class ReplaceOriginal : Node
return -1;
}
}
+
+
+ if(PreserverOriginalDates && args.Variables.TryGetValue("ORIGINAL_CREATE_UTC", out object oCreateTimeUtc) &&
+ args.Variables.TryGetValue("ORIGINAL_LAST_WRITE_UTC", out object oLastWriteUtc) &&
+ oCreateTimeUtc is DateTime dtCreateTimeUtc && oLastWriteUtc is DateTime dtLastWriteUtc)
+ {
+ Helpers.FileHelper.SetCreationTime(dest, dtCreateTimeUtc);
+ Helpers.FileHelper.SetLastWriteTime(dest, dtLastWriteUtc);
+ }
}
return 1;
diff --git a/BasicNodes/Helpers/FileHelper.cs b/BasicNodes/Helpers/FileHelper.cs
new file mode 100644
index 00000000..06167ea6
--- /dev/null
+++ b/BasicNodes/Helpers/FileHelper.cs
@@ -0,0 +1,26 @@
+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/ImageNodes/Images/ImageFile.cs b/ImageNodes/Images/ImageFile.cs
index cb79d0dc..af25ba70 100644
--- a/ImageNodes/Images/ImageFile.cs
+++ b/ImageNodes/Images/ImageFile.cs
@@ -28,6 +28,13 @@ public class ImageFile : ImageBaseNode
{
try
{
+ var fileInfo = new FileInfo(args.WorkingFile);
+ if (fileInfo.Exists)
+ {
+ args.Variables.Add("ORIGINAL_CREATE_UTC", fileInfo.CreationTimeUtc);
+ args.Variables.Add("ORIGINAL_LAST_WRITE_UTC", fileInfo.LastWriteTimeUtc);
+ }
+
UpdateImageInfo(args, this.Variables);
if(string.IsNullOrEmpty(base.CurrentFormat) == false)
args.RecordStatistic("IMAGE_FORMAT", base.CurrentFormat);
diff --git a/VideoNodes/InputNodes/VideoFile.cs b/VideoNodes/InputNodes/VideoFile.cs
index bd63556e..c1d669e1 100644
--- a/VideoNodes/InputNodes/VideoFile.cs
+++ b/VideoNodes/InputNodes/VideoFile.cs
@@ -67,6 +67,13 @@ public class VideoFile : VideoNode
args.Logger.ILog($"Video stream '{vs.Codec}' '{vs.Index}'");
}
+ var fileInfo = new FileInfo(args.WorkingFile);
+ if (fileInfo.Exists)
+ {
+ args.Variables.Add("ORIGINAL_CREATE_UTC", fileInfo.CreationTimeUtc);
+ args.Variables.Add("ORIGINAL_LAST_WRITE_UTC", fileInfo.LastWriteTimeUtc);
+ }
+
foreach (var stream in videoInfo.VideoStreams)
{
if (string.IsNullOrEmpty(stream.Codec) == false)