diff --git a/BasicNodes/Functions/Function.cs b/BasicNodes/Functions/Function.cs index 73aeceb0..cb072971 100644 --- a/BasicNodes/Functions/Function.cs +++ b/BasicNodes/Functions/Function.cs @@ -33,71 +33,52 @@ namespace FileFlows.BasicNodes.Functions if (string.IsNullOrEmpty(Code)) return -1; // no code, flow cannot continue doesnt know what to do - args.Logger?.DLog("Code: ", Environment.NewLine + new string('=', 40) + Environment.NewLine + Code + Environment.NewLine + new string('=', 40)); - - - long fileSize = 0; - var fileInfo = new FileInfo(args.WorkingFile); - if(fileInfo.Exists) - fileSize = fileInfo.Length; - - // replace Variables. with dictionary notation - string tcode = Code; - foreach (string k in args.Variables.Keys.OrderByDescending(x => x.Length)) - { - tcode = tcode.Replace("Variables." + k, "Variables['" + k + "']"); - } - - var sb = new StringBuilder(); - var log = new - { - ILog = new LogDelegate(args.Logger.ILog), - DLog = new LogDelegate(args.Logger.DLog), - WLog = new LogDelegate(args.Logger.WLog), - ELog = new LogDelegate(args.Logger.ELog), - }; - var engine = new Engine(options => - { - options.LimitMemory(4_000_000); - options.MaxStatements(500); - }) - .SetValue("Logger", args.Logger) - .SetValue("Variables", args.Variables) - .SetValue("Flow", args); try { + args.Logger?.DLog("Code: ", Environment.NewLine + new string('=', 40) + Environment.NewLine + Code + Environment.NewLine + new string('=', 40)); + + + long fileSize = 0; + var fileInfo = new FileInfo(args.WorkingFile); + if (fileInfo.Exists) + fileSize = fileInfo.Length; + + // replace Variables. with dictionary notation + string tcode = Code; + foreach (string k in args.Variables.Keys.OrderByDescending(x => x.Length)) + { + // replace Variables.Key or Variables?.Key?.Subkey etc to just the variable + // so Variables.file?.Orig.Name, will be replaced to Variables["file.Orig.Name"] + // since its just a dictionary key value + string keyRegex = @"Variables(\?)?\." + k.Replace(".", @"(\?)?\."); + tcode = Regex.Replace(tcode, keyRegex, "Variables['" + k + "']"); + } + + var sb = new StringBuilder(); + var log = new + { + ILog = new LogDelegate(args.Logger.ILog), + DLog = new LogDelegate(args.Logger.DLog), + WLog = new LogDelegate(args.Logger.WLog), + ELog = new LogDelegate(args.Logger.ELog), + }; + var engine = new Engine(options => + { + options.LimitMemory(4_000_000); + options.MaxStatements(500); + }) + .SetValue("Logger", args.Logger) + .SetValue("Variables", args.Variables) + .SetValue("Flow", args); var result = int.Parse(engine.Evaluate(tcode).ToObject().ToString()); return result; } catch (Exception ex) { - args.Logger.ELog("Failed executing function: " + ex.Message); + args.Logger?.ELog("Failed executing function: " + ex.Message + Environment.NewLine + ex.StackTrace); return -1; } } - - //private Dictionary ExplodeVariables(Dictionary input) - //{ - // Dictionary result = new(); - // foreach(var key in input.Keys) - // { - // if(key.IndexOf(".") > 0) - // { - // // folder.Date.Year - // // folder.Date.Month - // // folder.Date.Date - // //bk = Date - // string bk = key.Substring(0, key.IndexOf(".")); - // if(result.ContainsKey(bk) == false) - // result.Add(bk, new Dictionary()); - // Dictionary bkdict = (Dictionary)result[bk]; - // // nk = Year - // string nk = key.Substring(key.IndexOf(".") + 1); - // bkdict[] - // } - // } - // return result; - //} } } \ No newline at end of file diff --git a/BasicNodes/Tests/FunctionTests.cs b/BasicNodes/Tests/FunctionTests.cs index 1194985e..3fa078e4 100644 --- a/BasicNodes/Tests/FunctionTests.cs +++ b/BasicNodes/Tests/FunctionTests.cs @@ -176,6 +176,7 @@ return 1"; Assert.IsTrue(args.Parameters.ContainsKey("batman")); Assert.AreEqual(args.Parameters["batman"].ToString(), "1989"); } + [TestMethod] public void Function_Flow_GetDirectorySize() { @@ -208,6 +209,7 @@ return 0; var result = pm.Execute(args); Assert.AreEqual(2, result); } + [TestMethod] public void Function_Flow_ExecuteFfmpeg() { @@ -248,6 +250,144 @@ return 1; var result = pm.Execute(args); Assert.AreEqual(1, result); } + + + [TestMethod] + public void Function_Flow_NullabeVI() + { + Function pm = new Function(); + var logger = new TestLogger(); + var args = new FileFlows.Plugin.NodeParameters(@"c:\test\sdfsdfdsvfdcxdsf.mkv", logger, false, string.Empty); + + foreach(var kv in new Dictionary() + { + { "vi.Video.Codec", "hevc" }, + { "vi.Audio.Codec", "ac3" }, + { "vi.Audio.Codecs", "ac3,aac"}, + { "vi.Audio.Language", "eng" }, + { "vi.Audio.Languages", "eng, mao" }, + { "vi.Resolution", "1080p" }, + { "vi.Duration", 1800 }, + { "vi.VideoInfo", new + { + Bitrate = 10_000_000, + VideoStreams = new List { + new { Width = 1920, Height = 1080 } + } + } + }, + { "vi.Width", 1920 }, + { "vi.Height", 1080 }, + }) + { + args.Variables.Add(kv.Key, kv.Value); + }; + + pm.Code = @" +// get the first video stream, likely the only one +let video = Variables.vi?.VideoInfo?.VideoStreams[0]; +if (!video) + return -1; // no video streams detected + +if (video.Width > 1920) +{ + // down scale to 1920 and encodes using NVIDIA + // then add a 'Video Encode' node and in that node + // set + // 'Video Codec' to 'hevc' + // 'Video Codec Parameters' to '{EncodingParameters}' + Logger.ILog(`Need to downscale from ${video.Width}x${video.Height}`); + Variables.EncodingParameters = '-vf scale=1920:-2:flags=lanczos -c:v hevc_nvenc -preset hq -crf 23' + return 1; +} + +Logger.ILog('Do not need to downscale'); +return 2;"; + var result = pm.Execute(args); + Assert.IsTrue(result > 0); + } + + + [TestMethod] + public void Function_Flow_NullabeVI_2() + { + Function pm = new Function(); + var logger = new TestLogger(); + var args = new FileFlows.Plugin.NodeParameters(@"c:\test\sdfsdfdsvfdcxdsf.mkv", logger, false, string.Empty); + + foreach (var kv in new Dictionary() + { + { "vi.Video.Codec", "hevc" }, + { "vi.Audio.Codec", "ac3" }, + { "vi.Audio.Codecs", "ac3,aac"}, + { "vi.Audio.Language", "eng" }, + { "vi.Audio.Languages", "eng, mao" }, + { "vi.Resolution", "1080p" }, + { "vi.Duration", 1800 }, + { "vi.VideoInfo", new + { + Bitrate = 10_000_000, + VideoStreams = new List { + new { Width = 1920, Height = 1080 } + }, + AudioStreams = new List { + new { Bitrate = 1_000 } + } + } + }, + { "vi.Width", 1920 }, + { "vi.Height", 1080 }, + }) + { + args.Variables.Add(kv.Key, kv.Value); + }; + + pm.Code = @" +// check if the bitrate for a video is over a certain amount +let MAX_BITRATE = 3_000_000; // bitrate is 3,000 KBps + +let vi = Variables.vi?.VideoInfo; +if(!vi) + return -1; // no video information found + +// get the video stream +let bitrate = vi.VideoStreams[0]?.Bitrate; + +if(!bitrate) +{ + // video stream doesn't have bitrate information + // need to use the overall bitrate + let overall = vi.Bitrate; + if(!overall) + return 0; // couldn't get overall bitrate either + + // overall bitrate includes all audio streams, so we try and subtrack those + let calculated = overall; + if(vi.AudioStreams?.length) // check there are audio streams + { + for(let audio of vi.AudioStreams) + { + if(audio.Bitrate > 0) + calculated -= audio.Bitrate; + else{ + // audio doesn't have bitrate either, so we just subtract 5% of the original bitrate + // this is a guess, but it should get us close + calculated -= (overall * 0.05); + } + } + } + bitrate = calculated; +} + +// check if the bitrate is over the maximum bitrate +if(bitrate > MAX_BITRATE) + return 1; // it is, so call output 1 +return 2; // it isn't so call output 2"; + var result = pm.Execute(args); + Assert.IsTrue(result > 0); + } + + } }