From 368390b353dd97a8835cc0acba878a14671d2dfd Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sat, 2 Nov 2024 14:43:53 -0700 Subject: [PATCH] InPlaceMoveDirectory DirectoryName => DirectoryFullPath --- Metadata/Models/Stateless/Get.cs | 6 +- Rename/Models/Binder/AppSettings.cs | 8 +-- Rename/Models/RenameConfiguration.cs | 1 + Rename/Rename.cs | 65 ++++++++++++------- Shared/Models/FileHolder.cs | 8 +-- Shared/Models/FilePath.cs | 6 +- Shared/Models/Stateless/Methods/IDate.cs | 13 ++-- Shared/Models/Stateless/Methods/IId.cs | 6 +- Shared/Models/Stateless/XDate.cs | 81 +++++++++++++++++------- 9 files changed, 121 insertions(+), 73 deletions(-) diff --git a/Metadata/Models/Stateless/Get.cs b/Metadata/Models/Stateless/Get.cs index 81ded9d..94d2b42 100644 --- a/Metadata/Models/Stateless/Get.cs +++ b/Metadata/Models/Stateless/Get.cs @@ -17,9 +17,9 @@ internal static class Get foreach (string file in files) { fileHolder = FileHolder.Get(file); - if (fileHolder.DirectoryName is null) + if (fileHolder.DirectoryFullPath is null) continue; - key = $"{Path.Combine(fileHolder.DirectoryName, fileHolder.NameWithoutExtension)}"; + key = $"{Path.Combine(fileHolder.DirectoryFullPath, fileHolder.NameWithoutExtension)}"; if (!results.TryGetValue(key, out fileHolders)) { results.Add(key, []); @@ -44,7 +44,7 @@ internal static class Get FilePath? fastForwardMovingPictureExpertsGroupFilePath; ReadOnlyCollection? fastForwardMovingPictureExpertsGroupFiles; FilePath filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index: null); - string key = $"{Path.Combine(fileHolder.DirectoryName ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}"; + string key = $"{Path.Combine(fileHolder.DirectoryFullPath ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}"; if (distinct.Contains(key)) throw new NotSupportedException("Turn off parallelism when sidecar files are present!"); if (!renameConfiguration.SkipIdFiles || filePath.Id is null || (!filePath.IsIntelligentIdFormat && filePath.SortOrder is not null)) diff --git a/Rename/Models/Binder/AppSettings.cs b/Rename/Models/Binder/AppSettings.cs index eb4a6cf..f85aa63 100644 --- a/Rename/Models/Binder/AppSettings.cs +++ b/Rename/Models/Binder/AppSettings.cs @@ -39,10 +39,10 @@ public class AppSettings private static void Verify(Models.AppSettings appSettings) { - if (appSettings.MaxDegreeOfParallelism > 1 && (appSettings.RenameConfiguration.InPlaceWithOriginalName || appSettings.RenameConfiguration.InPlace)) - throw new NotSupportedException($"Change configuration: {nameof(appSettings.RenameConfiguration.InPlace)} or {nameof(appSettings.MaxDegreeOfParallelism)}"); - if (appSettings.RenameConfiguration.InPlaceWithOriginalName && appSettings.RenameConfiguration.InPlace) - throw new NotSupportedException($"Change configuration: {nameof(appSettings.RenameConfiguration.InPlace)} or {nameof(appSettings.RenameConfiguration.InPlaceWithOriginalName)}"); + if (appSettings.MaxDegreeOfParallelism > 1 && (appSettings.RenameConfiguration.InPlace || appSettings.RenameConfiguration.InPlaceMoveDirectory || appSettings.RenameConfiguration.InPlaceWithOriginalName)) + throw new NotSupportedException($"Change configuration: {nameof(appSettings.RenameConfiguration.InPlace)} or {nameof(appSettings.RenameConfiguration.InPlaceMoveDirectory)} or {nameof(appSettings.MaxDegreeOfParallelism)}"); + if (appSettings.RenameConfiguration.InPlace && appSettings.RenameConfiguration.InPlaceMoveDirectory && appSettings.RenameConfiguration.InPlaceWithOriginalName) + throw new NotSupportedException($"Change configuration: {nameof(appSettings.RenameConfiguration.InPlace)} or {nameof(appSettings.RenameConfiguration.InPlaceMoveDirectory)} or {nameof(appSettings.RenameConfiguration.InPlaceWithOriginalName)}"); } private static Models.AppSettings Get(AppSettings? appSettings, RenameConfiguration renameConfiguration) diff --git a/Rename/Models/RenameConfiguration.cs b/Rename/Models/RenameConfiguration.cs index df5b0cb..4bc8a92 100644 --- a/Rename/Models/RenameConfiguration.cs +++ b/Rename/Models/RenameConfiguration.cs @@ -9,6 +9,7 @@ public record RenameConfiguration(Shared.Models.MetadataConfiguration MetadataCo bool ForceNewId, string[] IgnoreExtensions, bool InPlace, + bool InPlaceMoveDirectory, bool InPlaceWithOriginalName, bool OnlySaveIdentifiersToDisk, string RelativePropertyCollectionFile, diff --git a/Rename/Rename.cs b/Rename/Rename.cs index 292291a..e8e3e40 100644 --- a/Rename/Rename.cs +++ b/Rename/Rename.cs @@ -60,7 +60,7 @@ public partial class Rename : IRename, IDisposable { CommandTask commandTask = Cli.Wrap("L:/Git/ffmpeg-2024-10-02-git-358fdf3083-full_build/bin/ffmpeg.exe") .WithArguments(new[] { "-i", filePath.FullName, "-vf", "select=eq(n\\,0)", "-q:v", "1", $"{filePath.Name}-%4d.jpg" }) - .WithWorkingDirectory(filePath.DirectoryName) + .WithWorkingDirectory(filePath.DirectoryFullPath) .ExecuteAsync(); commandTask.Task.Wait(); check = true; @@ -71,7 +71,7 @@ public partial class Rename : IRename, IDisposable } if (check) { - results.AddRange(Directory.GetFiles(filePath.DirectoryName, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly)); + results.AddRange(Directory.GetFiles(filePath.DirectoryFullPath, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly)); if (results.Count == 0) throw new Exception(); File.SetCreationTime(results[0], new(filePath.CreationTicks)); @@ -132,7 +132,7 @@ public partial class Rename : IRename, IDisposable string checkFileExtension = filePath.ExtensionLowered == jpeg ? jpg : filePath.ExtensionLowered; bool hasDateTimeOriginal = dateTime is not null; string paddedId = IId.GetPaddedId(metadataConfiguration, exifDirectory.Id.Value, hasIgnoreKeyword, hasDateTimeOriginal, i); - string checkDirectory = renameConfiguration.InPlaceWithOriginalName ? Path.Combine(filePath.DirectoryName, filePath.FileNameFirstSegment) : filePath.DirectoryName; + string checkDirectory = renameConfiguration.InPlaceWithOriginalName ? Path.Combine(filePath.DirectoryFullPath, filePath.FileNameFirstSegment) : filePath.DirectoryFullPath; string checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}"); if (checkFile != filePath.FullName) { @@ -170,6 +170,7 @@ public partial class Rename : IRename, IDisposable int index = -1; FileInfo fileInfo; FilePath filePath; + string directoryName; ExifDirectory exifDirectory; List sidecarFiles; DeterministicHashCode deterministicHashCode; @@ -182,7 +183,11 @@ public partial class Rename : IRename, IDisposable index += 1; rename.Tick(); if (keyValuePair.Value.Count > 1 && !renameConfiguration.ForceNewId) + { + if (renameConfiguration.InPlaceMoveDirectory) + continue; throw new NotSupportedException($"When sidecar files are present {nameof(renameConfiguration.ForceNewId)} must be true!"); + } if (keyValuePair.Value.Count > 2) throw new NotSupportedException("Too many sidecar files!"); foreach (FileHolder fileHolder in keyValuePair.Value) @@ -198,7 +203,8 @@ public partial class Rename : IRename, IDisposable { fastForwardMovingPictureExpertsGroupFiles = null; deterministicHashCode = new(null, filePath.Id, null); - if (renameConfiguration.InPlaceWithOriginalName || (renameConfiguration.InPlace && filePath.DirectoryName.EndsWith(filePath.Id.Value.ToString()))) + directoryName = Path.GetFileName(filePath.DirectoryFullPath); + if (renameConfiguration.InPlaceWithOriginalName || (renameConfiguration.InPlace && directoryName.EndsWith(filePath.Id.Value.ToString()))) continue; } else @@ -293,27 +299,37 @@ public partial class Rename : IRename, IDisposable } } - private static string? GetCheckDirectory(RenameConfiguration renameConfiguration, RecordB record, ReadOnlyCollection ids, bool multipleDirectoriesWithFiles) + private static string GetTFW(RecordB record, bool? isWrongYear) => + string.Concat(record.HasDateTimeOriginal ? "T" : "F", isWrongYear is not null && isWrongYear.Value ? "W" : record.FastForwardMovingPictureExpertsGroupUsed ? "V" : "I"); + + private static string GetDirectoryName(string year, string tfw, string prefix, string? splat, int seasonValue, string seasonName, string makerSplit) => + splat is null ? $"{prefix}{year} {tfw}{year}.{seasonValue} {seasonName}{makerSplit}" : $"{prefix}{year} {tfw}{year}{splat}"; + + private static string? GetCheckDirectory(RenameConfiguration renameConfiguration, RecordB record, ReadOnlyCollection ids, bool multipleDirectoriesWithFiles, string paddedId) { string? result; - if (record.FilePath.DirectoryName is null) - throw new NullReferenceException(nameof(record.FilePath.DirectoryName)); string year = record.DateTime.Year.ToString(); - string checkDirectoryName = Path.GetFileName(record.FilePath.DirectoryName); + string checkDirectoryName = Path.GetFileName(record.FilePath.DirectoryFullPath); if (multipleDirectoriesWithFiles && !checkDirectoryName.Contains(year)) result = null; else { - string? maker = IMetadata.GetMaker(record.ExifDirectory); - string hasDateTimeOriginal = record.HasDateTimeOriginal ? "Has" : "Not"; - (int seasonValue, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear); - string rootDirectory = renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory; - string splat = record.FilePath.DirectoryName[^3..][1] == '!' ? record.FilePath.DirectoryName[^3..] : string.Empty; - string fastForwardMovingPictureExpertsGroupUsed = record.FastForwardMovingPictureExpertsGroupUsed ? "Video" : "Image"; - string contains = record.ExifDirectory.Id is null || ids.Contains(record.ExifDirectory.Id.Value) ? "_ Exists _" : "_ New-Destination _"; - string makerSplit = string.IsNullOrEmpty(maker) ? string.IsNullOrEmpty(renameConfiguration.DefaultMaker) ? string.Empty : renameConfiguration.DefaultMaker : $" {maker.Split(' ')[0]}"; - string directoryName = $"{year}.{seasonValue} {seasonName}{makerSplit}{splat}"; - result = Path.Combine(rootDirectory, contains, fastForwardMovingPictureExpertsGroupUsed, hasDateTimeOriginal, directoryName); + (bool? isWrongYear, string[] years) = IDate.IsWrongYear(record.FilePath, record.ExifDirectory); + if (renameConfiguration.InPlaceMoveDirectory && !record.FilePath.FileNameFirstSegment.Contains(paddedId)) + result = null; + else + { + string tfw = GetTFW(record, isWrongYear); + string? maker = IMetadata.GetMaker(record.ExifDirectory); + string[] segments = checkDirectoryName.Split(years, StringSplitOptions.None); + string? splat = checkDirectoryName[^3..][1] == '!' ? checkDirectoryName[^3..] : null; + (int seasonValue, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear); + string rootDirectory = renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory; + string contains = record.ExifDirectory.Id is null || ids.Contains(record.ExifDirectory.Id.Value) ? "_ Exists _" : "_ New-Destination _"; + string makerSplit = string.IsNullOrEmpty(maker) ? string.IsNullOrEmpty(renameConfiguration.DefaultMaker) ? string.Empty : renameConfiguration.DefaultMaker : $" {maker.Split(' ')[0]}"; + string directoryName = GetDirectoryName(year, tfw, segments[0], splat, seasonValue, seasonName, makerSplit); + result = Path.GetFullPath(Path.Combine(rootDirectory, contains, directoryName)); + } } return result; } @@ -348,9 +364,12 @@ public partial class Rename : IRename, IDisposable private static bool? GetDirectoryCheck(RenameConfiguration renameConfiguration) { bool? result = null; - foreach (string directory in Directory.GetDirectories(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory, "*", SearchOption.TopDirectoryOnly)) + IEnumerable files; + string[] directories = Directory.GetDirectories(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) { - foreach (string _ in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories)) + files = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories); + foreach (string _ in files) { if (result is null) result = false; @@ -390,13 +409,11 @@ public partial class Rename : IRename, IDisposable record = sorted[i]; if (record.ExifDirectory.Id is null) continue; - if (record.FilePath.DirectoryName is null) - continue; - checkDirectory = GetCheckDirectory(renameConfiguration, record, ids, multipleDirectoriesWithFiles); + paddedId = IId.GetPaddedId(metadataConfiguration, record.ExifDirectory.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i); + checkDirectory = GetCheckDirectory(renameConfiguration, record, ids, multipleDirectoriesWithFiles, paddedId); if (string.IsNullOrEmpty(checkDirectory)) continue; checkFileExtension = record.FilePath.ExtensionLowered == jpeg ? jpg : record.FilePath.ExtensionLowered; - paddedId = IId.GetPaddedId(metadataConfiguration, record.ExifDirectory.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i); jsonFileSubDirectory = Path.GetDirectoryName(Path.GetDirectoryName(record.JsonFile)) ?? throw new Exception(); checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}"); if (checkFile == record.FilePath.FullName) diff --git a/Shared/Models/FileHolder.cs b/Shared/Models/FileHolder.cs index 7152f6d..62e2659 100644 --- a/Shared/Models/FileHolder.cs +++ b/Shared/Models/FileHolder.cs @@ -4,7 +4,7 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; public record FileHolder(DateTime? CreationTime, - string? DirectoryName, + string? DirectoryFullPath, bool Exists, string ExtensionLowered, string FullName, @@ -52,7 +52,7 @@ public record FileHolder(DateTime? CreationTime, { FileHolder result; result = new(new(filePath.CreationTicks), - filePath.DirectoryName, + filePath.DirectoryFullPath, true, filePath.ExtensionLowered, filePath.FullName, @@ -66,7 +66,7 @@ public record FileHolder(DateTime? CreationTime, private static FileHolder GetExisting(FileHolder fileHolder) => new(fileHolder.CreationTime, - fileHolder.DirectoryName, + fileHolder.DirectoryFullPath, fileHolder.Exists, fileHolder.ExtensionLowered, fileHolder.FullName, @@ -78,7 +78,7 @@ public record FileHolder(DateTime? CreationTime, private static FileHolder GetNonExisting(FileHolder fileHolder) => new(null, - fileHolder.DirectoryName, + fileHolder.DirectoryFullPath, fileHolder.Exists, fileHolder.ExtensionLowered, fileHolder.FullName, diff --git a/Shared/Models/FilePath.cs b/Shared/Models/FilePath.cs index f9f317b..20dbde3 100644 --- a/Shared/Models/FilePath.cs +++ b/Shared/Models/FilePath.cs @@ -5,7 +5,7 @@ using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models; public record FilePath(long CreationTicks, - string DirectoryName, + string DirectoryFullPath, string ExtensionLowered, string FileNameFirstSegment, string FullName, @@ -41,7 +41,7 @@ public record FilePath(long CreationTicks, int? sortOder; string fileNameFirstSegment = fileHolder.Name.Split('.')[0]; int sortOrderOnlyLengthIndex = metadataConfiguration.Offset.ToString().Length; - string fileDirectoryName = fileHolder.DirectoryName ?? throw new NullReferenceException(); + string fileDirectoryFullPath = fileHolder.DirectoryFullPath ?? throw new NullReferenceException(); bool isIntelligentIdFormat = IId.NameWithoutExtensionIsIntelligentIdFormat(metadataConfiguration, fileNameFirstSegment); bool isPaddedIntelligentIdFormat = IId.NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment); bool fileNameFirstSegmentIsIdFormat = !isPaddedIntelligentIdFormat && !isIntelligentIdFormat && IId.NameWithoutExtensionIsIdFormat(metadataConfiguration, fileHolder); @@ -69,7 +69,7 @@ public record FilePath(long CreationTicks, else throw new NotSupportedException(); result = new(fileHolder.CreationTime.Value.Ticks, - fileDirectoryName, + fileDirectoryFullPath, fileHolder.ExtensionLowered, fileNameFirstSegment, fileHolder.FullName, diff --git a/Shared/Models/Stateless/Methods/IDate.cs b/Shared/Models/Stateless/Methods/IDate.cs index b009605..1560644 100644 --- a/Shared/Models/Stateless/Methods/IDate.cs +++ b/Shared/Models/Stateless/Methods/IDate.cs @@ -3,21 +3,16 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IDate { - (bool?, string[]) TestStatic_IsWrongYear(string[] segments, string year) => - IsWrongYear(segments, year); - static (bool?, string[]) IsWrongYear(string[] segments, string year) => - XDate.IsWrongYear(segments, year); + (bool?, string[]) TestStatic_IsWrongYear(FilePath filePath, ExifDirectory exifDirectory) => + IsWrongYear(filePath, exifDirectory); + static (bool?, string[]) IsWrongYear(FilePath filePath, ExifDirectory exifDirectory) => + XDate.IsWrongYear(filePath, exifDirectory); (int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) => GetSeason(dayOfYear); static (int Season, string seasonName) GetSeason(int dayOfYear) => XDate.GetSeason(dayOfYear); - (int Season, string seasonName) TestStatic_GetSeasonAB(int dayOfYear) => - GetSeasonAB(dayOfYear); - static (int Season, string seasonName) GetSeasonAB(int dayOfYear) => - XDate.GetSeasonAB(dayOfYear); - DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) => GetDateTimeOriginal(exifDirectory); static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) => diff --git a/Shared/Models/Stateless/Methods/IId.cs b/Shared/Models/Stateless/Methods/IId.cs index 25cfff2..61375eb 100644 --- a/Shared/Models/Stateless/Methods/IId.cs +++ b/Shared/Models/Stateless/Methods/IId.cs @@ -26,14 +26,14 @@ public interface IId string TestStatic_GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) => GetIgnoreFullPath(filePath, fileHolder); static string GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) => - fileHolder.DirectoryName is null ? + fileHolder.DirectoryFullPath is null ? throw new NotSupportedException() : filePath.Id > -1 ? fileHolder.NameWithoutExtension[^1] == '9' ? - Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension[..^1]}8{fileHolder.ExtensionLowered}") : + Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}8{fileHolder.ExtensionLowered}") : throw new NotSupportedException("High") : fileHolder.NameWithoutExtension[^1] == '1' ? - Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension[..^1]}2{fileHolder.ExtensionLowered}") : + Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}2{fileHolder.ExtensionLowered}") : throw new NotSupportedException("Low"); bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment) => diff --git a/Shared/Models/Stateless/XDate.cs b/Shared/Models/Stateless/XDate.cs index 3fc29e7..ef22a82 100644 --- a/Shared/Models/Stateless/XDate.cs +++ b/Shared/Models/Stateless/XDate.cs @@ -1,3 +1,4 @@ +using System.Collections.ObjectModel; using System.Globalization; using System.Text; @@ -11,31 +12,18 @@ internal abstract class XDate (int Season, string seasonName) result = dayOfYear switch { < 78 => new(0, "Winter"), - < 171 => new(1, "Spring"), - < 264 => new(2, "Summer"), - < 354 => new(3, "Fall"), - _ => new(4, "Winter") + < 124 => new(1, "Spring"), + < 171 => new(2, "Spring"), + < 217 => new(3, "Summer"), + < 264 => new(4, "Summer"), + < 309 => new(5, "Fall"), + < 354 => new(6, "Fall"), + _ => new(7, "Winter") }; return result; } - internal static (int Season, string seasonName) GetSeasonAB(int dayOfYear) - { - (int Season, string seasonName) result = dayOfYear switch - { - < 78 => new(0, "WinterA"), - < 124 => new(1, "SpringA"), - < 171 => new(1, "SpringB"), - < 217 => new(2, "SummerA"), - < 264 => new(2, "SummerB"), - < 309 => new(3, "FallA"), - < 354 => new(3, "FallB"), - _ => new(4, "WinterB") - }; - return result; - } - - internal static (bool?, string[]) IsWrongYear(string[] segments, string year) + private static (bool?, string[]) IsWrongYear(string[] segments, string year) { bool? result; string[] results = ( @@ -66,6 +54,47 @@ internal abstract class XDate return new(result, results); } + internal static (bool?, string[]) IsWrongYear(FilePath filePath, ExifDirectory exifDirectory) + { + string[] results = []; + bool? result = null; + string year; + string directoryName; + string[] directorySegments; + List collection = []; + string? check = Path.GetFullPath(filePath.FullName); + string? pathRoot = Path.GetPathRoot(filePath.FullName); + if (string.IsNullOrEmpty(pathRoot)) + throw new Exception(); + DateTime? dateTimeOriginal = GetDateTimeOriginal(exifDirectory); + if (dateTimeOriginal is not null) + collection.Add(dateTimeOriginal.Value); + else + { + ReadOnlyCollection dateTimes = GetDateTimes(exifDirectory); + foreach (DateTime dateTime in dateTimes) + collection.Add(dateTime); + } + foreach (DateTime dateTime in collection) + { + year = dateTime.ToString("yyyy"); + for (int i = 0; i < int.MaxValue; i++) + { + check = Path.GetDirectoryName(check); + if (string.IsNullOrEmpty(check) || check == pathRoot) + break; + directoryName = Path.GetFileName(check); + directorySegments = directoryName.Split(' '); + (result, results) = IsWrongYear(directorySegments, year); + if (result is not null) + break; + } + if (result is not null && !result.Value) + break; + } + return new(result, results); + } + internal static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) { DateTime? result; @@ -153,9 +182,8 @@ internal abstract class XDate return result; } - internal static DateTime GetMinimum(ExifDirectory exifDirectory) + private static ReadOnlyCollection GetDateTimes(ExifDirectory exifDirectory) { - DateTime result; List results = []; foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) { @@ -207,6 +235,13 @@ internal abstract class XDate results.Add(fileMetadataDirectory.FileModifiedDate.Value); } } + return new(results); + } + + internal static DateTime GetMinimum(ExifDirectory exifDirectory) + { + DateTime result; + ReadOnlyCollection results = GetDateTimes(exifDirectory); result = results.Count == 0 ? DateTime.MinValue : results.Min(); return result; }