set WarningsAsErrors = true

This commit is contained in:
John Andrews
2024-05-07 14:05:03 +12:00
parent d6c2034d8e
commit d60b0486b6
81 changed files with 732 additions and 946 deletions

View File

@@ -14,6 +14,7 @@
<Product>Apprise</Product> <Product>Apprise</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Lets you send Apprise messages to a server</Description> <Description>Lets you send Apprise messages to a server</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -11,7 +11,7 @@ public class AppriseTests
[TestMethod] [TestMethod]
public void Apprise_Basic_All() public void Apprise_Basic_All()
{ {
var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null!);;
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");
@@ -25,7 +25,7 @@ public class AppriseTests
[TestMethod] [TestMethod]
public void Apprise_Basic_Valid() public void Apprise_Basic_Valid()
{ {
var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null!);;
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");
@@ -40,7 +40,7 @@ public class AppriseTests
[TestMethod] [TestMethod]
public void Apprise_Basic_Invalid() public void Apprise_Basic_Invalid()
{ {
var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -23,11 +23,13 @@ public class FFprobeAudioInfo
{ {
try try
{ {
var ffAudioFormatInfo = System.Text.Json.JsonSerializer.Deserialize<FFprobeAudioInfo>(json, #pragma warning disable IL2026
var ffAudioFormatInfo = JsonSerializer.Deserialize<FFprobeAudioInfo>(json,
new JsonSerializerOptions() new JsonSerializerOptions()
{ {
PropertyNameCaseInsensitive = true PropertyNameCaseInsensitive = true
}); });
#pragma warning restore IL2026
return ffAudioFormatInfo.Format; return ffAudioFormatInfo.Format;
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -13,6 +13,7 @@
<Product>Audio</Product> <Product>Audio</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Flow elements for processing Audio files. This plugin contains flow elements to convert Audio files to different formats. Flow elements to parse the Audio information from a file.</Description> <Description>Flow elements for processing Audio files. This plugin contains flow elements to convert Audio files to different formats. Flow elements to parse the Audio information from a file.</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;CS8618;CS8601;CS8602;CS8603;CS8604;CS8618;CS8625;CS8765;CS8767;</NoWarn> <NoWarn>1701;1702;CS8618;CS8601;CS8602;CS8603;CS8604;CS8618;CS8625;CS8765;CS8767;</NoWarn>

View File

@@ -25,11 +25,13 @@ namespace FileFlows.AudioNodes.Tests
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -13,6 +13,7 @@
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Basic flow elements for FileFlows. This plugin contains basic and common flow elements to process files. <Description>Basic flow elements for FileFlows. This plugin contains basic and common flow elements to process files.
This plugin is required for FileFlows to work.</Description> This plugin is required for FileFlows to work.</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;CS8618;CS8601;CS8602;CS8603;CS8604;CS8618;CS8625;CS8765;CS8767;CS8618l;CS8600</NoWarn> <NoWarn>1701;1702;CS8618;CS8601;CS8602;CS8603;CS8604;CS8618;CS8625;CS8765;CS8767;CS8618l;CS8600</NoWarn>
@@ -25,9 +26,6 @@ This plugin is required for FileFlows to work.</Description>
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" /> <PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" /> <PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="SharpCompress" Version="0.36.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Plugin"> <Reference Include="Plugin">
<HintPath>..\FileFlows.Plugin.dll</HintPath> <HintPath>..\FileFlows.Plugin.dll</HintPath>

View File

@@ -1,185 +1,189 @@
namespace FileFlows.BasicNodes.File using System.ComponentModel.DataAnnotations;
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
using FileFlows.Plugin.Helpers;
namespace FileFlows.BasicNodes.File;
/// <summary>
/// Node that copies a file
/// </summary>
public class CopyFile : Node
{ {
using System.ComponentModel.DataAnnotations; /// <summary>
using FileFlows.Plugin; /// Gets the number of inputs
using FileFlows.Plugin.Attributes; /// </summary>
using FileFlows.Plugin.Helpers; public override int Inputs => 1;
/// <summary>
/// Gets the number of outputs
/// </summary>
public override int Outputs => 1;
/// <summary>
/// Gets the type of node
/// </summary>
public override FlowElementType Type => FlowElementType.Process;
/// <summary>
/// Gets the icon for this node
/// </summary>
public override string Icon => "far fa-copy";
/// <summary> /// <summary>
/// Node that copies a file /// Gets the help URL
/// </summary> /// </summary>
public class CopyFile : Node public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/copy-file";
private string _DestinationPath = string.Empty;
private string _DestinationFile = string.Empty;
/// <summary>
/// Gets or sets the input file to move
/// </summary>
[TextVariable(1)]
public string InputFile{ get; set; }
/// <summary>
/// Gets or sets the destination path to copy the file to
/// </summary>
[Required]
[Folder(2)]
public string DestinationPath
{
get => _DestinationPath;
set => _DestinationPath = value ?? string.Empty;
}
/// <summary>
/// Gets or sets the destination file
/// </summary>
[TextVariable(3)]
public string DestinationFile
{ {
/// <summary> get => _DestinationFile;
/// Gets the number of inputs set => _DestinationFile = value ?? string.Empty;
/// </summary> }
public override int Inputs => 1;
/// <summary>
/// Gets the number of outputs
/// </summary>
public override int Outputs => 1;
/// <summary>
/// Gets the type of node
/// </summary>
public override FlowElementType Type => FlowElementType.Process;
/// <summary>
/// Gets the icon for this node
/// </summary>
public override string Icon => "far fa-copy";
/// <summary> /// <summary>
/// Gets the help URL /// Gets or sets if the folder structure should be copied
/// </summary> /// </summary>
public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/copy-file"; [Boolean(4)]
public bool CopyFolder { get; set; }
private string _DestinationPath = string.Empty; /// <summary>
private string _DestinationFile = string.Empty; /// Gets or sets additional files to copy
/// </summary>
[StringArray(5)]
public string[] AdditionalFiles { get; set; }
/// <summary>
/// Gets or sets if the additional files should be copied from the source location (true) or temporary location (false)
/// </summary>
[Boolean(6)]
public bool AdditionalFilesFromOriginal { get; set; }
/// <summary> /// <summary>
/// Gets or sets the input file to move /// Gets or sets if the original files creation and last write time dates should be preserved
/// </summary> /// </summary>
[TextVariable(1)] [Boolean(7)]
public string InputFile{ get; set; } public bool PreserverOriginalDates { get; set; }
/// <inheritdoc />
public override int Execute(NodeParameters args)
{
var destParts = MoveFile.GetDestinationPathParts(args, DestinationPath, DestinationFile, CopyFolder);
if (destParts.Filename == null)
return -1;
[Required] string inputFile = args.ReplaceVariables(InputFile ?? string.Empty, stripMissing: true)?.EmptyAsNull() ?? args.WorkingFile;
[Folder(2)]
public string DestinationPath // cant use new FileInfo(dest).Directory.Name here since
{ // if the folder is a linux folder and this node is running on windows
get => _DestinationPath; // /mnt, etc will be converted to c:\mnt and break the destination
set { _DestinationPath = value ?? ""; } var destDir = destParts.Path;
} var destFile = destParts.Filename;
[TextVariable(3)] if(inputFile != args.WorkingFile && string.IsNullOrWhiteSpace(DestinationFile))
public string DestinationFile
{ {
get => _DestinationFile; destFile = FileHelper.GetShortFileName(inputFile);
set { _DestinationFile = value ?? ""; } }
string dest = FileHelper.Combine(destParts.Path, destFile);
//bool copied = args.CopyFile(args.WorkingFile, dest, updateWorkingFile: true);
//if (!copied)
// return -1;
args.Logger?.ILog("File to copy: " + inputFile);
args.Logger?.ILog("Destination: " + dest);
if (args.FileService.FileCopy(inputFile, dest, true).Failed(out string error))
{
args.FailureReason = "Failed to copy file: " + error;
args.Logger?.ELog(args.FailureReason);
return -1;
} }
[Boolean(4)] if (inputFile == args.WorkingFile)
public bool CopyFolder { get; set; }
[StringArray(5)]
public string[] AdditionalFiles { get; set; }
[Boolean(6)]
public bool AdditionalFilesFromOriginal { get; set; }
/// <summary>
/// Gets or sets if the original files creation and last write time dates should be preserved
/// </summary>
[Boolean(7)]
public bool PreserverOriginalDates { get; set; }
private bool Canceled;
public override Task Cancel()
{ {
Canceled = true; args.Logger?.ILog("Setting working file to: " + dest);
return base.Cancel(); args.SetWorkingFile(dest);
}
if (PreserverOriginalDates)
public override int Execute(NodeParameters args)
{
Canceled = false;
var destParts = MoveFile.GetDestinationPathParts(args, DestinationPath, DestinationFile, CopyFolder);
if (destParts.Filename == null)
return -1;
string inputFile = args.ReplaceVariables(InputFile ?? string.Empty, stripMissing: true)?.EmptyAsNull() ?? args.WorkingFile;
// cant use new FileInfo(dest).Directory.Name here since
// if the folder is a linux folder and this node is running on windows
// /mnt, etc will be converted to c:\mnt and break the destination
var destDir = destParts.Path;
var destFile = destParts.Filename;
if(inputFile != args.WorkingFile && string.IsNullOrWhiteSpace(DestinationFile))
{ {
destFile = FileHelper.GetShortFileName(inputFile); if (args.Variables.TryGetValue("ORIGINAL_CREATE_UTC", out object oCreateTimeUtc) &&
} args.Variables.TryGetValue("ORIGINAL_LAST_WRITE_UTC", out object oLastWriteUtc) &&
string dest = FileHelper.Combine(destParts.Path, destFile); oCreateTimeUtc is DateTime dtCreateTimeUtc && oLastWriteUtc is DateTime dtLastWriteUtc)
//bool copied = args.CopyFile(args.WorkingFile, dest, updateWorkingFile: true);
//if (!copied)
// return -1;
args.Logger?.ILog("File to copy: " + inputFile);
args.Logger?.ILog("Destination: " + dest);
if (args.FileService.FileCopy(inputFile, dest, true).Failed(out string error))
{
args.FailureReason = "Failed to copy file: " + error;
args.Logger?.ELog(args.FailureReason);
return -1;
}
if (inputFile == args.WorkingFile)
{
args.Logger?.ILog("Setting working file to: " + dest);
args.SetWorkingFile(dest);
if (PreserverOriginalDates)
{ {
if (args.Variables.TryGetValue("ORIGINAL_CREATE_UTC", out object oCreateTimeUtc) && args.Logger?.ILog("Preserving dates");
args.Variables.TryGetValue("ORIGINAL_LAST_WRITE_UTC", out object oLastWriteUtc) && args.FileService.SetLastWriteTimeUtc(dest, dtLastWriteUtc);
oCreateTimeUtc is DateTime dtCreateTimeUtc && oLastWriteUtc is DateTime dtLastWriteUtc) args.FileService.SetCreationTimeUtc(dest, dtCreateTimeUtc);
{ }
args.Logger?.ILog("Preserving dates"); else
args.FileService.SetLastWriteTimeUtc(dest, dtLastWriteUtc); {
args.FileService.SetCreationTimeUtc(dest, dtCreateTimeUtc); args.Logger?.WLog("Preserve dates is on but failed to get original dates from variables");
}
else
{
args.Logger?.WLog("Preserve dates is on but failed to get original dates from variables");
}
} }
} }
}
var srcDir = FileHelper.GetDirectory(AdditionalFilesFromOriginal var srcDir = FileHelper.GetDirectory(AdditionalFilesFromOriginal
? args.FileName ? args.FileName
: inputFile); : inputFile);
if (AdditionalFiles?.Any() == true) if (AdditionalFiles?.Any() == true)
{
try
{ {
try foreach (var additional in AdditionalFiles)
{ {
foreach (var additional in AdditionalFiles) foreach(var addFile in args.FileService.GetFiles(srcDir, additional).ValueOrDefault ?? new string[] {})
{ {
foreach(var addFile in args.FileService.GetFiles(srcDir, additional).ValueOrDefault ?? new string[] {}) try
{ {
try string shortName = FileHelper.GetShortFileName(addFile);
{
string shortName = FileHelper.GetShortFileName(addFile); string addFileDest = destDir + args.FileService.PathSeparator + shortName;
args.FileService.FileCopy(addFile, addFileDest, true);
string addFileDest = destDir + args.FileService.PathSeparator + shortName; //args.CopyFile(addFile, addFileDest, updateWorkingFile: false);
args.FileService.FileCopy(addFile, addFileDest, true);
//args.CopyFile(addFile, addFileDest, updateWorkingFile: false);
//FileHelper.ChangeOwner(args.Logger, addFileDest, file: true); //FileHelper.ChangeOwner(args.Logger, addFileDest, file: true);
args.Logger?.ILog("Copied file: \"" + addFile + "\" to \"" + addFileDest + "\""); args.Logger?.ILog("Copied file: \"" + addFile + "\" to \"" + addFileDest + "\"");
} }
catch (Exception ex) catch (Exception ex)
{ {
args.Logger?.ILog("Failed copying file: \"" + addFile + "\": " + ex.Message); args.Logger?.ILog("Failed copying file: \"" + addFile + "\": " + ex.Message);
}
} }
} }
} }
catch (Exception ex)
{
args.Logger.WLog("Error copying additional files: " + ex.Message);
}
} }
catch (Exception ex)
// not needed as args.CopyFile does this {
//args?.SetWorkingFile(dest); args.Logger.WLog("Error copying additional files: " + ex.Message);
return 1; }
} }
// not needed as args.CopyFile does this
//args?.SetWorkingFile(dest);
return 1;
} }
} }

View File

@@ -19,8 +19,8 @@ public class PatternReplacer : Node
public override FlowElementType Type => FlowElementType.Process; public override FlowElementType Type => FlowElementType.Process;
/// <inheritdoc /> /// <inheritdoc />
public override string Icon => "fas fa-exchange-alt"; public override string Icon => "fas fa-exchange-alt";
/// <inheritdoc />
public string Group => "File"; public override string Group => "File";
/// <inheritdoc /> /// <inheritdoc />
public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/filename-pattern-replacer"; public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/filename-pattern-replacer";
@@ -108,7 +108,10 @@ public class PatternReplacer : Node
// this might not be a regex, but try it first // this might not be a regex, but try it first
updated = Regex.Replace(updated, replacement.Key, value, RegexOptions.IgnoreCase); updated = Regex.Replace(updated, replacement.Key, value, RegexOptions.IgnoreCase);
} }
catch (Exception ex) { } catch (Exception)
{
// Ignored
}
updated = updated.Replace(replacement.Key, value); updated = updated.Replace(replacement.Key, value);
} }

View File

@@ -1,4 +1,6 @@
#if (DEBUG) using System.Diagnostics.CodeAnalysis;
#if (DEBUG)
namespace BasicNodes.Tests namespace BasicNodes.Tests
{ {
@@ -25,11 +27,13 @@ namespace BasicNodes.Tests
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -460,23 +460,6 @@ public class LocalFileService : IFileService
FileHelper.ChangeOwner(logger, path, file: isFile, ownerGroup: OwnerGroup); FileHelper.ChangeOwner(logger, path, file: isFile, ownerGroup: OwnerGroup);
logMethod(logger.ToString()); logMethod(logger.ToString());
return;
if (OperatingSystem.IsLinux())
{
var filePermissions = FileHelper.ConvertLinuxPermissionsToUnixFileMode(permissions.Value);
if (filePermissions == UnixFileMode.None)
{
logMethod("SetPermissions: Invalid file permissions: " + permissions.Value);
return;
}
File.SetUnixFileMode(path, filePermissions);
logMethod($"SetPermissions: Permission [{filePermissions}] set on file: " + path);
}
} }
} }
#endif #endif

View File

@@ -1,9 +1,6 @@
using System.Diagnostics;
using FileFlows.Plugin; using FileFlows.Plugin;
using FileFlows.Plugin.Attributes; using FileFlows.Plugin.Attributes;
using System.IO.Compression;
using FileFlows.BasicNodes; using FileFlows.BasicNodes;
using SharpCompress.Archives;
using System.IO; using System.IO;
namespace BasicNodes.Tools; namespace BasicNodes.Tools;
@@ -13,9 +10,13 @@ namespace BasicNodes.Tools;
/// </summary> /// </summary>
public class Unpack: Node public class Unpack: Node
{ {
/// <inheritdoc />
public override int Inputs => 1; public override int Inputs => 1;
/// <inheritdoc />
public override int Outputs => 1; public override int Outputs => 1;
/// <inheritdoc />
public override FlowElementType Type => FlowElementType.Process; public override FlowElementType Type => FlowElementType.Process;
/// <inheritdoc />
public override string Icon => "fas fa-file-archive"; public override string Icon => "fas fa-file-archive";
/// <summary> /// <summary>
/// Gets the Help URL for this element /// Gets the Help URL for this element
@@ -25,6 +26,9 @@ public class Unpack: Node
private string _DestinationPath = string.Empty; private string _DestinationPath = string.Empty;
private string _file = string.Empty; private string _file = string.Empty;
/// <summary>
/// Gets or sets the destination path
/// </summary>
[Folder(1)] [Folder(1)]
public string DestinationPath public string DestinationPath
{ {
@@ -32,13 +36,17 @@ public class Unpack: Node
set { _DestinationPath = value ?? ""; } set { _DestinationPath = value ?? ""; }
} }
/// <summary>
/// Gets or sets the file to unpack
/// </summary>
[TextVariable(2)] [TextVariable(2)]
public string File public string File
{ {
get => _file; get => _file;
set { _file = value ?? ""; } set => _file = value ?? string.Empty;
} }
/// <inheritdoc />
public override int Execute(NodeParameters args) public override int Execute(NodeParameters args)
{ {
try try
@@ -58,10 +66,11 @@ public class Unpack: Node
if (Directory.Exists(destDir) == false) if (Directory.Exists(destDir) == false)
Directory.CreateDirectory(destDir); Directory.CreateDirectory(destDir);
if (fileInfo.Extension.ToLower() == ".zip") args.ArchiveHelper.Extract(filename, destDir, (percent) =>
ZipFile.ExtractToDirectory(filename, destDir, true); {
else args.PartPercentageUpdate(percent);
Extract(args, args.WorkingFile, destDir); });
return 1; return 1;
} }
@@ -71,59 +80,4 @@ public class Unpack: Node
return -1; return -1;
} }
} }
/// <summary>
/// Unpacks a file
/// </summary>
/// <param name="args">the node parameters</param>
/// <param name="workingFile">the file to extract</param>
/// <param name="destinationPath">the location to extract to</param>
private void Extract(NodeParameters args, string workingFile, string destinationPath)
{
bool isRar = workingFile.ToLowerInvariant().EndsWith(".cbr") || workingFile.ToLowerInvariant().EndsWith(".rar");
try
{
ArchiveFactory.WriteToDirectory(workingFile, destinationPath, new ()
{
ExtractFullPath = true
});
}
catch (Exception ex) when (isRar && ex.Message.Contains("Unknown Rar Header"))
{
UnrarCommandLineExtract(args, workingFile, destinationPath);
}
}
/// <summary>
/// Unpacks a folder
/// </summary>
/// <param name="args">the node parameters</param>
/// <param name="workingFile">the file to extract</param>
/// <param name="destinationPath">the location to extract to</param>
void UnrarCommandLineExtract(NodeParameters args, string workingFile, string destinationPath)
{
var process = new Process();
string unrar = args.GetToolPath("unrar")?.EmptyAsNull() ?? "unrar";
process.StartInfo.FileName = unrar;
process.StartInfo.ArgumentList.Add("x");
process.StartInfo.ArgumentList.Add("-o+");
process.StartInfo.ArgumentList.Add(workingFile);
process.StartInfo.ArgumentList.Add(destinationPath);
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
string output = process.StandardError.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
args.Logger?.ILog("Unrar output:\n" + output);
if (string.IsNullOrWhiteSpace(error) == false)
args.Logger?.ELog("Unrar error:\n" + error);
if (process.ExitCode != 0)
throw new Exception(error?.EmptyAsNull() ?? "Failed to extract rar file");
}
} }

