added option to copy/move nodes to also move additional files

This commit is contained in:
John Andrews
2022-02-12 18:29:44 +13:00
parent 939541842a
commit ade1f4a540
28 changed files with 174 additions and 105 deletions

Binary file not shown.

View File

@@ -32,7 +32,11 @@
"DestinationFile": "Destination File",
"DestinationFile-Help": "The filename to copy the file to. If empty, the original filename will be used",
"CopyFolder": "Copy Folder",
"CopyFolder-Help": "If the relative library folder structure should be copied too"
"CopyFolder-Help": "If the relative library folder structure should be copied too",
"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."
}
},
"DeleteSourceDirectory": {
@@ -180,7 +184,11 @@
"MoveFolder": "Copy Folder",
"MoveFolder-Help": "If the relative library folder structure should be copied too",
"DeleteOriginal": "Delete Original",
"DeleteOriginal-Help": "If the original file should be deleted, this will only happen if the working file is different to the original file"
"DeleteOriginal-Help": "If the original file should be deleted, this will only happen if the working file is different to the original file",
"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."
}
},
"OriginalFile": {

View File

@@ -31,6 +31,12 @@ namespace FileFlows.BasicNodes.File
[Boolean(3)]
public bool CopyFolder { get; set; }
[StringArray(4)]
public string[] AdditionalFiles { get; set; }
[Boolean(5)]
public bool AdditionalFilesFromOriginal { get; set; }
private bool Canceled;
public override Task Cancel()
@@ -118,12 +124,39 @@ namespace FileFlows.BasicNodes.File
args?.Logger?.ELog("Action was canceled.");
return -1;
}
else
{
args?.SetWorkingFile(dest);
return base.Execute(args!);
var srcDir = AdditionalFilesFromOriginal ? new FileInfo(args.FileName).DirectoryName : new FileInfo(args.WorkingFile).DirectoryName;
if (AdditionalFiles?.Any() == true)
{
try
{
var diSrc = new DirectoryInfo(srcDir);
foreach (var additional in AdditionalFiles)
{
foreach (var addFile in diSrc.GetFiles(additional))
{
try
{
string addFileDest = Path.Combine(destDir, addFile.Name);
System.IO.File.Copy(addFile.FullName, addFileDest, true);
args.Logger?.ILog("Copyied file: \"" + addFile.FullName + "\" to \"" + addFileDest + "\"");
}
catch (Exception ex)
{
args.Logger?.ILog("Failed copying file: \"" + addFile.FullName + "\": " + ex.Message);
}
}
}
}
catch (Exception ex)
{
args.Logger.WLog("Error copying additinoal files: " + ex.Message);
}
}
args?.SetWorkingFile(dest);
return 1;
}
}
}

View File

@@ -26,6 +26,12 @@ namespace FileFlows.BasicNodes.File
[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)
{
string dest = args.ReplaceVariables(DestinationPath, true);
@@ -68,9 +74,40 @@ namespace FileFlows.BasicNodes.File
var destDir = fiDest.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)
{
try
{
var diSrc = new DirectoryInfo(srcDir);
foreach (var additional in AdditionalFiles)
{
foreach(var addFile in diSrc.GetFiles(additional))
{
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);
}
}
}
}
catch(Exception ex)
{
args.Logger.WLog("Error moving additinoal files: " + ex.Message);
}
}
if (DeleteOriginal && args.FileName != args.WorkingFile)
{
args.Logger?.ILog("Deleting orginal file: " + args.FileName);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -16,7 +16,7 @@ namespace VideoNodes.Tests
[TestMethod]
public void DetectBlackBars_Test_01()
{
const string file = @"D:\videos\unprocessed\Fast Five (2011) Bluray-2160p.mkv";
const string file = @"D:\videos\Dexter - New Blood - S01E02 - Storm of Fuck.mkv";
var vi = new VideoInfoHelper(@"C:\utils\ffmpeg\ffmpeg.exe", new TestLogger());
var vii = vi.Read(file);
@@ -35,7 +35,13 @@ namespace VideoNodes.Tests
Assert.AreEqual(1, output);
}
}
[TestMethod]
public void DetectBlackBars_Test_02()
{
var crop = DetectBlackBars.TestAboveThreshold(1920, 1080, 1920, 1072, 20);
Assert.IsFalse(crop.crop);
}
}
}

View File

