added checksum and collection nodes

This commit is contained in:
reven
2022-01-04 17:12:35 +13:00
parent d3795ce2a2
commit 0453bbdc0c
23 changed files with 673 additions and 15 deletions

Binary file not shown.

View File

@@ -63,6 +63,20 @@
"Extensions-Help": "A list of case insensitive file extensions that will be matched against.\nOutput 1 Matches\nOutput 2: Does not match"
}
},
"FileExists": {
"Description": "Checks if a file exists\n\nOutput 1: File exists\nOutput 2: File does not exist",
"Fields": {
"FileName": "File Name",
"FileName-Help": "The file to check if exists. This should be used with a variable from a previous node."
}
},
"Delete": {
"Description": "Deletes a file",
"Fields": {
"FileName": "File Name",
"FileName-Help": "If left blank the current working file will be deleted, or folder if library is folder based."
}
},
"FileSize": {
"Description": "Checks if the file size matches the configured parameters. The values are in megabytes.\n\nOutput 1: Matches\nOutput 2: Does not match",
"Fields": {

View File

@@ -1,6 +1,7 @@
namespace FileFlows.BasicNodes.File
{
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
public class Delete : Node
{
@@ -9,15 +10,22 @@ namespace FileFlows.BasicNodes.File
public override FlowElementType Type => FlowElementType.Process;
public override string Icon => "far fa-trash-alt";
[TextVariable(1)]
public string FileName { get; set; }
public override int Execute(NodeParameters args)
{
string filename = args.ReplaceVariables(this.FileName ?? string.Empty, stripMissing: true);
if (string.IsNullOrEmpty(filename))
filename = args.WorkingFile;
if (args.IsDirectory)
{
try
{
args.Logger?.ILog("Deleting directory: " + args.WorkingFile);
Directory.Delete(args.WorkingFile, true);
args.Logger?.ILog("Deleted directory: " + args.WorkingFile);
args.Logger?.ILog("Deleting directory: " + filename);
Directory.Delete(filename, true);
args.Logger?.ILog("Deleted directory: " + filename);
return 1;
}
catch (Exception ex)
@@ -30,14 +38,14 @@ namespace FileFlows.BasicNodes.File
{
try
{
args.Logger?.ILog("Deleting file: " + args.WorkingFile);
System.IO.File.Delete(args.WorkingFile);
args.Logger?.ILog("Deleted file: " + args.WorkingFile);
args.Logger?.ILog("Deleting file: " + filename);
System.IO.File.Delete(filename);
args.Logger?.ILog("Deleted file: " + filename);
return 1;
}
catch (Exception ex)
{
args.Logger?.ELog($"Failed to delete file: '{args.WorkingFile}' => {ex.Message}");
args.Logger?.ELog($"Failed to delete file: '{filename}' => {ex.Message}");
return -1;
}
}

View File

@@ -0,0 +1,42 @@
namespace FileFlows.BasicNodes.File
{
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
public class FileExists: Node
{
public override int Inputs => 1;
public override int Outputs => 2;
public override FlowElementType Type => FlowElementType.Logic;
public override string Icon => "fas fa-question-circle";
[TextVariable(1)]
public string FileName { get; set; }
public override int Execute(NodeParameters args)
{
string file = args.ReplaceVariables(FileName ?? string.Empty, true);
if(string.IsNullOrWhiteSpace(file))
{
args.Logger?.ELog("FileName not set");
return -1;
}
try
{
var fileInfo = new FileInfo(file);
if (fileInfo.Exists)
{
args.Logger?.ILog("File does exist: " + file);
return 1;
}
args.Logger?.ILog("File does NOT exist: " + file);
return 2;
}
catch (Exception ex)
{
args.Logger?.ELog($"Failed testing if file '{file}' exists: " + ex.Message);
return -1;
}
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,15 @@
{
"Flow":{
"Parts": {
"MD5Checksum": {
"Description": "Computes a MD5 checksum of the working file and stores it in the variable \"MD5\" and in \"Checksum\"."
},
"SHA1Checksum": {
"Description": "Computes a SHA1 checksum of the working file and stores it in the variable \"SHA1\" and in \"Checksum\"."
},
"SHA256Checksum": {
"Description": "Computes a SHA256 checksum of the working file and stores it in the variable \"SHA256\" and in \"Checksum\"."
}
}
}
}

View File

@@ -0,0 +1,6 @@
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Text;
global using System.Threading.Tasks;
global using FileFlows.Plugin;

View File

@@ -0,0 +1,40 @@
using System.Security.Cryptography;
namespace ChecksumNodes
{
public class MD5:Node
{
public override int Inputs => 1;
public override int Outputs => 1;
public override FlowElementType Type => FlowElementType.Logic;
public override string Icon => "fas fa-file-contract";
private Dictionary<string, object> _Variables;
public override Dictionary<string, object> Variables => _Variables;
public MD5()
{
_Variables = new Dictionary<string, object>()
{
{ "Checksum", "4A4566696CC81C6053EC708975767498" },
{ "MD5", "4A4566696CC81C6053EC708975767498" }
};
}
public override int Execute(NodeParameters args)
{
using var md5 = System.Security.Cryptography.MD5.Create();
DateTime start = DateTime.Now;
using var stream = File.OpenRead(args.WorkingFile);
var hash = md5.ComputeHash(stream);
string hashStr = BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
args.Logger?.ILog("MD5 of working file: " + hashStr);
args.Logger?.ILog("Time taken to compute hash: " + (DateTime.Now - start));
args.UpdateVariables(new Dictionary<string, object>
{
{ "MD5", hashStr },
{ "Checksum", hashStr },
});
return 1;
}
}
}

View File

@@ -0,0 +1,41 @@
using System.Security.Cryptography;
namespace ChecksumNodes
{
public class SHA1:Node
{
public override int Inputs => 1;
public override int Outputs => 1;
public override FlowElementType Type => FlowElementType.Logic;
public override string Icon => "fas fa-file-contract";
private Dictionary<string, object> _Variables;
public override Dictionary<string, object> Variables => _Variables;
public SHA1()
{
_Variables = new Dictionary<string, object>()
{
{ "Checksum", "32B26A271530F105CBC35CB653110E1A49D019B6" },
{ "SHA1", "32B26A271530F105CBC35CB653110E1A49D019B6" }
};
}
public override int Execute(NodeParameters args)
{
using var hasher = System.Security.Cryptography.SHA1.Create();
DateTime start = DateTime.Now;
using var stream = File.OpenRead(args.WorkingFile);
var hash = hasher.ComputeHash(stream);
string hashStr = BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
args.Logger?.ILog("SHA1 of working file: " + hashStr);
args.Logger?.ILog("Time taken to compute hash: " + (DateTime.Now - start));
args.UpdateVariables(new Dictionary<string, object>
{
{ "SHA1", hashStr },
{ "Checksum", hashStr },
});
return 1;
}
}
}

View File

@@ -0,0 +1,41 @@
using System.Security.Cryptography;
namespace ChecksumNodes
{
public class SHA256:Node
{
public override int Inputs => 1;
public override int Outputs => 1;
public override FlowElementType Type => FlowElementType.Logic;
public override string Icon => "fas fa-file-contract";
private Dictionary<string, object> _Variables;
public override Dictionary<string, object> Variables => _Variables;
public SHA256()
{
_Variables = new Dictionary<string, object>()
{
{ "Checksum", "BD9B74A682CD757611805F86371A5A277B2941FA42345CE4C87A7C9E28244C2C" },
{ "SHA256", "BD9B74A682CD757611805F86371A5A277B2941FA42345CE4C87A7C9E28244C2C" }
};
}
public override int Execute(NodeParameters args)
{
using var hasher = System.Security.Cryptography.SHA256.Create();
DateTime start = DateTime.Now;
using var stream = File.OpenRead(args.WorkingFile);
var hash = hasher.ComputeHash(stream);
string hashStr = BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
args.Logger?.ILog("SHA256 of working file: " + hashStr);
args.Logger?.ILog("Time taken to compute hash: " + (DateTime.Now - start));
args.UpdateVariables(new Dictionary<string, object>
{
{ "SHA256", hashStr },
{ "Checksum", hashStr },
});
return 1;
}
}
}

View File

@@ -0,0 +1,41 @@
using System.Security.Cryptography;
namespace ChecksumNodes
{
public class SHA512:Node
{
public override int Inputs => 1;
public override int Outputs => 1;
public override FlowElementType Type => FlowElementType.Logic;
public override string Icon => "fas fa-file-contract";
private Dictionary<string, object> _Variables;
public override Dictionary<string, object> Variables => _Variables;
public SHA512()
{
_Variables = new Dictionary<string, object>()
{
{ "Checksum", "267dc7710d5938c6dc362dd24b2f3b13e4a75a1f4338c0d0a39786afd67491f13be0525f46b1bcdb7be934248870210e73166f9103063b9a6a986a94dae77d4e" },
{ "SHA512", "267dc7710d5938c6dc362dd24b2f3b13e4a75a1f4338c0d0a39786afd67491f13be0525f46b1bcdb7be934248870210e73166f9103063b9a6a986a94dae77d4e" }
};
}
public override int Execute(NodeParameters args)
{
using var hasher = System.Security.Cryptography.SHA512.Create();
DateTime start = DateTime.Now;
using var stream = File.OpenRead(args.WorkingFile);
var hash = hasher.ComputeHash(stream);
string hashStr = BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();
args.Logger?.ILog("SHA512 of working file: " + hashStr);
args.Logger?.ILog("Time taken to compute hash: " + (DateTime.Now - start));
args.UpdateVariables(new Dictionary<string, object>
{
{ "SHA512", hashStr },
{ "Checksum", hashStr },
});
return 1;
}
}
}

11
ChecksumNodes/Plugin.cs Normal file
View File

@@ -0,0 +1,11 @@
namespace ChecksumNodes
{
public class Plugin : IPlugin
{
public string Name => "Checksum Nodes";
public void Init()
{
}
}
}

View File

@@ -0,0 +1,49 @@
#if(DEBUG)
namespace ChecksumNodes.Tests
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class ChecksumTests
{
[TestMethod]
public void Checksum_MD5_Large()
{
var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "");
var output = new MD5().Execute(args);
Assert.IsTrue(args.Variables.ContainsKey("MD5"));
Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["MD5"] as string));
}
[TestMethod]
public void Checksum_SHA1_Large()
{
var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "");
var output = new SHA1().Execute(args);
Assert.IsTrue(args.Variables.ContainsKey("SHA1"));
Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA1"] as string));
}
[TestMethod]
public void Checksum_SHA256_Large()
{
var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "");
var output = new SHA256().Execute(args);
Assert.IsTrue(args.Variables.ContainsKey("SHA256"));
Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA256"] as string));
}
[TestMethod]
public void Checksum_SHA512_Large()
{
var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "");
var output = new SHA512().Execute(args);
Assert.IsTrue(args.Variables.ContainsKey("SHA512"));
Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA512"] as string));
}
}
}
#endif

View File

@@ -0,0 +1,45 @@
#if(DEBUG)
namespace ChecksumNodes.Tests
{
using FileFlows.Plugin;
using System;
using System.Collections.Generic;
using System.Linq;
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;
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)));
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);
}
}
}
#endif