View File

@@ -1,58 +0,0 @@
using BasicNodes.Tools;
using FileFlows.Plugin;
using FileFlows.Plugin.Attributes;
namespace FileFlows.BasicNodes.File;
/// <summary>
/// Node that unzips a file
/// </summary>
public class Unzip : Node
{
/// <summary>
/// Gets that this node is obsolete
/// </summary>
public override bool Obsolete => true;
/// <summary>
/// Gets the obsolete message
/// </summary>
public override string ObsoleteMessage => "This has been replaced with the Unpack flow element.\n\nUse that instead.";
public override int Inputs => 1;
public override int Outputs => 1;
public override FlowElementType Type => FlowElementType.Process;
public override string Icon => "fas fa-file-archive";
/// <summary>
/// Gets the Help URL for this element
/// </summary>
public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/unzip";
private string _DestinationPath = string.Empty;
private string _zip = string.Empty;
[Folder(1)]
public string DestinationPath
{
get => _DestinationPath;
set { _DestinationPath = value ?? ""; }
}
[TextVariable(2)]
public string Zip
{
get => _zip;
set { _zip = value ?? ""; }
}
/// <summary>
/// Executes the node
/// </summary>
/// <param name="args">the arguments</param>
/// <returns>the output</returns>
public override int Execute(NodeParameters args)
{
var unpack = new Unpack();
unpack.File = Zip;
unpack.DestinationPath = DestinationPath;
return unpack.Execute(args);
}
}

View File

@@ -42,7 +42,7 @@ public class Zip : Node
public string DestinationPath public string DestinationPath
{ {
get => _DestinationPath; get => _DestinationPath;
set { _DestinationPath = value ?? ""; } set => _DestinationPath = value ?? string.Empty;
} }
/// <summary> /// <summary>
@@ -52,7 +52,7 @@ public class Zip : Node
public string DestinationFile public string DestinationFile
{ {
get => _DestinationFile; get => _DestinationFile;
set { _DestinationFile = value ?? ""; } set => _DestinationFile = value ?? string.Empty;
} }
/// <summary> /// <summary>
@@ -121,39 +121,16 @@ public class Zip : Node
args.Logger?.ELog("Cannot zip remote directories"); args.Logger?.ELog("Cannot zip remote directories");
return -1; return -1;
} }
var dir = new DirectoryInfo(args.WorkingFile); args.ArchiveHelper.Compress(args.WorkingFile, tempZip, allDirectories: true, percentCallback:(percent) =>
var files = dir.GetFiles("*.*", SearchOption.AllDirectories);
using FileStream fs = new FileStream(tempZip, FileMode.Create);
using ZipArchive arch = new ZipArchive(fs, ZipArchiveMode.Create);
args?.PartPercentageUpdate(0);
float current = 0;
float count = files.Length;
foreach (var file in files)
{ {
++count; args.PartPercentageUpdate(percent);
if (string.Equals(file.FullName, destFile, StringComparison.CurrentCultureIgnoreCase)) });
continue; // cant zip the zip we are creating
string relative = file.FullName[(dir.FullName.Length + 1)..];
try
{
arch.CreateEntryFromFile(file.FullName, relative, CompressionLevel.SmallestSize);
}
catch (Exception ex)
{
args.Logger?.WLog("Failed to add file to zip: " + file.FullName + " => " + ex.Message);
}
args?.PartPercentageUpdate((current / count) * 100);
}
args?.PartPercentageUpdate(100); args?.PartPercentageUpdate(100);
} }
else else
{ {
string localFile = args.FileService.GetLocalPath(args.WorkingFile); string localFile = args.FileService.GetLocalPath(args.WorkingFile);
using FileStream fs = new FileStream(tempZip, FileMode.Create); args.ArchiveHelper.Compress(localFile, tempZip);
using ZipArchive arch = new ZipArchive(fs, ZipArchiveMode.Create);
arch.CreateEntryFromFile(localFile, FileHelper.GetShortFileName(args.LibraryFileName));
} }
if (System.IO.File.Exists(tempZip) == false) if (System.IO.File.Exists(tempZip) == false)

View File

@@ -13,6 +13,7 @@
<Product>Checksum</Product> <Product>Checksum</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Nodes that provide the ability to run a checksum against a file.</Description> <Description>Nodes that provide the ability to run a checksum against a file.</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug'"> <ItemGroup Condition=" '$(Configuration)' == 'Debug'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />

View File

@@ -11,7 +11,7 @@ namespace ChecksumNodes.Tests
[TestMethod] [TestMethod]
public void Checksum_MD5_Large() public void Checksum_MD5_Large()
{ {
var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "", null); var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "", null!);
var output = new MD5().Execute(args); var output = new MD5().Execute(args);
Assert.IsTrue(args.Variables.ContainsKey("MD5")); Assert.IsTrue(args.Variables.ContainsKey("MD5"));
Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["MD5"] as string)); Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["MD5"] as string));
@@ -20,7 +20,7 @@ namespace ChecksumNodes.Tests
[TestMethod] [TestMethod]
public void Checksum_SHA1_Large() public void Checksum_SHA1_Large()
{ {
var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "", null); var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "", null!);
var output = new SHA1().Execute(args); var output = new SHA1().Execute(args);
Assert.IsTrue(args.Variables.ContainsKey("SHA1")); Assert.IsTrue(args.Variables.ContainsKey("SHA1"));
Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA1"] as string)); Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA1"] as string));
@@ -29,7 +29,7 @@ namespace ChecksumNodes.Tests
[TestMethod] [TestMethod]
public void Checksum_SHA256_Large() public void Checksum_SHA256_Large()
{ {
var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "", null); var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "", null!);
var output = new SHA256().Execute(args); var output = new SHA256().Execute(args);
Assert.IsTrue(args.Variables.ContainsKey("SHA256")); Assert.IsTrue(args.Variables.ContainsKey("SHA256"));
Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA256"] as string)); Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA256"] as string));
@@ -38,7 +38,7 @@ namespace ChecksumNodes.Tests
[TestMethod] [TestMethod]
public void Checksum_SHA512_Large() public void Checksum_SHA512_Large()
{ {
var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "", null); var args = new NodeParameters(@"D:\videos\Injustice.mkv", new TestLogger(), false, "", null!);
var output = new SHA512().Execute(args); var output = new SHA512().Execute(args);
Assert.IsTrue(args.Variables.ContainsKey("SHA512")); Assert.IsTrue(args.Variables.ContainsKey("SHA512"));
Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA512"] as string)); Assert.IsFalse(string.IsNullOrWhiteSpace(args.Variables["SHA512"] as string));

View File

@@ -23,11 +23,13 @@ namespace ChecksumNodes.Tests
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }
public bool Contains(string message) public bool Contains(string message)