@@ -10,10 +10,10 @@ namespace FileFlows.VideoNodes
private string ffMpegExe;
private ILogger Logger;
Regex rgxTitle = new Regex(@"(?<=((^[\s]+title[\s]+:[\s])))(.*?)$", RegexOptions.Multiline);
Regex rgxDuration = new Regex(@"(?<=((^[\s]+DURATION(\-[\w]+)?[\s]+:[\s])))([\d]+:?)+\.[\d]{1,7}", RegexOptions.Multiline);
Regex rgxDuration2 = new Regex(@"(?<=((^[\s]+Duration:[\s])))([\d]+:?)+\.[\d]{1,7}", RegexOptions.Multiline);
Regex rgxAudioSampleRate = new Regex(@"(?<=((,|\s)))[\d]+(?=([\s]?hz))", RegexOptions.IgnoreCase);
static Regex rgxTitle = new Regex(@"(?<=((^[\s]+title[\s]+:[\s])))(.*?)$", RegexOptions.Multiline);
static Regex rgxDuration = new Regex(@"(?<=((^[\s]+DURATION(\-[\w]+)?[\s]+:[\s])))([\d]+:?)+\.[\d]{1,7}", RegexOptions.Multiline);
static Regex rgxDuration2 = new Regex(@"(?<=((^[\s]+Duration:[\s])))([\d]+:?)+\.[\d]{1,7}", RegexOptions.Multiline);
static Regex rgxAudioSampleRate = new Regex(@"(?<=((,|\s)))[\d]+(?=([\s]?hz))", RegexOptions.IgnoreCase);
public VideoInfoHelper(string ffMpegExe, ILogger logger)
{
@@ -62,64 +62,7 @@ namespace FileFlows.VideoNodes
}
Logger.ILog("Video Information:" + Environment.NewLine + output);
var rgxStreams = new Regex(@"Stream\s#[\d]+:[\d]+(.*?)(?=(Stream\s#[\d]|$))", RegexOptions.Singleline);
var streamMatches = rgxStreams.Matches(output);
int streamIndex = 0;
// get a rough estimate, bitrate: 346 kb/s
var rgxBitrate = new Regex(@"(?<=(bitrate: ))[\d\.]+(?!=( kb/s))");
var brMatch = rgxBitrate.Match(output);
if (brMatch.Success)
{
vi.Bitrate = float.Parse(brMatch.Value) * 1_000; // to convert to b/s
}
int subtitleIndex = 1;
foreach (Match sm in streamMatches)
{
if (sm.Value.Contains(" Video: "))
{
var vs = ParseVideoStream(sm.Value, output);
if (vs != null)
{
vs.Index = streamIndex;
var match = Regex.Match(sm.Value, @"(?<=(Stream #))[\d]+:[\d]+");
if (match.Success)
vs.IndexString = match.Value;
vi.VideoStreams.Add(vs);
}
}
else if (sm.Value.Contains(" Audio: "))
{
var audio = ParseAudioStream(sm.Value);
if (audio != null)
{
audio.Index = streamIndex;
var match = Regex.Match(sm.Value, @"(?<=(Stream #))[\d]+:[\d]+");
if (match.Success)
audio.IndexString = match.Value;
vi.AudioStreams.Add(audio);
}
}
else if (sm.Value.Contains(" Subtitle: "))
{
var sub = ParseSubtitleStream(sm.Value);
if (sub != null)
{
sub.Index = streamIndex;
sub.TypeIndex = subtitleIndex;
var match = Regex.Match(sm.Value, @"(?<=(Stream #))[\d]+:[\d]+");
if (match.Success)
sub.IndexString = match.Value;
vi.SubtitleStreams.Add(sub);
}
++subtitleIndex;
}
++streamIndex;
}
vi = ParseOutput(Logger, output);
}
}
catch (Exception ex)
@@ -130,7 +73,69 @@ namespace FileFlows.VideoNodes
return vi;
}
VideoStream ParseVideoStream(string info, string fullOutput)
public static VideoInfo ParseOutput(ILogger logger, string output)
{
var vi = new VideoInfo();
var rgxStreams = new Regex(@"Stream\s#[\d]+:[\d]+(.*?)(?=(Stream\s#[\d]|$))", RegexOptions.Singleline);
var streamMatches = rgxStreams.Matches(output);
int streamIndex = 0;
// get a rough estimate, bitrate: 346 kb/s
var rgxBitrate = new Regex(@"(?<=(bitrate: ))[\d\.]+(?!=( kb/s))");
var brMatch = rgxBitrate.Match(output);
if (brMatch.Success)
{
vi.Bitrate = float.Parse(brMatch.Value) * 1_000; // to convert to b/s
}
int subtitleIndex = 1;
foreach (Match sm in streamMatches)
{
if (sm.Value.Contains(" Video: "))
{
var vs = ParseVideoStream(logger, sm.Value, output);
if (vs != null)
{
vs.Index = streamIndex;
var match = Regex.Match(sm.Value, @"(?<=(Stream #))[\d]+:[\d]+");
if (match.Success)
vs.IndexString = match.Value;
vi.VideoStreams.Add(vs);
}
}
else if (sm.Value.Contains(" Audio: "))
{
var audio = ParseAudioStream(sm.Value);
if (audio != null)
{
audio.Index = streamIndex;
var match = Regex.Match(sm.Value, @"(?<=(Stream #))[\d]+:[\d]+");
if (match.Success)
audio.IndexString = match.Value;
vi.AudioStreams.Add(audio);
}
}
else if (sm.Value.Contains(" Subtitle: "))
{
var sub = ParseSubtitleStream(sm.Value);
if (sub != null)
{
sub.Index = streamIndex;
sub.TypeIndex = subtitleIndex;
var match = Regex.Match(sm.Value, @"(?<=(Stream #))[\d]+:[\d]+");
if (match.Success)
sub.IndexString = match.Value;
vi.SubtitleStreams.Add(sub);
}
++subtitleIndex;
}
++streamIndex;
}
return vi;
}
public static VideoStream ParseVideoStream(ILogger logger, string info, string fullOutput)
{
// Stream #0:0(eng): Video: h264 (High), yuv420p(tv, bt709/unknown/unknown, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn (default)
string line = info.Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries).First();
@@ -154,22 +159,22 @@ namespace FileFlows.VideoNodes
if (rgxDuration.IsMatch(info) && TimeSpan.TryParse(rgxDuration.Match(info).Value, out TimeSpan duration) && duration.TotalSeconds > 0)
{
vs.Duration = duration;
Logger?.ILog("Video stream duration: " + vs.Duration);
logger?.ILog("Video stream duration: " + vs.Duration);
}
else if (rgxDuration2.IsMatch(fullOutput) && TimeSpan.TryParse(rgxDuration2.Match(fullOutput).Value, out TimeSpan duration2) && duration2.TotalSeconds > 0)
{
vs.Duration = duration2;
Logger?.ILog("Video stream duration: " + vs.Duration);
logger?.ILog("Video stream duration: " + vs.Duration);
}
else
{
Logger?.ILog("Failed to read duration for VideoStream: " + info);
logger?.ILog("Failed to read duration for VideoStream: " + info);
}
return vs;
}
AudioStream ParseAudioStream(string info)
public static AudioStream ParseAudioStream(string info)
{
// Stream #0:1(eng): Audio: dts (DTS), 48000 Hz, stereo, fltp, 1536 kb/s (default)
string line = info.Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries).First();
@@ -201,7 +206,7 @@ namespace FileFlows.VideoNodes
return audio;
}
SubtitleStream ParseSubtitleStream(string info)
public static SubtitleStream ParseSubtitleStream(string info)
{
// Stream #0:1(eng): Audio: dts (DTS), 48000 Hz, stereo, fltp, 1536 kb/s (default)
string line = info.Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries).First();