Binary file not shown.

View File

@@ -0,0 +1,21 @@
{
"Flow":{
"Parts": {
"DataCollection": {
"Description": "Checks a database for a key and a matching value. If not in the database, will add it to the database.\n\nOutput 1: Not in database\nOutput 2: In database and the same\nOutput 3: In datbase but value different.",
"Fields": {
"DatabaseFolder": "Database Folder",
"DatabaseFolder-Help": "The location to save the SQLite database file. This folder must be accessible by every processing node.",
"DatabaseFile": "Database File",
"DatabaseFile-Help": "The SQLite database file. This file must be accessible by every processing node.",
"Key": "Key",
"Key-Help": "The key value used to index the collection. If left empty the original filename will be used.",
"Value": "Value",
"Value-Help": "The value to check the collection for. Can be a variable from a previous node.",
"UpdateIfDifferent": "Update If Different",
"UpdateIfDifferent-Help": "If the value in the collection should be updated if the stored value is different."
}
}
}
}
}

View File

@@ -0,0 +1,6 @@
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Text;
global using System.Threading.Tasks;
global using FileFlows.Plugin;

View File

@@ -0,0 +1,142 @@
using FileFlows.Plugin.Attributes;
using NPoco;
using System.ComponentModel.DataAnnotations;
using System.Data.SQLite;
namespace CollectionNodes
{
public class DataCollection : Node
{
public override int Inputs => 1;
public override int Outputs => 3;
public override FlowElementType Type => FlowElementType.Logic;
public override string Icon => "fas fa-database";
private Dictionary<string, object> _Variables;
public override Dictionary<string, object> Variables => _Variables;
public DataCollection()
{
_Variables = new Dictionary<string, object>()
{
{ "dc.Value", "ValueFromDataCollection" }
};
}
[Required]
[Folder(1)]
public string DatabaseFolder { get; set; }
[Required]
[Text(2)]
public string DatabaseFile { get; set; }
[TextVariable(3)]
public string Key { get; set; }
[Required]
[TextVariable(4)]
public string Value { get; set; }
[Boolean(5)]
public bool UpdateIfDifferent { get; set; }
public override int Execute(NodeParameters args)
{
string key = args.ReplaceVariables(Key ?? string.Empty, true);
string value = args.ReplaceVariables(Value ?? string.Empty, true);
if (string.IsNullOrEmpty(key))
{
args.Logger?.ELog("Key not set using filename");
key = args.FileName;
}
if (string.IsNullOrEmpty(value))
{
args.Logger?.ELog("Value not set");
return -1;
}
if (args.Variables.ContainsKey(value))
{
value = args.Variables[value]?.ToString() ?? string.Empty;
}
string dbFile = Path.Combine(
args.ReplaceVariables(this.DatabaseFolder, stripMissing: true),
args.ReplaceVariables(this.DatabaseFile, stripMissing: true)
);
CreateDatabaseIfNotExists(dbFile);
var db = new Database($"Data Source={dbFile};Version=3;", null, SQLiteFactory.Instance);
string result = db.ExecuteScalar<string>($"select {nameof(DbObject.Value)} from {nameof(DbObject)} where {nameof(DbObject.Key)} = @0", key);
if(result == null)
{
args.Logger?.ILog("Key not in database");
db.Execute($"insert into {nameof(DbObject)} values (@0, @1)", key, value);
args.UpdateVariables(new Dictionary<string, object>
{
{ "dc.Value", value },
});
return 1;
}
if (result == value)
{
args.Logger?.ILog("Value matches what was already in database");
args.UpdateVariables(new Dictionary<string, object>
{
{ "dc.Value", value },
});
return 2;
}
args.UpdateVariables(new Dictionary<string, object>
{
{ "dc.Value", result },
});
args.Logger?.ILog("Key value did not match what was in database");
if (UpdateIfDifferent)
{
db.Execute($"update {nameof(DbObject)} set {nameof(DbObject.Value)} = @1 where {nameof(DbObject.Key)} = @0", key, value);
}
return 3;
}
private void CreateDatabaseIfNotExists(string dbFile)
{
if (System.IO.File.Exists(dbFile) == false)
SQLiteConnection.CreateFile(dbFile);
else
{
// create backup
File.Copy(dbFile, dbFile + ".backup", true);
}
using (var con = new SQLiteConnection($"Data Source={dbFile};Version=3;"))
{
con.Open();
using (var cmd = new SQLiteCommand($"SELECT name FROM sqlite_master WHERE type='table' AND name='{nameof(DbObject)}'", con))
{
if (cmd.ExecuteScalar() != null)
return;// tables exist, all good
}
using (var cmd = new SQLiteCommand(CreateDbScript, con))
{
cmd.ExecuteNonQuery();
}
con.Close();
}
}
private const string CreateDbScript = @$"CREATE TABLE {nameof(DbObject)}(
Key VARCHAR(1024) NOT NULL PRIMARY KEY,
Value TEXT NOT NULL
);";
private class DbObject
{
public string Key { get; set; }
public string Value { get; set; }
}
}
}

