using ImageMagick; using SixLabors.ImageSharp.Formats; namespace FileFlows.ImageNodes.Images; /// /// Represents an abstract base class for nodes related to image processing. /// public abstract class ImageBaseNode:Node { /// /// Represents the key for storing image information in a context or dictionary. /// private const string IMAGE_INFO = "ImageInfo"; /// /// Gets or sets the current format of the image. /// protected string CurrentFormat { get; private set; } /// /// Gets or sets the current width of the image. /// protected int CurrentWidth { get; private set; } /// /// Gets or sets the current height of the image. /// protected int CurrentHeight { get; private set; } /// /// Calls any pre-execute setup code /// /// The NodeParameters /// true if successful, otherwise false public override bool PreExecute(NodeParameters args) { var localFile = args.FileService.GetLocalPath(args.WorkingFile); if (localFile.IsFailed) { args.Logger?.ELog("Working file cannot be read: " + localFile.Error); return false; } if (args.WorkingFile.ToLowerInvariant().EndsWith(".heic")) { using var image = new MagickImage(localFile); CurrentHeight = image.Height; CurrentWidth = image.Width; CurrentFormat = "HEIC"; } else { using var image = Image.Load(localFile, out IImageFormat format); CurrentHeight = image.Height; CurrentWidth = image.Width; CurrentFormat = format.Name; } var metadata = new Dictionary(); metadata.Add("Format", CurrentFormat); metadata.Add("Width", CurrentWidth); metadata.Add("Height", CurrentHeight); args.SetMetadata(metadata); return true; } /// /// Updates information about an image based on the provided NodeParameters and optional variables. /// /// The NodeParameters /// Additional variables associated with the image (optional). protected void UpdateImageInfo(NodeParameters args, Dictionary variables = null) { string extension = FileHelper.GetExtension(args.WorkingFile).ToLowerInvariant().TrimStart('.'); if (extension == "heic") { using var image = new MagickImage(args.WorkingFile); UpdateImageInfo(args, image.Width, image.Height, "HEIC", variables); } else { using var image = Image.Load(args.WorkingFile, out IImageFormat format); DateTime? dateTaken = null; if (image.Metadata.ExifProfile != null) { args.Logger?.ILog("EXIF Profile found"); var dateTimeOriginalString = image.Metadata.ExifProfile.GetValue(SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag.DateTimeOriginal)?.Value; if (string.IsNullOrWhiteSpace(dateTimeOriginalString)) { args.Logger?.ILog("No DateTimeOriginal found"); } else { if (string.IsNullOrWhiteSpace(dateTimeOriginalString) == false && TryParseDateTime(dateTimeOriginalString, out DateTime? dateTimeOriginal)) { dateTaken = dateTimeOriginal; args.Logger?.ILog("DateTimeOriginal: " + dateTimeOriginal); } else { args.Logger?.ILog("Invalid date format for DateTimeOriginal: " + dateTimeOriginalString); } } } else { args.Logger?.ILog("No EXIF Profile found"); } UpdateImageInfo(args, image.Width, image.Height, format.Name, variables: variables, dateTaken: dateTaken); } } /// /// Updates information about an image based on the provided NodeParameters, width, height, format, variables, and dateTaken. /// /// The NodeParameters /// The width of the image. /// The height of the image. /// The format of the image. /// Additional variables associated with the image (optional). /// The date when the image was taken (optional). protected void UpdateImageInfo(NodeParameters args, int width, int height, string format, Dictionary variables = null, DateTime? dateTaken = null) { var imageInfo = new ImageInfo { Width = width, Height = height, Format = format }; variables ??= new Dictionary(); args.Parameters[IMAGE_INFO] = imageInfo; variables.AddOrUpdate("img.Width", imageInfo.Width); variables.AddOrUpdate("img.Height", imageInfo.Height); variables.AddOrUpdate("img.Format", imageInfo.Format); variables.AddOrUpdate("img.IsPortrait", imageInfo.IsPortrait); variables.AddOrUpdate("img.IsLandscape", imageInfo.IsLandscape); if (dateTaken != null) { variables.AddOrUpdate("img.DateTaken", dateTaken.Value); } var metadata = new Dictionary(); metadata.Add("Format", imageInfo.Format); metadata.Add("Width", imageInfo.Width); metadata.Add("Height", imageInfo.Height); args.SetMetadata(metadata); args.UpdateVariables(variables); } /// /// Gets information about an image based on the provided NodeParameters. /// /// The NodeParameters /// /// An ImageInfo object representing information about the image, or null if information could not be retrieved. /// internal ImageInfo? GetImageInfo(NodeParameters args) { if (args.Parameters.ContainsKey(IMAGE_INFO) == false) { args.Logger?.WLog("No image information loaded, use a 'Image File' node first"); return null; } var result = args.Parameters[IMAGE_INFO] as ImageInfo; if (result == null) { args.Logger?.WLog("ImageInfo not found for file"); return null; } return result; } /// /// Converts an image to a format we can use, if needed /// /// the node parameters /// the filename fo the image to use protected string ConvertImageIfNeeded(NodeParameters args) { string extension = FileHelper.GetExtension(args.WorkingFile).ToLowerInvariant().TrimStart('.'); if (extension == "heic") { // special case have to use imagemagick using var image = new MagickImage(args.WorkingFile); image.Format = MagickFormat.Png; var newFile = FileHelper.Combine(args.TempPath, Guid.NewGuid() + ".png"); image.Write(newFile); return newFile; } return args.WorkingFile; } /// /// Tries to parse a DateTime from a string, attempting different formats. /// /// The string representation of the DateTime. /// When this method returns, contains the parsed DateTime if successful; otherwise, null. /// /// True if the parsing was successful; otherwise, false. /// static bool TryParseDateTime(string dateTimeString, out DateTime? dateTime) { DateTime parsedDateTime; // Try parsing using DateTime.TryParse if (DateTime.TryParse(dateTimeString, out parsedDateTime)) { dateTime = parsedDateTime; return true; } // Define an array of possible date formats for additional attempts string[] dateFormats = { "yyyy:MM:dd HH:mm:ss", "yyyy-MM-dd HH:mm:ss" /* Add more formats if needed */ }; // Attempt to parse using different formats foreach (var format in dateFormats) { if (DateTime.TryParseExact(dateTimeString, format, null, System.Globalization.DateTimeStyles.None, out parsedDateTime)) { dateTime = parsedDateTime; return true; } } // Set dateTime to null if parsing fails with all formats dateTime = null; return false; } }