mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2025-12-21 16:19:31 -06:00
set WarningsAsErrors = true
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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.
@@ -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">
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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"));
|
||||||
|
|||||||
@@ -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"));
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user