18
CollectionNodes/Plugin.cs Normal file
View File

@@ -0,0 +1,18 @@
using FileFlows.Plugin.Attributes;
using System.ComponentModel.DataAnnotations;
namespace CollectionNodes
{
public class Plugin : IPlugin
{
public string Name => "Collection Nodes";
[Folder(1)]
[Required]
public string DataDirectory { get; set; }
public void Init()
{
}
}
}

View File

@@ -0,0 +1,68 @@
#if(DEBUG)
namespace CollectionNodes.Tests
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class DataCollectionTests
{
[TestMethod]
public void DataCollection_NotIn()
{
var logger =new TestLogger();
var args = new NodeParameters(@"D:\videos\injustice.mkv", logger, false, "");
string dbFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".sqlite");
var dc = new DataCollection()
{
Value = "Checksum",
DatabaseFile = dbFile,
UpdateIfDifferent = true,
};
args.Variables.Add("Checksum", "batman");
var output = dc.Execute(args);
Assert.AreEqual(1, output);
}
[TestMethod]
public void DataCollection_InAndSame()
{
var logger = new TestLogger();
var args = new NodeParameters(@"D:\videos\injustice.mkv", logger, false, "");
string dbFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".sqlite");
var dc = new DataCollection()
{
Value = "Checksum",
DatabaseFile = dbFile,
UpdateIfDifferent = true,
};
args.Variables.Add("Checksum", "batman");
var output = dc.Execute(args);
Assert.AreEqual(1, output);
var output2 = dc.Execute(args);
Assert.AreEqual(2, output2);
}
[TestMethod]
public void DataCollection_InAndNotSame()
{
var logger = new TestLogger();
var args = new NodeParameters(@"D:\videos\injustice.mkv", logger, false, "");
string dbFile = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".sqlite");
var dc = new DataCollection()
{
Value = "Checksum",
DatabaseFile = dbFile,
UpdateIfDifferent = true,
};
args.Variables.Add("Checksum", "batman");
var output = dc.Execute(args);
Assert.AreEqual(1, output);
args.Variables["Checksum"] = "joker";
var output3 = dc.Execute(args);
Assert.AreEqual(3, output3);
}
}
}
#endif