View File

@@ -24,9 +24,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Docnet.Core" Version="2.6.0" /> <PackageReference Include="Docnet.Core" Version="2.6.0" />
<PackageReference Include="SharpCompress" Version="0.37.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FileFlows.Plugin"> <Reference Include="FileFlows.Plugin">

View File

@@ -111,6 +111,15 @@ public class ComicConverter: Node
/// <inheritdoc /> /// <inheritdoc />
public override int Execute(NodeParameters args) public override int Execute(NodeParameters args)
{ {
var localFileResult = args.FileService.GetLocalPath(args.WorkingFile);
if (localFileResult.Failed(out var error))
{
args.FailureReason = "Failed getting local file: " + error;
args.Logger?.ELog(args.FailureReason);
return -1;
}
var localFile = localFileResult.Value;
string currentFormat = new FileInfo(args.WorkingFile).Extension; string currentFormat = new FileInfo(args.WorkingFile).Extension;
if (string.IsNullOrEmpty(currentFormat)) if (string.IsNullOrEmpty(currentFormat))
{ {
@@ -123,9 +132,12 @@ public class ComicConverter: Node
currentFormat = currentFormat[1..]; // remove the dot currentFormat = currentFormat[1..]; // remove the dot
currentFormat = currentFormat.ToUpper(); currentFormat = currentFormat.ToUpper();
args.Logger?.ILog("Current Format: " + currentFormat);
args.Logger?.ILog("Desired Format: " + Format);
var metadata = new Dictionary<string, object>(); var metadata = new Dictionary<string, object>();
int pageCount = GetPageCount(args, currentFormat, args.WorkingFile); int pageCount = GetPageCount(args, currentFormat, localFile);
args.Logger?.ILog("Page Count: " + pageCount);
metadata.Add("Format", currentFormat); metadata.Add("Format", currentFormat);
metadata.Add("Pages", pageCount); metadata.Add("Pages", pageCount);
args.RecordStatisticRunningTotals("COMIC_FORMAT", currentFormat); args.RecordStatisticRunningTotals("COMIC_FORMAT", currentFormat);
@@ -143,8 +155,14 @@ public class ComicConverter: Node
string destinationPath = Path.Combine(args.TempPath, Guid.NewGuid().ToString()); string destinationPath = Path.Combine(args.TempPath, Guid.NewGuid().ToString());
Directory.CreateDirectory(destinationPath); Directory.CreateDirectory(destinationPath);
if (Helpers.ComicExtractor.Extract(args, args.WorkingFile, destinationPath, halfProgress: true, cancellation: cancellation.Token) == false) if (Helpers.ComicExtractor
.Extract(args, localFile, destinationPath, halfProgress: true, cancellation: cancellation.Token)
.Failed(out error))
{
args.FailureReason = "Failed to extract comic: " + error;
args.Logger?.ELog(args.FailureReason);
return -1; return -1;
}
if (EnsureTopDirectory) if (EnsureTopDirectory)
{ {
@@ -186,19 +204,7 @@ public class ComicConverter: Node
File.Delete(file); File.Delete(file);
continue; continue;
} }
// if (file.ToLowerInvariant().EndsWith(".jp2"))
// {
// // special case
// if (ConvertJp2ToJpeg(args.Logger, ref file) == false)
// {
// args.FailureReason =
// "JPEG 2000 File detected which is only supported if ImageMagick is installed.";
// args.Logger?.ELog(args.FailureReason);
// break;
// }
// }
if (rgxImages.IsMatch(file) == false) if (rgxImages.IsMatch(file) == false)
continue; continue;
@@ -249,60 +255,6 @@ public class ComicConverter: Node
return 1; return 1;
} }
/// <summary>
/// Tries to use imagemagick to convert an image to JPEG
/// </summary>
/// <param name="logger">The logger</param>
/// <param name="file">the file to convert</param>
/// <returns>true if converted</returns>
private bool ConvertJp2ToJpeg(ILogger logger, ref string file)
{
try
{
string outFile = Path.ChangeExtension(file, "jpg");
// Start the ImageMagick convert process
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "convert", // ImageMagick's convert command
ArgumentList = { file, outFile },
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using Process process = Process.Start(startInfo);
// Capture output
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
process.WaitForExit();
// Check for errors
if (string.IsNullOrEmpty(error) == false)
{
logger?.ELog($"Error occurred: {error}");
return false;
}
logger?.ILog("Converted JPEG 2000 File: " + file);
File.Delete(file);
file = outFile;
// Conversion successful
return true;
}
catch (Exception ex)
{
logger.ELog($"An error occurred converted the JPEG 2000 image: {ex.Message}");
return false;
}
}
/// <summary> /// <summary>
/// Cancels the conversion /// Cancels the conversion
/// </summary> /// </summary>
@@ -318,9 +270,9 @@ public class ComicConverter: Node
/// </summary> /// </summary>
/// <param name="args">the node parameters</param> /// <param name="args">the node parameters</param>
/// <param name="format">the format</param> /// <param name="format">the format</param>
/// <param name="workingFile">the working file to get the page count for</param> /// <param name="file">the file to get the page count for</param>
/// <returns>the number of pages</returns> /// <returns>the number of pages</returns>
private int GetPageCount(NodeParameters args, string format, string workingFile) private int GetPageCount(NodeParameters args, string format, string file)
{ {
if (format == null) if (format == null)
return 0; return 0;
@@ -328,9 +280,9 @@ public class ComicConverter: Node
switch (format) switch (format)
{ {
case "PDF": case "PDF":
return Helpers.PdfHelper.GetPageCount(workingFile); return Helpers.PdfHelper.GetPageCount(file);
default: default:
return Helpers.GenericExtractor.GetImageCount(args, workingFile); return args.ArchiveHelper.GetFileCount(file,@"\.(jpeg|jpg|jpe|jp2|png|bmp|tiff|webp|gif)$");
} }
} }
@@ -347,7 +299,7 @@ public class ComicConverter: Node
string file = Path.Combine(args.TempPath, Guid.NewGuid() + "." + format.ToLower()); string file = Path.Combine(args.TempPath, Guid.NewGuid() + "." + format.ToLower());
args.Logger?.ILog("Creating comic: " + file); args.Logger?.ILog("Creating comic: " + file);
if (format == "CBZ") if (format == "CBZ")
Helpers.ZipHelper.Compress(args, directory, file); args.ArchiveHelper.Compress(directory, file);
//else if (format == "CB7") //else if (format == "CB7")
// Helpers.SevenZipHelper.Compress(args, directory, file + ".7z"); // Helpers.SevenZipHelper.Compress(args, directory, file + ".7z");
else if (format == "PDF") else if (format == "PDF")

View File

@@ -1,6 +1,4 @@
using System.Text.RegularExpressions; namespace FileFlows.ComicNodes.Comics;
namespace FileFlows.ComicNodes.Comics;
/// <summary> /// <summary>
/// Extracts a comic /// Extracts a comic
@@ -20,6 +18,9 @@ public class ComicExtractor : Node
CancellationTokenSource cancellation = new CancellationTokenSource(); CancellationTokenSource cancellation = new CancellationTokenSource();
/// <summary>
/// Gets or sets the destination to extract the comic into
/// </summary>
[Required] [Required]
[Folder(1)] [Folder(1)]
public string DestinationPath { get; set; } public string DestinationPath { get; set; }
@@ -32,20 +33,33 @@ public class ComicExtractor : Node
dest = dest.Replace("/", Path.DirectorySeparatorChar.ToString()); dest = dest.Replace("/", Path.DirectorySeparatorChar.ToString());
if (string.IsNullOrEmpty(dest)) if (string.IsNullOrEmpty(dest))
{ {
args.Logger?.ELog("No destination specified"); args.FailureReason = "No destination specified";
args.Result = NodeResult.Failure; args.Logger?.ELog(args.FailureReason);
return -1;
}
if (Helpers.ComicExtractor
.Extract(args, args.WorkingFile, dest, halfProgress: false, cancellation: cancellation.Token)
.Failed(out string error))
{
args.FailureReason = "Failed to extract comic: " + error;
args.Logger?.ELog(args.FailureReason);
return -1; return -1;
} }
Helpers.ComicExtractor.Extract(args, args.WorkingFile, dest, halfProgress: false, cancellation: cancellation.Token);
var metadata = new Dictionary<string, object>(); var metadata = new Dictionary<string, object>();
metadata.Add("Format", args.WorkingFile.Substring(args.WorkingFile.LastIndexOf(".") + 1).ToUpper()); metadata.Add("Format", args.WorkingFile[(args.WorkingFile.LastIndexOf(".", StringComparison.Ordinal) + 1)..].ToUpper());
var rgxImages = new Regex(@"\.(jpeg|jpg|jpe|png|bmp|tiff|webp|gif)$"); var rgxImages = new Regex(@"\.(jpeg|jpg|jpe|png|bmp|tiff|webp|gif)$");
metadata.Add("Pages", Directory.GetFiles(dest, "*.*", SearchOption.AllDirectories).Where(x => rgxImages.IsMatch(x)).Count()); metadata.Add("Pages", Directory.GetFiles(dest, "*.*", SearchOption.AllDirectories).Count(x => rgxImages.IsMatch(x)));
args.SetMetadata(metadata); args.SetMetadata(metadata);
return 1; return 1;
} }
/// <summary>
/// Cancels the extraction
/// </summary>
/// <returns>the task to await</returns>
public override Task Cancel() public override Task Cancel()
{ {
cancellation.Cancel(); cancellation.Cancel();

View File

@@ -43,7 +43,7 @@ public class CreateComicInfo : Node
var localFile = localFileResult.Value; var localFile = localFileResult.Value;
var result = ZipHelper.FileExists(localFile, "comicinfo.xml"); var result = args.ArchiveHelper.FileExists(localFile, "comicinfo.xml");
if (result.Failed(out error)) if (result.Failed(out error))
{ {
args.FailureReason = error; args.FailureReason = error;
@@ -107,7 +107,7 @@ public class CreateComicInfo : Node
args.Logger?.ILog("comicinfo.xml created: " + comicInfoFile); args.Logger?.ILog("comicinfo.xml created: " + comicInfoFile);
File.WriteAllText(comicInfoFile, xml); File.WriteAllText(comicInfoFile, xml);
if (ZipHelper.AddToArchive(localFile, comicInfoFile).Failed(out error)) if (args.ArchiveHelper.AddToArchive(localFile, comicInfoFile).Failed(out error))
{ {
args.FailureReason = error; args.FailureReason = error;
args.Logger?.ELog(error); args.Logger?.ELog(error);

View File

@@ -8,15 +8,21 @@ namespace FileFlows.ComicNodes.Helpers;
internal class ComicExtractor internal class ComicExtractor
{ {
internal static bool Extract(NodeParameters args, string file, string destinationPath, bool halfProgress, CancellationToken cancellation) /// <summary>
/// Extracts a comic book to a given folder
/// </summary>
/// <param name="args">the Node Parameters</param>
/// <param name="file">the file to extract</param>
/// <param name="destinationPath">the destination to extract files into, this should be an empty path</param>
/// <param name="halfProgress">if the progress should be halfed when reporting</param>
/// <param name="cancellation">the cancelation token</param>
/// <returns>true is successfully extracted the comic, otherwise false</returns>
internal static Result<bool> Extract(NodeParameters args, string file, string destinationPath, bool halfProgress, CancellationToken cancellation)
{ {
string currentFormat = new FileInfo(file).Extension;
string currentFormat = new FileInfo(args.WorkingFile).Extension;
if (string.IsNullOrEmpty(currentFormat)) if (string.IsNullOrEmpty(currentFormat))
{ return Result<bool>.Fail("Could not detect format for: " + file);
args.Logger?.ELog("Could not detect format for: " + args.WorkingFile);
return false;
}
if (currentFormat[0] == '.') if (currentFormat[0] == '.')
currentFormat = currentFormat[1..]; // remove the dot currentFormat = currentFormat[1..]; // remove the dot
currentFormat = currentFormat.ToUpper(); currentFormat = currentFormat.ToUpper();
@@ -25,13 +31,16 @@ internal class ComicExtractor
args.Logger?.ILog("Extracting comic pages to: " + destinationPath); args.Logger?.ILog("Extracting comic pages to: " + destinationPath);
if (currentFormat == "PDF") if (currentFormat == "PDF")
PdfHelper.Extract(args, args.WorkingFile, destinationPath, "page", halfProgress: halfProgress, cancellation: cancellation); PdfHelper.Extract(args, file, destinationPath, "page", halfProgress: halfProgress, cancellation: cancellation);
else if (currentFormat == "CBZ") else if (currentFormat is "CBZ" or "CB7" or "CBR" or "GZ" or "BZ2")
ZipHelper.Extract(args, args.WorkingFile, destinationPath, halfProgress: halfProgress); return args.ArchiveHelper.Extract(file, destinationPath, (percent) =>
else if (currentFormat == "CB7" || currentFormat == "CBR" || currentFormat == "GZ" || currentFormat == "BZ2") {
GenericExtractor.Extract(args, args.WorkingFile, destinationPath, halfProgress: halfProgress); if (halfProgress)
percent /= 2;
args.PartPercentageUpdate?.Invoke(percent);
});
else else
throw new Exception("Unknown format:" + currentFormat); return Result<bool>.Fail("Unknown format:" + currentFormat);
return true; return true;
} }

View File

@@ -1,52 +1,52 @@
using SharpCompress.Archives; // using SharpCompress.Archives;
using System; // using System;
using System.IO.Compression; // using System.IO.Compression;
using System.Text.RegularExpressions; // using System.Text.RegularExpressions;
//
namespace FileFlows.ComicNodes.Helpers; // namespace FileFlows.ComicNodes.Helpers;
//
internal class GenericExtractor // internal class GenericExtractor
{ // {
/// <summary> // /// <summary>
/// Uncompresses a folder // /// Uncompresses a folder
/// </summary> // /// </summary>
/// <param name="args">the node parameters</param> // /// <param name="args">the node parameters</param>
/// <param name="workingFile">the file to extract</param> // /// <param name="workingFile">the file to extract</param>
/// <param name="destinationPath">the location to extract to</param> // /// <param name="destinationPath">the location to extract to</param>
/// <param name="halfProgress">if the NodeParameter.PartPercentageUpdate should end at 50%</param> // /// <param name="halfProgress">if the NodeParameter.PartPercentageUpdate should end at 50%</param>
internal static void Extract(NodeParameters args, string workingFile, string destinationPath, bool halfProgress = true) // internal static void Extract(NodeParameters args, string workingFile, string destinationPath, bool halfProgress = true)
{ // {
if (args?.PartPercentageUpdate != null) // if (args?.PartPercentageUpdate != null)
args?.PartPercentageUpdate(halfProgress ? 50 : 0); // args?.PartPercentageUpdate(halfProgress ? 50 : 0);
//
bool isRar = workingFile.ToLowerInvariant().EndsWith(".cbr"); // bool isRar = workingFile.ToLowerInvariant().EndsWith(".cbr");
try // try
{ // {
ArchiveFactory.WriteToDirectory(workingFile, destinationPath); // ArchiveFactory.WriteToDirectory(workingFile, destinationPath);
PageNameHelper.FixPageNames(destinationPath); // PageNameHelper.FixPageNames(destinationPath);
} // }
catch (Exception ex) when (isRar && ex.Message.Contains("Unknown Rar Header")) // catch (Exception ex) when (isRar && ex.Message.Contains("Unknown Rar Header"))
{ // {
UnrarCommandLine.Extract(args, workingFile, destinationPath, halfProgress: halfProgress); // UnrarCommandLine.Extract(args, workingFile, destinationPath, halfProgress: halfProgress);
} // }
//
if (args?.PartPercentageUpdate != null) // if (args?.PartPercentageUpdate != null)
args?.PartPercentageUpdate(halfProgress ? 50 : 100); // args?.PartPercentageUpdate(halfProgress ? 50 : 100);
} // }
//
internal static int GetImageCount(NodeParameters args, string workingFile) // internal static int GetImageCount(NodeParameters args, string workingFile)
{ // {
bool isRar = workingFile.ToLowerInvariant().EndsWith(".cbr"); // bool isRar = workingFile.ToLowerInvariant().EndsWith(".cbr");
try // try
{ // {
var rgxImages = new Regex(@"\.(jpeg|jpg|jpe|png|bmp|tiff|webp|gif)$", RegexOptions.IgnoreCase); // var rgxImages = new Regex(@"\.(jpeg|jpg|jpe|png|bmp|tiff|webp|gif)$", RegexOptions.IgnoreCase);
using var archive = ArchiveFactory.Open(workingFile); // using var archive = ArchiveFactory.Open(workingFile);
var files = archive.Entries.Where(entry => !entry.IsDirectory).ToArray(); // var files = archive.Entries.Where(entry => !entry.IsDirectory).ToArray();
return files.Count(x => x.Key != null && rgxImages.IsMatch(x.Key)); // return files.Count(x => x.Key != null && rgxImages.IsMatch(x.Key));
} // }
catch(Exception ex) when (isRar && ex.Message.Contains("Unknown Rar Header")) // catch(Exception ex) when (isRar && ex.Message.Contains("Unknown Rar Header"))
{ // {
return UnrarCommandLine.GetImageCount(args, workingFile); // return UnrarCommandLine.GetImageCount(args, workingFile);
} // }
} // }
} // }

View File

@@ -1,15 +1,6 @@
using Docnet.Core; using Docnet.Core;
using Docnet.Core.Editors; using Docnet.Core.Editors;
using Docnet.Core.Models; using Docnet.Core.Models;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.PixelFormats;
namespace FileFlows.ComicNodes.Helpers; namespace FileFlows.ComicNodes.Helpers;
@@ -29,31 +20,13 @@ internal class PdfHelper
using var pageReader = docReader.GetPageReader(i); using var pageReader = docReader.GetPageReader(i);
var rawBytes = pageReader.GetImage(); var rawBytes = pageReader.GetImage();
var width = pageReader.GetPageWidth(); var file = Path.Combine(destinationDirectory,
var height = pageReader.GetPageHeight(); filePrefix + "-" + i.ToString(new string('0', pageCount.ToString().Length)));
var result = args.ImageHelper.SaveImage(rawBytes, file);
using var image = Image.LoadPixelData<Bgra32>(rawBytes, width, height); if (result.Failed(out string error))
// Infer the image format
(IImageFormat? imageFormat, string? fileExtension) = InferImageFormat(rawBytes);
if (imageFormat == null)
{ {
args?.Logger?.WLog("Failed to inter image type from PDF, failing back to JPG"); args.Logger?.WLog("Failed to save image: " + error);
imageFormat = JpegFormat.Instance; continue;
fileExtension = "jpg";
}
else
{
args?.Logger?.ILog("File Extension of image: " + fileExtension);
}
var file = Path.Combine(destinationDirectory,
filePrefix + "-" + i.ToString(new string('0', pageCount.ToString().Length)))
+ "." + fileExtension;
using (var outputStream = File.Create(file + "." + fileExtension))
{
image.Save(outputStream, imageFormat);
} }
if (args?.PartPercentageUpdate != null) if (args?.PartPercentageUpdate != null)
@@ -70,42 +43,6 @@ internal class PdfHelper
args?.PartPercentageUpdate(halfProgress ? 50 : 0); args?.PartPercentageUpdate(halfProgress ? 50 : 0);
} }
/// <summary>
/// Infers the image format based on the first few bytes of the image data.
/// </summary>
/// <param name="bytes">The image data bytes.</param>
/// <returns>The inferred image format and file extension.</returns>
private static (IImageFormat? Format, string Extension) InferImageFormat(byte[] bytes)
{
// Try to infer image format based on magic numbers
if (bytes.Length >= 2 && bytes[0] == 0xFF && bytes[1] == 0xD8) // JPEG
return (JpegFormat.Instance, "jpg");
if (bytes.Length >= 8 && BitConverter.ToUInt64(bytes, 0) == 0x89504E470D0A1A0A) // PNG
return (PngFormat.Instance, "png");
if (bytes.Length >= 4 && bytes[0] == 0x47 && bytes[1] == 0x49 && bytes[2] == 0x46 && bytes[3] == 0x38) // GIF
return (GifFormat.Instance, "gif");
if (bytes.Length >= 4 && bytes[0] == 0x52 && bytes[1] == 0x49 && bytes[2] == 0x46 && bytes[3] == 0x46 &&
bytes[8] == 0x57 && bytes[9] == 0x45 && bytes[10] == 0x42 && bytes[11] == 0x50) // WebP
return (WebpFormat.Instance, "webp");
if (bytes.Length >= 4 && BitConverter.ToUInt32(bytes, 0) == 0x49492A00) // TIFF
return (TiffFormat.Instance, "tiff");
if (bytes.Length >= 2 && bytes[0] == 0x42 && bytes[1] == 0x4D) // BMP
return (BmpFormat.Instance, "bmp");
// If none of the known formats are detected, fall back to Image.DetectFormat()
try
{
IImageFormat format = Image.DetectFormat(bytes);
string extension = format?.DefaultMimeType?.Split('/')[1] ?? "png";
return (format, extension);
}
catch (Exception)
{
return (null, null);
}
}
/// <summary> /// <summary>
/// Creates a PDF from images /// Creates a PDF from images
/// </summary> /// </summary>
@@ -124,45 +61,23 @@ internal class PdfHelper
for(int i = 0; i < files.Length; i++) for(int i = 0; i < files.Length; i++)
{ {
var file = files[i]; var file = files[i];
var format = Image.DetectFormat(file); if (file.ToLowerInvariant().EndsWith(".png") || file.ToLowerInvariant().EndsWith(".webp"))
var info = Image.Identify(file);
if (file.ToLower().EndsWith(".png"))
{ {
var img = Image.Load(file); string jpegImage = Path.ChangeExtension(file, "jpg");
using var memoryStream = new MemoryStream(); args.ImageHelper.ConvertToJpeg(file, jpegImage, null);
img.SaveAsJpeg(memoryStream); file = jpegImage;
var jpeg = new JpegImage
{
Bytes = memoryStream.ToArray(),
Width = info.Width,
Height = info.Height
};
images.Add(jpeg);
} }
else if(file.ToLower().EndsWith(".webp"))
{
var img = Image.Load(file);
using var memoryStream = new MemoryStream();
img.SaveAsJpeg(memoryStream);
var jpeg = new JpegImage
{
Bytes = memoryStream.ToArray(),
Width = info.Width,
Height = info.Height
};
images.Add(jpeg);
}
else // jpeg
{
var jpeg = new JpegImage
{
Bytes = File.ReadAllBytes(file),
Width = info.Width,
Height = info.Height
};
images.Add(jpeg);
} (int width, int height) = args.ImageHelper.GetDimensions(file).Value;
var jpeg = new JpegImage
{
Bytes = File.ReadAllBytes(file),
Width = width,
Height = height
};
images.Add(jpeg);
if (args?.PartPercentageUpdate != null) if (args?.PartPercentageUpdate != null)
{ {
float percent = (i / ((float)files.Length)) * 100f; float percent = (i / ((float)files.Length)) * 100f;

View File

@@ -1,116 +1,116 @@
using System; // using System;
using System.IO.Compression; // using System.IO.Compression;
//
namespace FileFlows.ComicNodes.Helpers; // namespace FileFlows.ComicNodes.Helpers;
//
internal class ZipHelper // internal class ZipHelper
{ // {
/// <summary> // /// <summary>
/// Zips a folder to a file // /// Zips a folder to a file
/// </summary> // /// </summary>
/// <param name="args">the NodeParameters</param> // /// <param name="args">the NodeParameters</param>
/// <param name="directory">the directory to zip</param> // /// <param name="directory">the directory to zip</param>
/// <param name="output">the output file of the zip</param> // /// <param name="output">the output file of the zip</param>
/// <param name="pattern">the file pattern to include in the zip</param> // /// <param name="pattern">the file pattern to include in the zip</param>
/// <param name="allDirectories">If all directories should be included or just the top most</param> // /// <param name="allDirectories">If all directories should be included or just the top most</param>
/// <param name="halfProgress">if the NodePArameter.PartPercentageUpdate should start at 50%</param> // /// <param name="halfProgress">if the NodePArameter.PartPercentageUpdate should start at 50%</param>
internal static void Compress(NodeParameters args, string directory, string output, string pattern = "*.*", bool allDirectories = false, bool halfProgress = true) // internal static void Compress(NodeParameters args, string directory, string output, string pattern = "*.*", bool allDirectories = false, bool halfProgress = true)
{ // {
//
var dir = new DirectoryInfo(directory); // var dir = new DirectoryInfo(directory);
var files = dir.GetFiles(pattern, allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); // var files = dir.GetFiles(pattern, allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
using FileStream fs = new FileStream(output, FileMode.Create); // using FileStream fs = new FileStream(output, FileMode.Create);
using ZipArchive arch = new ZipArchive(fs, ZipArchiveMode.Create); // using ZipArchive arch = new ZipArchive(fs, ZipArchiveMode.Create);
if(args?.PartPercentageUpdate != null) // if(args?.PartPercentageUpdate != null)
args?.PartPercentageUpdate(halfProgress ? 50 : 0); // args?.PartPercentageUpdate(halfProgress ? 50 : 0);
float current = 0; // float current = 0;
float count = files.Length; // float count = files.Length;
foreach (var file in files) // foreach (var file in files)
{ // {
++count; // ++count;
string relative = file.FullName.Substring(dir.FullName.Length + 1); // string relative = file.FullName.Substring(dir.FullName.Length + 1);
try // try
{ // {
arch.CreateEntryFromFile(file.FullName, relative, CompressionLevel.SmallestSize); // arch.CreateEntryFromFile(file.FullName, relative, CompressionLevel.SmallestSize);
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
args.Logger?.WLog("Failed to add file to zip: " + file.FullName + " => " + ex.Message); // args.Logger?.WLog("Failed to add file to zip: " + file.FullName + " => " + ex.Message);
} // }
if (args?.PartPercentageUpdate != null) // if (args?.PartPercentageUpdate != null)
{ // {
float percent = (current / count) * 100f; // float percent = (current / count) * 100f;
if (halfProgress) // if (halfProgress)
percent = 50 + (percent / 2); // percent = 50 + (percent / 2);
args?.PartPercentageUpdate(percent); // args?.PartPercentageUpdate(percent);
} // }
} // }
if (args?.PartPercentageUpdate != null) // if (args?.PartPercentageUpdate != null)
args?.PartPercentageUpdate(100); // args?.PartPercentageUpdate(100);
} // }
//
internal static void Extract(NodeParameters args, string workingFile, string destinationPath, bool halfProgress = true) // internal static void Extract(NodeParameters args, string workingFile, string destinationPath, bool halfProgress = true)
{ // {
if (args?.PartPercentageUpdate != null) // if (args?.PartPercentageUpdate != null)
args?.PartPercentageUpdate(halfProgress ? 50 : 0); // args?.PartPercentageUpdate(halfProgress ? 50 : 0);
//
ZipFile.ExtractToDirectory(workingFile, destinationPath); // ZipFile.ExtractToDirectory(workingFile, destinationPath);
PageNameHelper.FixPageNames(destinationPath); // PageNameHelper.FixPageNames(destinationPath);
//
if (args?.PartPercentageUpdate != null) // if (args?.PartPercentageUpdate != null)
args?.PartPercentageUpdate(halfProgress ? 50 : 100); // args?.PartPercentageUpdate(halfProgress ? 50 : 100);
} // }
//
/// <summary> // /// <summary>
/// Checks if a file exist in the archive // /// Checks if a file exist in the archive
/// </summary> // /// </summary>
/// <param name="archivePath">the path to the archive</param> // /// <param name="archivePath">the path to the archive</param>
/// <param name="file">the file to look for</param> // /// <param name="file">the file to look for</param>
/// <returns>true if exists, otherwise false</returns> // /// <returns>true if exists, otherwise false</returns>
public static Result<bool> FileExists(string archivePath, string file) // public static Result<bool> FileExists(string archivePath, string file)
{ // {
// Check if the archive file exists // // Check if the archive file exists
if (File.Exists(archivePath) == false) // if (File.Exists(archivePath) == false)
return Result<bool>.Fail("Archive file not found: " + archivePath); // return Result<bool>.Fail("Archive file not found: " + archivePath);
//
try // try
{ // {
// Open the zip archive // // Open the zip archive
using ZipArchive archive = ZipFile.OpenRead(archivePath); // using ZipArchive archive = ZipFile.OpenRead(archivePath);
return archive.Entries.Any(x => x.FullName.ToLowerInvariant().Equals(file.ToLowerInvariant())); // return archive.Entries.Any(x => x.FullName.ToLowerInvariant().Equals(file.ToLowerInvariant()));
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
return Result<bool>.Fail(ex.Message); // return Result<bool>.Fail(ex.Message);
} // }
} // }
//
/// <summary> // /// <summary>
/// Adds a file to the archive // /// Adds a file to the archive
/// </summary> // /// </summary>
/// <param name="archivePath">the path to the archive</param> // /// <param name="archivePath">the path to the archive</param>
/// <param name="file">the file to add</param> // /// <param name="file">the file to add</param>
/// <returns>true if successfully added</returns> // /// <returns>true if successfully added</returns>
public static Result<bool> AddToArchive(string archivePath, string file) // public static Result<bool> AddToArchive(string archivePath, string file)
{ // {
// Check if the archive file exists // // Check if the archive file exists
if (File.Exists(archivePath) == false) // if (File.Exists(archivePath) == false)
return Result<bool>.Fail("Archive file not found: " + archivePath); // return Result<bool>.Fail("Archive file not found: " + archivePath);
//
try // try
{ // {
// Open the zip archive // // Open the zip archive
using ZipArchive archive = ZipFile.Open(archivePath, ZipArchiveMode.Update); // using ZipArchive archive = ZipFile.Open(archivePath, ZipArchiveMode.Update);
//
// Create a new entry for the file // // Create a new entry for the file
archive.CreateEntryFromFile(file, Path.GetFileName(file)); // archive.CreateEntryFromFile(file, Path.GetFileName(file));
//
// Successfully added the file // // Successfully added the file
return Result<bool>.Success(true); // return Result<bool>.Success(true);
} // }
catch (Exception ex) // catch (Exception ex)
{ // {
return Result<bool>.Fail(ex.Message); // return Result<bool>.Fail(ex.Message);
} // }
} // }
} // }

View File

@@ -168,9 +168,9 @@ File shrunk in size by: {{ difference | file_size }} / {{ percent }}%
string url = $"https://discordapp.com/api/webhooks/{settings.WebhookId}/{settings.WebhookToken}"; string url = $"https://discordapp.com/api/webhooks/{settings.WebhookId}/{settings.WebhookToken}";
#pragma warning disable IL2026
var content = new StringContent(JsonSerializer.Serialize(webhook), Encoding.UTF8, "application/json"); var content = new StringContent(JsonSerializer.Serialize(webhook), Encoding.UTF8, "application/json");
#pragma warning restore IL2026
using var httpClient = new HttpClient(); using var httpClient = new HttpClient();
var response = httpClient.PostAsync(url, content).Result; var response = httpClient.PostAsync(url, content).Result;
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)

View File

@@ -14,6 +14,7 @@
<Product>Discord</Product> <Product>Discord</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Allows you to send messages to a Discord server.</Description> <Description>Allows you to send messages to a Discord server.</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="DiscordNodes.en.json" /> <None Remove="DiscordNodes.en.json" />

View File

@@ -11,7 +11,7 @@ public class DiscordTests
[TestMethod] [TestMethod]
public void Discord_Simple_Message() public void Discord_Simple_Message()
{ {
var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");
@@ -26,7 +26,7 @@ public class DiscordTests
[TestMethod] [TestMethod]
public void Discord_Basic_Message() public void Discord_Basic_Message()
{ {
var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -1,44 +1,58 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
using MailKit.Net.Smtp; using MailKit.Net.Smtp;
using MimeKit; using MimeKit;
namespace FileFlows.Communication namespace FileFlows.Communication;
/// <summary>
/// Sends an email
/// </summary>
public class SendEmail:Node
{ {
public class SendEmail:Node /// <inheritdoc />
public override int Inputs => 1;
/// <inheritdoc />
public override int Outputs => 2;
/// <inheritdoc />
public override string Icon => "fas fa-envelope";
/// <inheritdoc />
public override FlowElementType Type => FlowElementType.Communication;
/// <inheritdoc />
public override bool FailureNode => true;
/// <summary>
/// Gets or sets the recipients
/// </summary>
[StringArray(1)] public string[]? Recipients { get; set; }
/// <summary>
/// Gets or sets the subject
/// </summary>
[TextVariable(2)] public string Subject { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the message
/// </summary>
[Required]
[Template(3, nameof(BodyTemplates))]
public string Body { get; set; } = string.Empty;
private static List<ListOption>? _BodyTemplates;
/// <summary>
/// Gets the body template options
/// </summary>
public static List<ListOption> BodyTemplates
{ {
public override int Inputs => 1; get
public override int Outputs => 2;
public override string Icon => "fas fa-envelope";
public override FlowElementType Type => FlowElementType.Communication;
public override bool FailureNode => true;
[StringArray(1)]
public string[] Recipients { get; set; }
[TextVariable(2)]
public string Subject { get; set; }
/// <summary>
/// Gets or sets the message
/// </summary>
[Required]
[Template(3, nameof(BodyTemplates))]
public string Body { get; set; }
private static List<ListOption> _BodyTemplates;
public static List<ListOption> BodyTemplates
{ {
get if (_BodyTemplates == null)
{ {
if (_BodyTemplates == null) _BodyTemplates = new List<ListOption>
{ {
_BodyTemplates = new List<ListOption> new () { Label = "Basic", Value = @"File: {{ file.Orig.FullName }}
{
new () { Label = "Basic", Value = @"File: {{ file.Orig.FullName }}
Size: {{ file.Size }}" }, Size: {{ file.Size }}" },
new () { Label = "File Size Changes", Value = @" new () { Label = "File Size Changes", Value = @"
{{ difference = file.Size - file.Orig.Size }} {{ difference = file.Size - file.Orig.Size }}
{{ percent = (difference / file.Orig.Size) * 100 | math.round 2 }} {{ percent = (difference / file.Orig.Size) * 100 | math.round 2 }}
@@ -52,110 +66,93 @@ File grew in size: {{ difference | math.abs | file_size }}
{{ else }} {{ else }}
File shrunk in size by: {{ difference | file_size }} / {{ percent }}% File shrunk in size by: {{ difference | file_size }} / {{ percent }}%
{{ end }}"} {{ end }}"}
}; };
}
return _BodyTemplates;
} }
return _BodyTemplates;
} }
}
public override int Execute(NodeParameters args) /// <inheritdoc />
public override int Execute(NodeParameters args)
{
try
{ {
try var settings = args.GetPluginSettings<PluginSettings>();
if (string.IsNullOrEmpty(settings?.SmtpServer))
{ {
var settings = args.GetPluginSettings<PluginSettings>(); args.Logger?.ELog(
"No SMTP Server configured, configure this under the 'Plugins > Email > Edit' page.");
if (string.IsNullOrEmpty(settings?.SmtpServer)) return -1;
{
args.Logger?.ELog(
"No SMTP Server configured, configure this under the 'Plugins > Email > Edit' page.");
return -1;
}
args.Logger?.ILog($"Got SMTP Server: {settings.SmtpServer} [{settings.SmtpPort}]");
string body = RenderBody(args);
string sender = settings.Sender ?? "fileflows@" + Environment.MachineName;
string subject = args.ReplaceVariables(this.Subject ?? String.Empty)?.EmptyAsNull() ??
"Email from FileFlows";
SendMailKit(args, settings, sender, subject, body);
//SendDotNet(args, settings, sender, subject, body);
return 1;
}
catch (Exception ex)
{
args.Logger?.WLog("Error sending message: " + ex.Message);
return 2;
} }
args.Logger?.ILog($"Got SMTP Server: {settings.SmtpServer} [{settings.SmtpPort}]");
string body = RenderBody(args);
string sender = settings.Sender ?? "fileflows@" + Environment.MachineName;
string subject = args.ReplaceVariables(this.Subject ?? String.Empty)?.EmptyAsNull() ??
"Email from FileFlows";
SendMailKit(args, settings, sender, subject, body);
//SendDotNet(args, settings, sender, subject, body);
return 1;
} }
catch (Exception ex)
internal string RenderBody(NodeParameters args)
{ {
if (string.IsNullOrEmpty(this.Body)) args.Logger?.WLog("Error sending message: " + ex.Message);
return string.Empty; return 2;
return args.RenderTemplate!(this.Body);
} }
}
private void SendDotNet(NodeParameters args, PluginSettings settings, string sender, string subject, string body) /// <summary>
/// Renders the body of the email
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>the rendered body</returns>
internal string RenderBody(NodeParameters args)
{
if (string.IsNullOrEmpty(this.Body))
return string.Empty;
return args.RenderTemplate!(this.Body);
}
private void SendMailKit(NodeParameters args, PluginSettings settings, string sender, string subject, string body)
{
var message = new MimeMessage();
message.From.Add(new MailboxAddress(sender, sender));
if (Recipients?.Any() != true)
{ {
args.Logger?.ILog($"Send using .NET internal mail library"); args.Logger?.ELog("No recipients");
System.Net.Mail.MailMessage message = new (); return;
message.From = new System.Net.Mail.MailAddress(sender); }
foreach (var recipient in Recipients) foreach (var recipient in Recipients)
message.To.Add(recipient); message.To.Add(new MailboxAddress(recipient, recipient));
message.Subject = subject; message.Subject = subject;
message.Body = args.ReplaceVariables(body); message.Body = new TextPart("plain")
{
Text = body
};
args.Logger?.ILog($"About to construct SmtpClient");
using (var client = new SmtpClient())
{
args.Logger?.ILog($"Connecting to SMTP Server: {settings.SmtpServer}:{settings.SmtpPort}");
client.Connect(settings.SmtpServer, settings.SmtpPort);
System.Net.Mail.SmtpClient smtp = new ();
smtp.Port = settings.SmtpPort;
smtp.Host = settings.SmtpServer;
if (string.IsNullOrEmpty(settings.SmtpUsername) == false) if (string.IsNullOrEmpty(settings.SmtpUsername) == false)
{ {
args.Logger?.ILog("Sending using credientials"); args.Logger?.ILog("Sending using credientials");
smtp.EnableSsl = true; client.AuthenticationMechanisms.Remove("XOAUTH2");
smtp.UseDefaultCredentials = false; client.Authenticate(settings.SmtpUsername, settings.SmtpPassword);
smtp.Credentials = new System.Net.NetworkCredential(settings.SmtpUsername, settings.SmtpPassword);
//smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
}
args.Logger?.ILog("About to send email");
smtp.Send(message);
args.Logger?.ILog("Email sent!");
}
private void SendMailKit(NodeParameters args, PluginSettings settings, string sender, string subject, string body)
{
var message = new MimeMessage();
message.From.Add(new MailboxAddress(sender, sender));
foreach (var recipient in Recipients)
message.To.Add(new MailboxAddress(recipient, recipient));
message.Subject = subject;
message.Body = new TextPart("plain")
{
Text = body
};
args.Logger?.ILog($"About to construct SmtpClient");
using (var client = new SmtpClient())
{
args.Logger?.ILog($"Connecting to SMTP Server: {settings.SmtpServer}:{settings.SmtpPort}");
client.Connect(settings.SmtpServer, settings.SmtpPort);
if (string.IsNullOrEmpty(settings.SmtpUsername) == false)
{
args.Logger?.ILog("Sending using credientials");
client.AuthenticationMechanisms.Remove("XOAUTH2");
client.Authenticate(settings.SmtpUsername, settings.SmtpPassword);
}
args.Logger?.ILog($"About to send message");
client.Send(message);
args.Logger?.ILog($"Message sent");
client.Disconnect(true);
} }
args.Logger?.ILog($"About to send message");
client.Send(message);
args.Logger?.ILog($"Message sent");
client.Disconnect(true);
} }
} }
} }

View File

@@ -16,6 +16,7 @@
<Product>Email</Product> <Product>Email</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>This plugin allows you to send an email while executing a Flow.</Description> <Description>This plugin allows you to send an email while executing a Flow.</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug'"> <ItemGroup Condition=" '$(Configuration)' == 'Debug'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />

View File

@@ -18,7 +18,7 @@ namespace EmailNodes.Tests
public void Email_TemplateTest() public void Email_TemplateTest()
{ {
const string file = @"D:\music\unprocessed\04-billy_joel-scenes_from_an_italian_restaurant-b2125758.mp3"; const string file = @"D:\music\unprocessed\04-billy_joel-scenes_from_an_italian_restaurant-b2125758.mp3";
var args = new NodeParameters(file, new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(file, new TestLogger(), false, string.Empty, null!);
string test = Guid.NewGuid().ToString("N"); string test = Guid.NewGuid().ToString("N");
args.Variables.Add("TestParameter", test); args.Variables.Add("TestParameter", test);
var node = new SendEmail(); var node = new SendEmail();
@@ -31,7 +31,7 @@ namespace EmailNodes.Tests
public void Email_TemplateTest2() public void Email_TemplateTest2()
{ {
const string file = @"D:\music\unprocessed\04-billy_joel-scenes_from_an_italian_restaurant-b2125758.mp3"; const string file = @"D:\music\unprocessed\04-billy_joel-scenes_from_an_italian_restaurant-b2125758.mp3";
var args = new NodeParameters(file, new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(file, new TestLogger(), false, string.Empty, null!);
string test = Guid.NewGuid().ToString("N"); string test = Guid.NewGuid().ToString("N");
args.Variables.Add("TestParameter", test); args.Variables.Add("TestParameter", test);
var node = new SendEmail(); var node = new SendEmail();
@@ -43,7 +43,7 @@ namespace EmailNodes.Tests
public void Email_TemplateTest3() public void Email_TemplateTest3()
{ {
const string file = @"D:\music\unprocessed\04-billy_joel-scenes_from_an_italian_restaurant-b2125758.mp3"; const string file = @"D:\music\unprocessed\04-billy_joel-scenes_from_an_italian_restaurant-b2125758.mp3";
var args = new NodeParameters(file, new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(file, new TestLogger(), false, string.Empty, null!);
string test = Guid.NewGuid().ToString("N"); string test = Guid.NewGuid().ToString("N");
args.Variables.Add("TestParameter", test); args.Variables.Add("TestParameter", test);
var node = new SendEmail(); var node = new SendEmail();

View File

@@ -24,11 +24,13 @@ namespace EmailNodes.Tests
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -14,6 +14,7 @@
<Product>Emby</Product> <Product>Emby</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Emby plugin that lets you update Emby with any changes</Description> <Description>Emby plugin that lets you update Emby with any changes</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="Emby.en.json" /> <None Remove="Emby.en.json" />

View File

@@ -11,7 +11,7 @@ public class EmbyTests
[TestMethod] [TestMethod]
public void Emby_Basic() public void Emby_Basic()
{ {
var args = new NodeParameters(@"/media/movies/Citizen Kane (1941)/Citizen Kane (1941).mp4", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/media/movies/Citizen Kane (1941)/Citizen Kane (1941).mp4", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");
@@ -24,7 +24,7 @@ public class EmbyTests
[TestMethod] [TestMethod]
public void Emby_Fail() public void Emby_Fail()
{ {
var args = new NodeParameters(@"/media/unknownmovies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/media/unknownmovies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.invalid.json"); return File.ReadAllText("../../../settings.invalid.json");
@@ -37,7 +37,7 @@ public class EmbyTests
[TestMethod] [TestMethod]
public void Emby_Mapped() public void Emby_Mapped()
{ {
var args = new NodeParameters(@"/mnt/movies/Citizen Kane (1941)/Citizen Kane (1941).mp4", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/mnt/movies/Citizen Kane (1941)/Citizen Kane (1941).mp4", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

Binary file not shown.

Binary file not shown.

View File

@@ -13,6 +13,7 @@
<Product>Gotify</Product> <Product>Gotify</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Lets you send Gotify messages to a server</Description> <Description>Lets you send Gotify messages to a server</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FileFlows.Plugin"> <Reference Include="FileFlows.Plugin">

View File

@@ -11,7 +11,7 @@ public class GotifyTests
[TestMethod] [TestMethod]
public void Gotify_Basic_Message() public void Gotify_Basic_Message()
{ {
var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -4,7 +4,7 @@ public class ImageInfo
{ {
public int Width { get; set; } public int Width { get; set; }
public int Height { get; set; } public int Height { get; set; }
public string Format { get; set; } public string Format { get; set; } = string.Empty;
public bool IsPortrait => Width < Height; public bool IsPortrait => Width < Height;
public bool IsLandscape => Height < Width; public bool IsLandscape => Height < Width;
} }

View File

@@ -13,6 +13,7 @@
<Product>Image</Product> <Product>Image</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Nodes for processing images files. This plugin contains nodes to convert and manipulate images.</Description> <Description>Nodes for processing images files. This plugin contains nodes to convert and manipulate images.</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug'"> <ItemGroup Condition=" '$(Configuration)' == 'Debug'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />

View File

@@ -1,5 +1,4 @@
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using System.ComponentModel; using System.ComponentModel;
using ImageMagick; using ImageMagick;

View File

@@ -16,7 +16,7 @@ public abstract class ImageBaseNode:Node
/// <summary> /// <summary>
/// Gets or sets the current format of the image. /// Gets or sets the current format of the image.
/// </summary> /// </summary>
protected string CurrentFormat { get; private set; } protected string? CurrentFormat { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the current width of the image. /// Gets or sets the current width of the image.
@@ -72,7 +72,7 @@ public abstract class ImageBaseNode:Node
/// </summary> /// </summary>
/// <param name="args">The NodeParameters</param> /// <param name="args">The NodeParameters</param>
/// <param name="variables">Additional variables associated with the image (optional).</param> /// <param name="variables">Additional variables associated with the image (optional).</param>
protected void UpdateImageInfo(NodeParameters args, Dictionary<string, object> variables = null) protected void UpdateImageInfo(NodeParameters args, Dictionary<string, object>? variables = null)
{ {
string extension = FileHelper.GetExtension(args.WorkingFile).ToLowerInvariant().TrimStart('.'); string extension = FileHelper.GetExtension(args.WorkingFile).ToLowerInvariant().TrimStart('.');
if (extension == "heic") if (extension == "heic")
@@ -124,7 +124,7 @@ public abstract class ImageBaseNode:Node
/// <param name="format">The format of the image.</param> /// <param name="format">The format of the image.</param>
/// <param name="variables">Additional variables associated with the image (optional).</param> /// <param name="variables">Additional variables associated with the image (optional).</param>
/// <param name="dateTaken">The date when the image was taken (optional).</param> /// <param name="dateTaken">The date when the image was taken (optional).</param>
protected void UpdateImageInfo(NodeParameters args, int width, int height, string format, Dictionary<string, object> variables = null, DateTime? dateTaken = null) protected void UpdateImageInfo(NodeParameters args, int width, int height, string format, Dictionary<string, object>? variables = null, DateTime? dateTaken = null)
{ {
var imageInfo = new ImageInfo var imageInfo = new ImageInfo
{ {

View File

@@ -18,7 +18,7 @@ public class ImageFormat: ImageNode
if(formatOpts.format?.Name == CurrentFormat) if(formatOpts.format?.Name == CurrentFormat)
{ {
args.Logger?.ILog("File already in format: " + formatOpts.format.Name); args.Logger?.ILog("File already in format: " + (formatOpts.format?.Name ?? string.Empty));
return 2; return 2;
} }

View File

@@ -15,9 +15,9 @@ namespace FileFlows.ImageNodes.Images;
public abstract class ImageNode : ImageBaseNode public abstract class ImageNode : ImageBaseNode
{ {
[Select(nameof(FormatOptions), 1)] [Select(nameof(FormatOptions), 1)]
public string Format { get; set; } public string Format { get; set; } = string.Empty;
private static List<ListOption> _FormatOptions; private static List<ListOption>? _FormatOptions;
public static List<ListOption> FormatOptions public static List<ListOption> FormatOptions
{ {
get get

View File

@@ -24,7 +24,11 @@ public class ImageResizer: ImageNode
[Select(nameof(ResizeModes), 2)] [Select(nameof(ResizeModes), 2)]
public ResizeMode Mode { get; set; } public ResizeMode Mode { get; set; }
private static List<ListOption> _ResizeModes; private static List<ListOption>? _ResizeModes;
/// <summary>
/// Gets the resize modes
/// </summary>
public static List<ListOption> ResizeModes public static List<ListOption> ResizeModes
{ {
get get

View File

@@ -15,7 +15,10 @@ public class ImageRotate: ImageNode
[Select(nameof(AngleOptions), 2)] [Select(nameof(AngleOptions), 2)]
public int Angle { get; set; } public int Angle { get; set; }
private static List<ListOption> _AngleOptions; private static List<ListOption>? _AngleOptions;
/// <summary>
/// Gest the Angle Options
/// </summary>
public static List<ListOption> AngleOptions public static List<ListOption> AngleOptions
{ {
get get
@@ -24,9 +27,9 @@ public class ImageRotate: ImageNode
{ {
_AngleOptions = new List<ListOption> _AngleOptions = new List<ListOption>
{ {
new ListOption { Value = 90, Label = "90"}, new () { Value = 90, Label = "90"},
new ListOption { Value = 180, Label = "180"}, new () { Value = 180, Label = "180"},
new ListOption { Value = 270, Label = "270"} new () { Value = 270, Label = "270"}
}; };
} }
return _AngleOptions; return _AngleOptions;

View File

@@ -10,11 +10,11 @@ namespace FileFlows.ImageNodes.Tests;
[TestClass] [TestClass]
public class ImageNodesTests public class ImageNodesTests
{ {
string TestImage1; string? TestImage1;
string TestImage2; string? TestImage2;
string TestImageHeic; string? TestImageHeic;
string TempDir; string TempDir;
string TestCropImage1, TestCropImage2, TestCropImage3, TestCropImage4, TestCropImageNoCrop, TestExif; string? TestCropImage1, TestCropImage2, TestCropImage3, TestCropImage4, TestCropImageNoCrop, TestExif;
public ImageNodesTests() public ImageNodesTests()
{ {
@@ -46,7 +46,7 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_ImageFormat() public void ImageNodes_Basic_ImageFormat()
{ {
var args = new NodeParameters(TestImage1, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImage1, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -59,7 +59,7 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_ImageFormat_Heic() public void ImageNodes_Basic_ImageFormat_Heic()
{ {
var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -72,7 +72,7 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_IsLandscape_Heic() public void ImageNodes_Basic_IsLandscape_Heic()
{ {
var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -89,7 +89,7 @@ public class ImageNodesTests
public void ImageNodes_Basic_Resize() public void ImageNodes_Basic_Resize()
{ {
var logger = new TestLogger(); var logger = new TestLogger();
var args = new NodeParameters(TestImage1, logger, false, string.Empty, null) var args = new NodeParameters(TestImage1, logger, false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -107,7 +107,7 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_Resize_Heic() public void ImageNodes_Basic_Resize_Heic()
{ {
var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -122,15 +122,15 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_Resize_Percent() public void ImageNodes_Basic_Resize_Percent()
{ {
var args = new NodeParameters(TestImage1, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImage1, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
var imgFile = new ImageFile(); var imgFile = new ImageFile();
imgFile.Execute(args); imgFile.Execute(args);
int width = imgFile.GetImageInfo(args).Width; int width = imgFile.GetImageInfo(args)?.Width ?? 0;
int height = imgFile.GetImageInfo(args).Height; int height = imgFile.GetImageInfo(args)?.Height ?? 0;
var node = new ImageResizer(); var node = new ImageResizer();
node.Width = 200; node.Width = 200;
@@ -147,7 +147,7 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_Flip() public void ImageNodes_Basic_Flip()
{ {
var args = new NodeParameters(TestImage2, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImage2, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -161,7 +161,7 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_Flip_Heic() public void ImageNodes_Basic_Flip_Heic()
{ {
var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -174,7 +174,7 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_Rotate() public void ImageNodes_Basic_Rotate()
{ {
var args = new NodeParameters(TestImage2, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImage2, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -187,7 +187,7 @@ public class ImageNodesTests
[TestMethod] [TestMethod]
public void ImageNodes_Basic_Rotate_Heic() public void ImageNodes_Basic_Rotate_Heic()
{ {
var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null) var args = new NodeParameters(TestImageHeic, new TestLogger(), false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -202,7 +202,7 @@ public class ImageNodesTests
{ {
Assert.IsTrue(System.IO.File.Exists(TestCropImage1)); Assert.IsTrue(System.IO.File.Exists(TestCropImage1));
var logger = new TestLogger(); var logger = new TestLogger();
var args = new NodeParameters(TestCropImage1, logger, false, string.Empty, null) var args = new NodeParameters(TestCropImage1, logger, false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -220,7 +220,7 @@ public class ImageNodesTests
public void ImageNodes_Basic_AutoCrop_02() public void ImageNodes_Basic_AutoCrop_02()
{ {
var logger = new TestLogger(); var logger = new TestLogger();
var args = new NodeParameters(TestCropImage2, logger, false, string.Empty, null) var args = new NodeParameters(TestCropImage2, logger, false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -237,7 +237,7 @@ public class ImageNodesTests
public void ImageNodes_Basic_AutoCrop_03() public void ImageNodes_Basic_AutoCrop_03()
{ {
var logger = new TestLogger(); var logger = new TestLogger();
var args = new NodeParameters(TestCropImage3, logger, false, string.Empty, null) var args = new NodeParameters(TestCropImage3, logger, false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -255,7 +255,7 @@ public class ImageNodesTests
public void ImageNodes_Basic_AutoCrop_04() public void ImageNodes_Basic_AutoCrop_04()
{ {
var logger = new TestLogger(); var logger = new TestLogger();
var args = new NodeParameters(TestCropImage4, logger, false, string.Empty, null) var args = new NodeParameters(TestCropImage4, logger, false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -279,7 +279,7 @@ public class ImageNodesTests
public void ImageNodes_Basic_AutoCrop_NoCrop() public void ImageNodes_Basic_AutoCrop_NoCrop()
{ {
var logger = new TestLogger(); var logger = new TestLogger();
var args = new NodeParameters(TestCropImageNoCrop, logger, false, string.Empty, null) var args = new NodeParameters(TestCropImageNoCrop, logger, false, string.Empty, null!)
{ {
TempPath = TempDir TempPath = TempDir
}; };
@@ -306,7 +306,7 @@ public class ImageNodesTests
var node = new ImageFile(); var node = new ImageFile();
var result = node.Execute(args); var result = node.Execute(args);
Assert.AreEqual(1, result); Assert.AreEqual(1, result);
if(node.Variables.TryGetValue("img.DateTaken", out object oDate) == false) if(node.Variables.TryGetValue("img.DateTaken", out object? oDate) == false)
Assert.Fail("Failed to get date time"); Assert.Fail("Failed to get date time");
if(oDate is DateTime dt == false) if(oDate is DateTime dt == false)

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -17,8 +17,8 @@ public class LocalFileService : IFileService
/// <summary> /// <summary>
/// Gets or sets the allowed paths the file service can access /// Gets or sets the allowed paths the file service can access
/// </summary> /// </summary>
public string[] AllowedPaths { get; init; } public string[]? AllowedPaths { get; init; }
/// <summary> /// <summary>
/// Gets or sets a function for replacing variables in a string. /// Gets or sets a function for replacing variables in a string.
/// </summary> /// </summary>
@@ -26,7 +26,7 @@ public class LocalFileService : IFileService
/// The function takes a string input, a boolean indicating whether to strip missing variables, /// The function takes a string input, a boolean indicating whether to strip missing variables,
/// and a boolean indicating whether to clean special characters. /// and a boolean indicating whether to clean special characters.
/// </remarks> /// </remarks>
public ReplaceVariablesDelegate ReplaceVariables { get; set; } public ReplaceVariablesDelegate? ReplaceVariables { get; set; }
/// <summary> /// <summary>
/// Gets or sets the permissions to use for files /// Gets or sets the permissions to use for files
@@ -36,7 +36,7 @@ public class LocalFileService : IFileService
/// <summary> /// <summary>
/// Gets or sets the owner:group to use for files /// Gets or sets the owner:group to use for files
/// </summary> /// </summary>
public string OwnerGroup { get; set; } public string? OwnerGroup { get; set; }
/// <summary> /// <summary>
/// Gets or sets the logger used for logging /// Gets or sets the logger used for logging
@@ -169,7 +169,7 @@ public class LocalFileService : IFileService
Name = fileInfo.Name, Name = fileInfo.Name,
FullName = fileInfo.FullName, FullName = fileInfo.FullName,
Length = fileInfo.Length, Length = fileInfo.Length,
Directory = fileInfo.DirectoryName Directory = fileInfo.DirectoryName!
}; };
} }
catch (Exception ex) catch (Exception ex)
@@ -257,7 +257,7 @@ public class LocalFileService : IFileService
var fileInfo = new FileInfo(path); var fileInfo = new FileInfo(path);
if (fileInfo.Exists == false) if (fileInfo.Exists == false)
return Result<bool>.Fail("File does not exist"); return Result<bool>.Fail("File does not exist");
var destDir = new FileInfo(destination).Directory; var destDir = new FileInfo(destination).Directory!;
if (destDir.Exists == false) if (destDir.Exists == false)
{ {
destDir.Create(); destDir.Create();
@@ -286,7 +286,7 @@ public class LocalFileService : IFileService
if (fileInfo.Exists == false) if (fileInfo.Exists == false)
return Result<bool>.Fail("File does not exist"); return Result<bool>.Fail("File does not exist");
var destDir = new FileInfo(destination).Directory; var destDir = new FileInfo(destination).Directory!;
if (destDir.Exists == false) if (destDir.Exists == false)
{ {
destDir.Create(); destDir.Create();
@@ -435,7 +435,7 @@ public class LocalFileService : IFileService
return true; return true;
} }
public void SetPermissions(string path, int? permissions = null, Action<string> logMethod = null) public void SetPermissions(string path, int? permissions = null, Action<string>? logMethod = null)
{ {
logMethod ??= (string message) => Logger?.ILog(message); logMethod ??= (string message) => Logger?.ILog(message);
@@ -457,26 +457,9 @@ public class LocalFileService : IFileService
FileHelper.SetPermissions(logger, path, file: isFile, permissions: permissions); FileHelper.SetPermissions(logger, path, file: isFile, permissions: permissions);
FileHelper.ChangeOwner(logger, path, file: isFile, ownerGroup: OwnerGroup); FileHelper.ChangeOwner(logger, path, file: isFile, ownerGroup: OwnerGroup ?? string.Empty);
logMethod(logger.ToString()); logMethod(logger.ToString());
return;
if (OperatingSystem.IsLinux())
{
var filePermissions = FileHelper.ConvertLinuxPermissionsToUnixFileMode(permissions.Value);
if (filePermissions == UnixFileMode.None)
{
logMethod("SetPermissions: Invalid file permissions: " + permissions.Value);
return;
}
File.SetUnixFileMode(path, filePermissions);
logMethod($"SetPermissions: Permission [{filePermissions}] set on file: " + path);
}
} }
} }
#endif #endif

View File

@@ -140,15 +140,18 @@ namespace MetaNodes.Music
return tag.Artists; return tag.Artists;
} }
} }
catch (Exception ex) { } catch (Exception)
{
// Ignored
}
// try find it from the filename.... // try find it from the filename....
var parts = args.RelativeFile.Replace("\\", "/").Split('/'); var parts = args.RelativeFile.Replace("\\", "/").Split('/');
if (parts.Length == 3) if (parts.Length == 3)
return parts[0]; // artist/album/song.mp3 return parts[0]; // artist/album/song.mp3
else if (parts.Length > 3) if (parts.Length > 3)
return parts[parts.Length - 3]; return parts[parts.Length - 3];
else if (parts.Length == 2) if (parts.Length == 2)
return parts[0]; return parts[0];
// not in path. try looking for it in the filename // not in path. try looking for it in the filename

View File

@@ -24,11 +24,13 @@ namespace MetaNodes.Tests
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -186,10 +186,12 @@ public class MovieLookupTests
var md = MovieLookup.GetVideoMetadata(args, movieApi, 414906, @"D:\videos\temp"); var md = MovieLookup.GetVideoMetadata(args, movieApi, 414906, @"D:\videos\temp");
Assert.IsNotNull(md); Assert.IsNotNull(md);
#pragma warning disable IL2026
string json = System.Text.Json.JsonSerializer.Serialize(md, new System.Text.Json.JsonSerializerOptions string json = System.Text.Json.JsonSerializer.Serialize(md, new System.Text.Json.JsonSerializerOptions
{ {
WriteIndented = true WriteIndented = true
}); });
#pragma warning restore IL2026
File.WriteAllText(@"D:\videos\metadata.json", json); File.WriteAllText(@"D:\videos\metadata.json", json);
} }
[TestMethod] [TestMethod]

View File

@@ -120,7 +120,7 @@ public class TVShowLookup : Node
return 1; return 1;
} }
internal static (string LookupName, string Year) GetLookupName(string filename, bool useFolderName) internal static (string? LookupName, string? Year) GetLookupName(string filename, bool useFolderName)
{ {
var fileInfo = new FileInfo(filename); var fileInfo = new FileInfo(filename);
string lookupName; string lookupName;

View File

@@ -34,9 +34,9 @@ internal abstract class ApiRequestBase
public async Task<ApiQueryResponse<T>> QueryAsync<T>( string command, Func<string, T> deserializer ) public async Task<ApiQueryResponse<T>> QueryAsync<T>( string command, Func<string, T> deserializer )
=> await QueryAsync( command, new Dictionary<string, string>(), deserializer ); => await QueryAsync( command, new Dictionary<string, string>(), deserializer );
public async Task<ApiQueryResponse<T>> QueryAsync<T>( string command, IDictionary<string, string> parameters, Func<string, T> deserializer ) public async Task<ApiQueryResponse<T>> QueryAsync<T>( string command, IDictionary<string, string> parameters, Func<string, T>? deserializer )
{ {
deserializer ??= JsonConvert.DeserializeObject<T>; deserializer ??= (string str) => JsonConvert.DeserializeObject<T>(str);
using HttpClient client = CreateClient(); using HttpClient client = CreateClient();
string cmd = CreateCommand( command, parameters ); string cmd = CreateCommand( command, parameters );

View File

@@ -98,7 +98,7 @@ public static class GenreFactory
.Where( x => x.IsStatic ) .Where( x => x.IsStatic )
.Where( x => x.IsPublic ) .Where( x => x.IsPublic )
.Where( x => x.ReturnType == typeof( Genre ) ) .Where( x => x.ReturnType == typeof( Genre ) )
.Select( x => ( Genre )x.Invoke( null, null ) ) .Select( x => ( Genre )x.Invoke( null, null )! )
.ToList(); .ToList();
return all.AsReadOnly(); return all.AsReadOnly();

View File

@@ -1,13 +1,10 @@
using FileFlows.Plex.Models; using FileFlows.Plex.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace FileFlows.Plex.MediaManagement; namespace FileFlows.Plex.MediaManagement;
/// <summary>
/// Plex flow element
/// </summary>
public abstract class PlexNode:Node public abstract class PlexNode:Node
{ {
/// <inheritdoc /> /// <inheritdoc />
@@ -20,15 +17,13 @@ public abstract class PlexNode:Node
public override bool NoEditorOnAdd => true; public override bool NoEditorOnAdd => true;
/// <inheritdoc /> /// <inheritdoc />
public override string Icon => "svg:plex"; public override string Icon => "svg:plex";
[Text(1)]
public string ServerUrl { get; set; }
[Text(2)] [Text(1)] public string ServerUrl { get; set; } = string.Empty;
public string AccessToken { get; set; }
[Text(2)] public string AccessToken { get; set; } = string.Empty;
[KeyValue(3, null)] [KeyValue(3, null)]
public List<KeyValuePair<string, string>> Mapping { get; set; } public List<KeyValuePair<string, string>>? Mapping { get; set; }
public override int Execute(NodeParameters args) public override int Execute(NodeParameters args)
{ {
@@ -79,7 +74,9 @@ public abstract class PlexNode:Node
{ {
var options = new System.Text.Json.JsonSerializerOptions(); var options = new System.Text.Json.JsonSerializerOptions();
options.PropertyNameCaseInsensitive = true; options.PropertyNameCaseInsensitive = true;
sections = System.Text.Json.JsonSerializer.Deserialize<PlexSections>(sectionsResponse.body, options); #pragma warning disable IL2026
sections = System.Text.Json.JsonSerializer.Deserialize<PlexSections>(sectionsResponse.body, options)!;
#pragma warning restore IL2026
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -123,7 +120,7 @@ public abstract class PlexNode:Node
protected abstract int ExecuteActual(NodeParameters args, PlexDirectory directory, string url, string mappedPath, string accessToken); protected abstract int ExecuteActual(NodeParameters args, PlexDirectory directory, string url, string mappedPath, string accessToken);
private Func<HttpClient, string, (bool success, string body)> _GetWebRequest; private Func<HttpClient, string, (bool success, string body)>? _GetWebRequest;
internal Func<HttpClient, string, (bool success, string body)> GetWebRequest internal Func<HttpClient, string, (bool success, string body)> GetWebRequest
{ {
get get
@@ -148,7 +145,7 @@ public abstract class PlexNode:Node
return _GetWebRequest; return _GetWebRequest;
} }
} }
private Func<HttpClient, string, (bool success, string body)> _PutWebRequest; private Func<HttpClient, string, (bool success, string body)>? _PutWebRequest;
internal Func<HttpClient, string, (bool success, string body)> PutWebRequest internal Func<HttpClient, string, (bool success, string body)> PutWebRequest
{ {
get get

View File

@@ -2,12 +2,12 @@
public class PlexDirectory public class PlexDirectory
{ {
public string Key { get; set; } public string? Key { get; set; }
public PlexDirectoryLocation[] Location { get; set; } public PlexDirectoryLocation[]? Location { get; set; }
} }
public class PlexDirectoryLocation public class PlexDirectoryLocation
{ {
public int Id { get; set; } public int Id { get; set; }
public string Path { get; set; } public string? Path { get; set; }
} }

View File

@@ -2,14 +2,14 @@
internal class PlexMedia internal class PlexMedia
{ {
public string RatingKey { get; set; } public string? RatingKey { get; set; }
public int Id { get; set; } public int Id { get; set; }
public PlexPart[] Part { get; set; } public PlexPart[]? Part { get; set; }
} }
internal class PlexPart internal class PlexPart
{ {
public int Id { get; set; } public int Id { get; set; }
public string Key { get; set; } public string? Key { get; set; }
public string File { get; set; } public string? File { get; set; }
} }

View File

@@ -2,7 +2,7 @@
internal class PlexMetadata internal class PlexMetadata
{ {
public string RatingKey { get; set; } public string? RatingKey { get; set; }
public string Key { get; set; } public string? Key { get; set; }
public PlexMedia[] Media { get; set; } public PlexMedia[]? Media { get; set; }
} }

View File

@@ -2,12 +2,12 @@
internal class PlexSections internal class PlexSections
{ {
public PlexSection MediaContainer { get; set; } public PlexSection? MediaContainer { get; set; }
} }
internal class PlexSection internal class PlexSection
{ {
public int Size { get; set; } public int Size { get; set; }
public PlexDirectory[] Directory { get; set; } public PlexDirectory[]? Directory { get; set; }
public PlexMetadata[] Metadata { get; set; } public PlexMetadata[]? Metadata { get; set; }
} }

View File

@@ -14,6 +14,7 @@
<Product>Plex</Product> <Product>Plex</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Plex plugin that lets you update Plex with any changes</Description> <Description>Plex plugin that lets you update Plex with any changes</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Update="i18n\*.json"> <None Update="i18n\*.json">

View File

@@ -11,7 +11,7 @@ public class PlexAnayzeTests
[TestMethod] [TestMethod]
public void PlexAnayze_Basic() public void PlexAnayze_Basic()
{ {
var args = new NodeParameters(@"/media/tv/Outrageous Fortune/Season 3/Outrageous Fotune - 3x02.mkv", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/media/tv/Outrageous Fortune/Season 3/Outrageous Fotune - 3x02.mkv", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");
@@ -24,7 +24,7 @@ public class PlexAnayzeTests
[TestMethod] [TestMethod]
public void PlexAnayze_Fail() public void PlexAnayze_Fail()
{ {
var args = new NodeParameters(@"/media/tv/Outrageous Fortune/Season 3/Outrageous Fotune - 3x02a.mkv", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/media/tv/Outrageous Fortune/Season 3/Outrageous Fotune - 3x02a.mkv", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");
@@ -37,7 +37,7 @@ public class PlexAnayzeTests
[TestMethod] [TestMethod]
public void PlexAnayze_Mapping() public void PlexAnayze_Mapping()
{ {
var args = new NodeParameters(@"/mnt/movies/The Batman (2022)/The Batman (2022).mkv", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/mnt/movies/The Batman (2022)/The Batman (2022).mkv", new TestLogger(), false, string.Empty, null!);
var settings = new PluginSettings(); var settings = new PluginSettings();
settings.Mapping = new List<KeyValuePair<string, string>>(); settings.Mapping = new List<KeyValuePair<string, string>>();
settings.Mapping.Add(new KeyValuePair<string, string>("/mnt/movies", "/media/movies")); settings.Mapping.Add(new KeyValuePair<string, string>("/mnt/movies", "/media/movies"));

View File

@@ -11,7 +11,7 @@ public class PlexUpdaterTests
[TestMethod] [TestMethod]
public void Plex_Basic() public void Plex_Basic()
{ {
var args = new NodeParameters(@"/media/movies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/media/movies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");
@@ -24,7 +24,7 @@ public class PlexUpdaterTests
[TestMethod] [TestMethod]
public void Plex_Fail() public void Plex_Fail()
{ {
var args = new NodeParameters(@"/media/unknownmovies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/media/unknownmovies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../settings.json"); return File.ReadAllText("../../../settings.json");
@@ -37,7 +37,7 @@ public class PlexUpdaterTests
[TestMethod] [TestMethod]
public void Plex_Mapping() public void Plex_Mapping()
{ {
var args = new NodeParameters(@"/mnt/movies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty, null);; var args = new NodeParameters(@"/mnt/movies/The Batman (2022)/The Batman.mkv", new TestLogger(), false, string.Empty, null!);
var settings = new PluginSettings(); var settings = new PluginSettings();
settings.Mapping = new List<KeyValuePair<string, string>>(); settings.Mapping = new List<KeyValuePair<string, string>>();
settings.Mapping.Add(new KeyValuePair<string, string>("/mnt/movies", "/media/movies")); settings.Mapping.Add(new KeyValuePair<string, string>("/mnt/movies", "/media/movies"));

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -117,12 +117,14 @@ File shrunk in size by: {{ difference | file_size }} / {{ percent }}%
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", settings.ApiToken); httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", settings.ApiToken);
// Create the request content // Create the request content
#pragma warning disable IL2026
var content = new StringContent(JsonSerializer.Serialize( var content = new StringContent(JsonSerializer.Serialize(
new { new {
type = "note", type = "note",
title, title,
body body
}), Encoding.UTF8, "application/json"); }), Encoding.UTF8, "application/json");
#pragma warning restore IL2026
var response = httpClient.PostAsync("https://api.pushbullet.com/v2/pushes", content).Result; var response = httpClient.PostAsync("https://api.pushbullet.com/v2/pushes", content).Result;

View File

@@ -13,6 +13,7 @@
<Product>Pushbullet</Product> <Product>Pushbullet</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Lets you send Pushbullet messages to a server</Description> <Description>Lets you send Pushbullet messages to a server</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Update="i18n\*.json"> <None Update="i18n\*.json">

View File

@@ -11,7 +11,7 @@ public class PushbulletTests
[TestMethod] [TestMethod]
public void Pushbullet_Basic_Message() public void Pushbullet_Basic_Message()
{ {
var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null); var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../../../Pushbullet.json"); return File.ReadAllText("../../../../../Pushbullet.json");

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -13,6 +13,7 @@
<Product>Pushover</Product> <Product>Pushover</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Lets you send Pushover messages to a server</Description> <Description>Lets you send Pushover messages to a server</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Update="i18n\*.json"> <None Update="i18n\*.json">

View File

@@ -11,7 +11,7 @@ public class PushoverTests
[TestMethod] [TestMethod]
public void Pushover_Basic_Message() public void Pushover_Basic_Message()
{ {
var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null); var args = new NodeParameters("test.file", new TestLogger(), false, string.Empty, null!);
args.GetPluginSettingsJson = (string input) => args.GetPluginSettingsJson = (string input) =>
{ {
return File.ReadAllText("../../../../../pushover.json"); return File.ReadAllText("../../../../../pushover.json");

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -76,7 +76,7 @@ public class WebImageScraper : Node
string path = string.Empty; string path = string.Empty;
if (lines.Length > 1) if (lines.Length > 1)
{ {
Uri.TryCreate(line, UriKind.Absolute, out Uri uri); Uri.TryCreate(line, UriKind.Absolute, out Uri? uri);
if (uri != null) if (uri != null)
{ {
string uriPath = uri.AbsolutePath.Trim('/'); string uriPath = uri.AbsolutePath.Trim('/');
@@ -114,7 +114,7 @@ public class WebImageScraper : Node
{ {
await File.WriteAllLinesAsync(fileName, urls); await File.WriteAllLinesAsync(fileName, urls);
} }
catch (Exception ex) catch (Exception)
{ {
// Handle file writing error // Handle file writing error
} }
@@ -168,7 +168,7 @@ public class WebImageScraper : Node
/// <param name="depth">The current depth of link following.</param> /// <param name="depth">The current depth of link following.</param>
/// <param name="baseUrl">The base URL for domain comparison.</param> /// <param name="baseUrl">The base URL for domain comparison.</param>
/// <returns>A list of image URLs extracted from the URL and its sub-links.</returns> /// <returns>A list of image URLs extracted from the URL and its sub-links.</returns>
private async Task<List<string>> ProcessUrlAndGetImageUrls(ILogger logger, string url, int depth, string baseUrl) private async Task<List<string>> ProcessUrlAndGetImageUrls(ILogger logger, string url, int depth, string? baseUrl)
{ {
var imageUrls = new List<string>(); var imageUrls = new List<string>();
if (++PageCount > MaxPages) if (++PageCount > MaxPages)
@@ -176,7 +176,7 @@ public class WebImageScraper : Node
if (string.IsNullOrEmpty(baseUrl)) if (string.IsNullOrEmpty(baseUrl))
{ {
Uri.TryCreate(url, UriKind.Absolute, out Uri uri); Uri.TryCreate(url, UriKind.Absolute, out Uri? uri);
baseUrl = uri?.GetLeftPart(UriPartial.Path); // Retrieve URL up to path level baseUrl = uri?.GetLeftPart(UriPartial.Path); // Retrieve URL up to path level
} }

View File

@@ -12,6 +12,7 @@
<Product>Site Scraping</Product> <Product>Site Scraping</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Flow elements used to scrape website for data/images etc.</Description> <Description>Flow elements used to scrape website for data/images etc.</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug'"> <ItemGroup Condition=" '$(Configuration)' == 'Debug'">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -15,7 +15,7 @@ public class WebsiteScraperTests
var temp = "/home/john/Pictures/scrapped/temp"; var temp = "/home/john/Pictures/scrapped/temp";
if (Directory.Exists(temp) == false) if (Directory.Exists(temp) == false)
Directory.CreateDirectory(temp); Directory.CreateDirectory(temp);
var args = new NodeParameters(file, logger, false, string.Empty, null) var args = new NodeParameters(file, logger, false, string.Empty, null!)
{ {
TempPath = temp TempPath = temp
}; };

View File

@@ -13,6 +13,7 @@
<Product>Telegram</Product> <Product>Telegram</Product>
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Lets you send Telegram messages</Description> <Description>Lets you send Telegram messages</Description>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="Telegram.en.json" /> <None Remove="Telegram.en.json" />

View File

@@ -24,11 +24,13 @@ internal class TestLogger : ILogger
{ {
if (args == null || args.Length == 0) if (args == null || args.Length == 0)
return; return;
#pragma warning disable IL2026
string message = type + " -> " + string message = type + " -> " +
string.Join(", ", args.Select(x => string.Join(", ", args.Select(x =>
x == null ? "null" : x == null ? "null" :
x.GetType().IsPrimitive || x is string ? x.ToString() : x.GetType().IsPrimitive || x is string ? x.ToString() :
System.Text.Json.JsonSerializer.Serialize(x))); System.Text.Json.JsonSerializer.Serialize(x)));
#pragma warning restore IL2026
Messages.Add(message); Messages.Add(message);
} }

View File

@@ -13,6 +13,7 @@
<PackageProjectUrl>https://fileflows.com/</PackageProjectUrl> <PackageProjectUrl>https://fileflows.com/</PackageProjectUrl>
<Description>Flow element for the processing of video files, including but not limited to video file conversion, video file detection, upscaling, downscaling, and transcoding.</Description> <Description>Flow element for the processing of video files, including but not limited to video file conversion, video file detection, upscaling, downscaling, and transcoding.</Description>
<RootNamespace> FileFlows.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace> <RootNamespace> FileFlows.$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;CS8618;CS8601;CS8602;CS8603;CS8604;CS8618;CS8625;CS8765;CS8767;</NoWarn> <NoWarn>1701;1702;CS8618;CS8601;CS8602;CS8603;CS8604;CS8618;CS8625;CS8765;CS8767;</NoWarn>