Binary file not shown.

View File

@@ -44,6 +44,11 @@
Gets or sets the processing priority of this library
</summary>
</member>
<member name="P:FileFlows.Shared.Models.LibraryFile.ExecutedNodes">
<summary>
Gets or sets a list of nodes that were executed against this library file
</summary>
</member>
<member name="T:FileFlows.Shared.Validators.DefaultValidator">
<summary>
Used instead of null

View File

@@ -35,18 +35,6 @@
}
}
},
"NodaTime/3.0.9": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.7.1"
},
"runtime": {
"lib/netstandard2.0/NodaTime.dll": {
"assemblyVersion": "3.0.9.0",
"fileVersion": "3.0.9.0"
}
}
},
"System.Runtime.CompilerServices.Unsafe/4.7.1": {},
"FileFlows.Plugin/1.0.0": {
"runtime": {
"FileFlows.Plugin.dll": {}
@@ -55,8 +43,7 @@
"FileFlows.ServerShared/1.0.0": {
"dependencies": {
"FileFlows.Plugin": "1.0.0",
"FileFlows.Shared": "1.0.0",
"NodaTime": "3.0.9"
"FileFlows.Shared": "1.0.0"
},
"runtime": {
"FileFlows.ServerShared.dll": {}
@@ -93,20 +80,6 @@
"path": "microsoft.extensions.objectpool/5.0.5",
"hashPath": "microsoft.extensions.objectpool.5.0.5.nupkg.sha512"
},
"NodaTime/3.0.9": {
"type": "package",
"serviceable": true,
"sha512": "sha512-7KzJBkgSzLLyBXNYafDlnzlji3aB/8myvt5RJu5aaTFq6puVCq2WDJY9v9yDMg22+5or5IaKNItQ1jNA8nzQNA==",
"path": "nodatime/3.0.9",
"hashPath": "nodatime.3.0.9.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/4.7.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-zOHkQmzPCn5zm/BH+cxC1XbUS3P4Yoi3xzW7eRgVpDR2tPGSzyMZ17Ig1iRkfJuY0nhxkQQde8pgePNiA7z7TQ==",
"path": "system.runtime.compilerservices.unsafe/4.7.1",
"hashPath": "system.runtime.compilerservices.unsafe.4.7.1.nupkg.sha512"
},
"FileFlows.Plugin/1.0.0": {
"type": "project",
"serviceable": false,

View File

@@ -59,4 +59,6 @@ SMTP
templating
scriban
github
normalization
normalization
doesn''t
*

Binary file not shown.