View File

@@ -0,0 +1,45 @@
#if(DEBUG)
namespace CollectionNodes.Tests
{
using FileFlows.Plugin;
using System;
using System.Collections.Generic;
using System.Linq;
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;
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)));
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);
}
}
}
#endif

View File

@@ -7,7 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicNodes", "BasicNodes\Ba
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VideoNodes", "VideoNodes\VideoNodes.csproj", "{CF96D3D1-1D8B-47F7-BEA7-BB238F7A566A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MetaNodes", "MetaNodes\MetaNodes.csproj", "{E6F8E9F6-31D7-4A89-966D-2690CA7C0528}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MetaNodes", "MetaNodes\MetaNodes.csproj", "{E6F8E9F6-31D7-4A89-966D-2690CA7C0528}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChecksumNodes", "ChecksumNodes\ChecksumNodes.csproj", "{910B0A24-0E5C-4E1D-9C52-D537F896DE02}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CollectionNodes", "CollectionNodes\CollectionNodes.csproj", "{4211AAE9-0764-4626-B0F2-089A466E002A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -27,6 +31,14 @@ Global
{E6F8E9F6-31D7-4A89-966D-2690CA7C0528}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6F8E9F6-31D7-4A89-966D-2690CA7C0528}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6F8E9F6-31D7-4A89-966D-2690CA7C0528}.Release|Any CPU.Build.0 = Release|Any CPU
{910B0A24-0E5C-4E1D-9C52-D537F896DE02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{910B0A24-0E5C-4E1D-9C52-D537F896DE02}.Debug|Any CPU.Build.0 = Debug|Any CPU
{910B0A24-0E5C-4E1D-9C52-D537F896DE02}.Release|Any CPU.ActiveCfg = Release|Any CPU
{910B0A24-0E5C-4E1D-9C52-D537F896DE02}.Release|Any CPU.Build.0 = Release|Any CPU
{4211AAE9-0764-4626-B0F2-089A466E002A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4211AAE9-0764-4626-B0F2-089A466E002A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4211AAE9-0764-4626-B0F2-089A466E002A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4211AAE9-0764-4626-B0F2-089A466E002A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -6,15 +6,8 @@ namespace FileFlows.VideoNodes
public class Plugin : FileFlows.Plugin.IPlugin
{
public string Name => "Video Nodes";
[Required]
[File(2, "exe")]
public string FFProbeExe { get; set; }
public void Init()
{
//var context = new System.Runtime.Loader.AssemblyLoadContext(null, true);
//context.LoadFromAssemblyPath(@"C:\Users\john\src\ViWatcher\Plugins\VideoNodes\bin\Debug\net6.0\FFMpegCore.dll");
}
}
}