From d23398db7a0c606a2b823603a1f726d257dbce4c Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sun, 2 Jul 2023 17:57:50 -0700 Subject: [PATCH] Offset-Date-Time-Original Rename updates again Mirror-Length in place Delete-By-Distinct long to string Copy-Distinct handle move and move back --- .vscode/launch.json | 21 +- .vscode/tasks.json | 2 +- Copy-Distinct/CopyDistinct.cs | 176 ++++++++++++--- Copy-Distinct/Models/AppSettings.cs | 31 +-- Date-Group/.vscode/tasks.json | 2 +- Delete-By-Distinct/DeleteByDistinct.cs | 47 ++-- .../Models/Binder/AppSettings.cs | 2 - Drag-Drop-Move/DragDropMove.cs | 137 +----------- .../Models/Stateless/Methods/IMetadata.cs | 13 +- Metadata/Models/Stateless/Methods/Metadata.cs | 111 ++++++--- Mirror-Length/MirrorLength.cs | 128 +++++++++-- Mirror-Length/Models/AppSettings.cs | 8 +- .../Models/AppSettings.cs | 16 ++ .../Models/Binder/AppSettings.cs | 44 ++++ .../Models/Binder/Configuration.cs | 59 +++++ .../Models/Configuration.cs | 38 ++++ .../Stateless/SerilogExtensionMethods.cs | 10 + .../Offset-Date-Time-Original.csproj | 60 +++++ .../OffsetDateTimeOriginal.cs | 211 ++++++++++++++++++ Offset-Date-Time-Original/Program.cs | 71 ++++++ .../appsettings.Development.json | 10 + Offset-Date-Time-Original/appsettings.json | 126 +++++++++++ Rename/.vscode/UserSecrets/secrets.json | 2 +- Rename/.vscode/tasks.json | 2 +- Rename/Rename.cs | 66 +++--- Shared/Models/Stateless/Methods/IProperty.cs | 5 + Shared/Models/Stateless/Methods/Property.cs | 85 ++++--- View-by-Distance-MKLink-Console.sln | 6 + 28 files changed, 1148 insertions(+), 341 deletions(-) create mode 100644 Offset-Date-Time-Original/Models/AppSettings.cs create mode 100644 Offset-Date-Time-Original/Models/Binder/AppSettings.cs create mode 100644 Offset-Date-Time-Original/Models/Binder/Configuration.cs create mode 100644 Offset-Date-Time-Original/Models/Configuration.cs create mode 100644 Offset-Date-Time-Original/Models/Stateless/SerilogExtensionMethods.cs create mode 100644 Offset-Date-Time-Original/Offset-Date-Time-Original.csproj create mode 100644 Offset-Date-Time-Original/OffsetDateTimeOriginal.cs create mode 100644 Offset-Date-Time-Original/Program.cs create mode 100644 Offset-Date-Time-Original/appsettings.Development.json create mode 100644 Offset-Date-Time-Original/appsettings.json diff --git a/.vscode/launch.json b/.vscode/launch.json index e11b71c..4858671 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -223,6 +223,23 @@ "stopAtEntry": false, "requireExactSource": false }, + { + "name": "Offset-Date-Time-Original", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/Offset-Date-Time-Original/bin/Debug/net7.0/win-x64/Offset-Date-Time-Original.dll", + "args": [ + "s" + ], + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "cwd": "${workspaceFolder}", + "console": "externalTerminal", + "stopAtEntry": false, + "requireExactSource": false + }, { "name": "PrepareForOld", "type": "coreclr", @@ -245,7 +262,7 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Person/bin/Debug/net6.0/Person.dll", + "program": "${workspaceFolder}/Person/bin/Debug/net7.0/Person.dll", "args": [ "s" ], @@ -475,7 +492,7 @@ // // File.Move(string.Concat(jsonGroupFile, abd), jsonGroupFile); // fileAndJsonGroupFiles.Add(new FileAndJsonGroupFile(file, isUniqueFileName, jsonGroupFile, true)); // } -// C:\Program Files\ImageMagick-7.1.0-Q16-HDRI>identify -format '%Q' "D:\7) Question\- - - Images\Mike Goolgle Photos 2014\-1507651551.jpg" +// C:\Program Files\ImageMagick-7.1.0-Q16-HDRI>identify -format '%Q' "D:\7-Question\- - - Images\Mike Goolgle Photos 2014\-1507651551.jpg" // '98' // C:\Program Files\ImageMagick-7.1.0-Q16-HDRI>identify -format '%Q' "D:\7) Question\- - - Images\Mike Goolgle Photos 2014\197616258.jpg" // '90' diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 081120a..21a878b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -28,7 +28,7 @@ "${workspaceFolder}/Person/Person.csproj", "set", "SaveDirectory", - "D:/1) Images A/Images-dd514b88-Results/A2) People/dd514b88/([])" + "D:/1-Images-A/Images-dd514b88-Results/A2) People/dd514b88/([])" ], "problemMatcher": "$msCompile" }, diff --git a/Copy-Distinct/CopyDistinct.cs b/Copy-Distinct/CopyDistinct.cs index ce4f513..8a555e5 100644 --- a/Copy-Distinct/CopyDistinct.cs +++ b/Copy-Distinct/CopyDistinct.cs @@ -31,33 +31,58 @@ public class CopyDistinct _ConfigurationRoot = configurationRoot; ILogger? log = Log.ForContext(); Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); - _FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, appSettings.CopyTo, new string[] { appSettings.ResultDirectoryKey }); Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); _PropertyConfiguration = propertyConfiguration; _Configuration = configuration; propertyConfiguration.Update(); log.Information(propertyConfiguration.RootDirectory); - Verify(); - List lines = CopyDistinctFilesInDirectories(log); - File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); - if (!lines.Any()) - _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(propertyConfiguration.RootDirectory); + (bool move, List allFiles, bool anyLenFiles, bool moveBack) = Verify(); + _FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, appSettings.CopyTo, new string[] { appSettings.ResultDirectoryKey }); + List lines = CopyDistinctFilesInDirectories(log, move, allFiles, anyLenFiles, moveBack); + if (lines.Any()) + File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(propertyConfiguration.RootDirectory); } - private void Verify() + private (bool, List, bool, bool) Verify() { if (_AppSettings is null) - { } + throw new NullReferenceException(nameof(_AppSettings)); if (_IsEnvironment is null) - { } + throw new NullReferenceException(nameof(_IsEnvironment)); if (_Configuration is null) - { } + throw new NullReferenceException(nameof(_Configuration)); if (_ConfigurationRoot is null) - { } + throw new NullReferenceException(nameof(_ConfigurationRoot)); if (_WorkingDirectory is null) - { } + throw new NullReferenceException(nameof(_WorkingDirectory)); if (_PropertyConfiguration is null) - { } + throw new NullReferenceException(nameof(_PropertyConfiguration)); + bool moveBack; + const string fileSearchFilter = "*"; + const string directorySearchFilter = "*"; + string copyTo = Path.GetFullPath(_AppSettings.CopyTo); + bool move = copyTo == _PropertyConfiguration.RootDirectory; + List filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter); + (_, List allFiles) = Get(filesCollection); + bool anyLenFiles = allFiles.Any(l => l.EndsWith("len")); + if (!move) + moveBack = false; + else + { + if (!anyLenFiles) + throw new NotSupportedException("Use Mirror-Length app first!"); + if (string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey)) + throw new NotSupportedException("Change appsettings!"); + if (!Directory.Exists(Path.Combine(_PropertyConfiguration.RootDirectory, _AppSettings.ResultDirectoryKey))) + moveBack = false; + else + { + move = false; + moveBack = true; + } + } + return (move, allFiles, anyLenFiles, moveBack); } private static (List, List) Get(List filesCollection) @@ -89,15 +114,16 @@ public class CopyDistinct string directoryName; bool wrapped = false; List distinct = new(); + FileHolder[] sortedFileHolders = (from l in fileHolders orderby l.LastWriteTime, l.FullName.Length descending select l).ToArray(); string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey; - foreach (FileHolder fileHolder in fileHolders) + foreach (FileHolder fileHolder in sortedFileHolders) { progressBar.Tick(); - if (fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null) + if (fileHolder.Name.EndsWith("len") || fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null) continue; (_, directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration.ResultAllInOneSubdirectoryLength, fileHolder.NameWithoutExtension); directoryName = Path.GetFileName(fileHolder.DirectoryName); - if (directoryName.Length < _PropertyConfiguration.ResultAllInOneSubdirectoryLength || !fileHolder.Name.StartsWith(directoryName)) + if (directoryName.Length < _PropertyConfiguration.ResultAllInOneSubdirectoryLength + 3 || !fileHolder.Name.StartsWith(directoryName)) { if (wrapped) continue; @@ -135,7 +161,64 @@ public class CopyDistinct return results; } - private static List Copy(ProgressBar progressBar, List<(FileHolder, string, string)> toDoCollection) + private static List<(FileHolder, string, string)> GetMoveBackToDoCollection(List files) + { + List<(FileHolder, string, string)> results = new(); + string key; + string? value; + string fileName; + files.Reverse(); + string? directory; + string destinationFile; + List distinctFound = new(); + List distinctNeeded = new(); + Dictionary nameToPath = new(); + for (int i = 1; i < 3; i++) + { + foreach (string file in files) + { + fileName = Path.GetFileName(file); + if (fileName.EndsWith("len")) + { + key = fileName[..^3]; + destinationFile = file[..^3]; + if (nameToPath.ContainsKey(key)) + continue; + nameToPath.Add(key, destinationFile); + } + else + { + if (!distinctNeeded.Contains(file)) + distinctNeeded.Add(file); + if (!nameToPath.ContainsKey(fileName)) + continue; + if (distinctFound.Contains(file)) + continue; + distinctFound.Add(file); + } + } + if (distinctNeeded.Count != distinctFound.Count) + continue; + break; + } + foreach (string file in files) + { + // if (distinctNeeded.Count != distinctFound.Count) + // continue; + fileName = Path.GetFileName(file); + if (fileName.EndsWith("len")) + continue; + if (!nameToPath.TryGetValue(fileName, out value)) + continue; + directory = Path.GetDirectoryName(value); + if (string.IsNullOrEmpty(directory)) + continue; + results.Add(new(new(file), directory, value)); + } + return results; + } + + private static List CopyOrMove(ProgressBar progressBar, List<(FileHolder, string, string)> toDoCollection, bool move, bool moveBack) { List results = new(); FileInfo fileInfo; @@ -151,28 +234,39 @@ public class CopyDistinct } results.Add(fileHolder.NameWithoutExtension); try - { File.Copy(fileHolder.FullName, to); } + { + if (move || moveBack) + File.Move(fileHolder.FullName, to); + else + File.Copy(fileHolder.FullName, to); + } catch (Exception) { } } return results; } - private List CopyDistinctFilesInDirectories(ILogger log) + private List CopyDistinctFilesInDirectories(ILogger log, bool move, List allFiles, bool anyLenFiles, bool moveBack) { List results = new(); ProgressBar progressBar; ConsoleKey? consoleKey = null; - const string fileSearchFilter = "*"; string message = nameof(CopyDistinct); - const string directorySearchFilter = "*"; List distinctDirectories = new(); - List filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter); - (_, List allFiles) = Get(filesCollection); + List<(FileHolder, string, string)> toDoCollection; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - progressBar = new(allFiles.Count, message, options); - FileHolder[] fileHolders = (from l in allFiles select new FileHolder(l)).OrderBy(l => l.LastWriteTime).ToArray(); - List<(FileHolder, string, string)> toDoCollection = GetToDoCollection(progressBar, fileHolders); - progressBar.Dispose(); + if (moveBack) + { + if (!anyLenFiles) + throw new NotSupportedException(); + toDoCollection = GetMoveBackToDoCollection(allFiles); + } + else + { + progressBar = new(allFiles.Count, message, options); + FileHolder[] fileHolders = (from l in allFiles select new FileHolder(l)).ToArray(); + toDoCollection = GetToDoCollection(progressBar, fileHolders); + progressBar.Dispose(); + } foreach ((FileHolder fileHolder, string directory, string to) in toDoCollection) { if (distinctDirectories.Contains(directory)) @@ -184,23 +278,41 @@ public class CopyDistinct if (!Directory.Exists(distinctDirectory)) _ = Directory.CreateDirectory(distinctDirectory); } - log.Information($"Ready to Copy {toDoCollection.Count} file(s)?"); + if (move) + log.Information($"Ready to Move {toDoCollection.Count} file(s)?"); + else if (!moveBack) + log.Information($"Ready to Copy {toDoCollection.Count} file(s)?"); + else + log.Information($"Ready to Move back {toDoCollection.Count} file(s)?"); for (int y = 0; y < int.MaxValue; y++) { - log.Information("Press \"Y\" key to copy file(s), \"N\" key to log file(s) or close console to not copy files"); + if (move) + log.Information("Press \"Y\" key to move file(s), \"N\" key to log file(s) or close console to not move files"); + else if (!moveBack) + log.Information("Press \"Y\" key to copy file(s), \"N\" key to log file(s) or close console to not copy files"); + else + log.Information("Press \"Y\" key to move back file(s), \"N\" key to log file(s) or close console to not move back files"); consoleKey = System.Console.ReadKey().Key; if (consoleKey is ConsoleKey.Y or ConsoleKey.N) break; } log.Information(". . ."); if (consoleKey is null || consoleKey.Value != ConsoleKey.Y) - log.Information("Nothing copied!"); + { + if (move || moveBack) + log.Information("Nothing moved!"); + else + log.Information("Nothing copied!"); + } else { progressBar = new(allFiles.Count, message, options); - results.AddRange(Copy(progressBar, toDoCollection)); + results.AddRange(CopyOrMove(progressBar, toDoCollection, move, moveBack)); progressBar.Dispose(); - log.Information("Done copying"); + if (move || moveBack) + log.Information("Done moving"); + else + log.Information("Done copying"); } return results; } diff --git a/Copy-Distinct/Models/AppSettings.cs b/Copy-Distinct/Models/AppSettings.cs index aa58310..3aac425 100644 --- a/Copy-Distinct/Models/AppSettings.cs +++ b/Copy-Distinct/Models/AppSettings.cs @@ -1,34 +1,15 @@ using System.Text.Json; -using System.Text.Json.Serialization; namespace View_by_Distance.Copy.Distinct.Models; -public class AppSettings +public record AppSettings(string Company, + bool CopyDuplicates, + string CopyTo, + int MaxDegreeOfParallelism, + string ResultDirectoryKey, + string WorkingDirectoryName) { - public string Company { init; get; } - public bool CopyDuplicates { init; get; } - public string CopyTo { init; get; } - public int MaxDegreeOfParallelism { init; get; } - public string ResultDirectoryKey { init; get; } - public string WorkingDirectoryName { init; get; } - - [JsonConstructor] - public AppSettings(string company, - bool copyDuplicates, - string copyTo, - int maxDegreeOfParallelism, - string resultDirectoryKey, - string workingDirectoryName) - { - Company = company; - CopyTo = copyTo; - CopyDuplicates = copyDuplicates; - MaxDegreeOfParallelism = maxDegreeOfParallelism; - ResultDirectoryKey = resultDirectoryKey; - WorkingDirectoryName = workingDirectoryName; - } - public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); diff --git a/Date-Group/.vscode/tasks.json b/Date-Group/.vscode/tasks.json index f440d49..16577c6 100644 --- a/Date-Group/.vscode/tasks.json +++ b/Date-Group/.vscode/tasks.json @@ -28,7 +28,7 @@ "${workspaceFolder}/Date-Group.csproj", "set", "SaveDirectory", - "D:/1) Images A/Images-1e85c0ba-Results/A2) People/1e85c0ba/([])" + "D:/1-Images-A/Images-1e85c0ba-Results/A2) People/1e85c0ba/([])" ], "problemMatcher": "$msCompile" }, diff --git a/Delete-By-Distinct/DeleteByDistinct.cs b/Delete-By-Distinct/DeleteByDistinct.cs index 63bff3c..9f2ed4d 100644 --- a/Delete-By-Distinct/DeleteByDistinct.cs +++ b/Delete-By-Distinct/DeleteByDistinct.cs @@ -19,30 +19,33 @@ public class DeleteByDistinct { } long ticks = DateTime.Now.Ticks; ILogger? log = Log.ForContext(); - Dictionary> longToCollection = new(); + Dictionary> keyValuePairs = new(); Configuration configuration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; configuration.Update(); log.Information(configuration.RootDirectory); + if (!appSettings.TicksForLong && !appSettings.SizeForLong) + throw new Exception("Check appSettings file!"); bool compareIsPopulatedAndNotTheSame = !string.IsNullOrEmpty(appSettings.CompareRootDirectory) && appSettings.CompareRootDirectory != configuration.RootDirectory; - Work(appSettings, ticks, log, configuration.RootDirectory, nameof(configuration.RootDirectory), options, longToCollection, logOnly: compareIsPopulatedAndNotTheSame); + Work(appSettings, ticks, log, configuration.RootDirectory, nameof(configuration.RootDirectory), options, keyValuePairs, logOnly: compareIsPopulatedAndNotTheSame); if (compareIsPopulatedAndNotTheSame) - Work(appSettings, ticks, log, appSettings.CompareRootDirectory, nameof(appSettings.CompareRootDirectory), options, longToCollection, logOnly: false); + Work(appSettings, ticks, log, appSettings.CompareRootDirectory, nameof(appSettings.CompareRootDirectory), options, keyValuePairs, logOnly: false); _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(appSettings.CompareRootDirectory); } - private static void Work(AppSettings appSettings, long ticks, ILogger log, string directory, string variable, ProgressBarOptions options, Dictionary> longToCollection, bool logOnly) + private static void Work(AppSettings appSettings, long ticks, ILogger log, string directory, string variable, ProgressBarOptions options, Dictionary> keyValuePairs, bool logOnly) { - long check; - string message; + string check; string logFile; - string checkName; + string message; int totalSeconds; + string checkName; FileInfo fileInfo; - ProgressBar progressBar; List? fileNames; + ProgressBar progressBar; ConsoleKey? consoleKey = null; List deletedFiles = new(); + int pad = ticks.ToString().Length + 1; List deletedDirectories = new(); Dictionary> longToCollectionB = new(); List<(string Source, string Destination)> renameFiles = new(); @@ -59,29 +62,43 @@ public class DeleteByDistinct if (file.EndsWith(".id") || file.Contains("Rename") || file.EndsWith(".lsv")) continue; fileInfo = new(file); - if (appSettings.SizeForLong) + if (appSettings.SizeForLong && appSettings.TicksForLong) { if (fileInfo.Length < 100) continue; - check = fileInfo.Length; + if (fileInfo.LastWriteTime.Hour == 0 && fileInfo.LastWriteTime.Minute == 0 && fileInfo.LastWriteTime.Second == 0) + continue; + check = string.Concat(fileInfo.Length.ToString().PadRight(pad, '0'), fileInfo.LastWriteTime.Ticks); + if (!check.ToString().StartsWith(fileInfo.Length.ToString())) + throw new NotSupportedException(); + } + else if (appSettings.SizeForLong) + { + if (fileInfo.Length < 100) + continue; + check = fileInfo.Length.ToString(); } else if (appSettings.TicksForLong) { if (fileInfo.LastWriteTime.Hour == 0 && fileInfo.LastWriteTime.Minute == 0 && fileInfo.LastWriteTime.Second == 0) continue; - check = fileInfo.LastWriteTime.Ticks; + check = fileInfo.LastWriteTime.Ticks.ToString(); } else throw new Exception(); - if (!longToCollection.TryGetValue(check, out fileNames)) + if (!keyValuePairs.TryGetValue(check, out fileNames)) { - longToCollection.Add(check, new()); - if (!longToCollection.TryGetValue(check, out fileNames)) + keyValuePairs.Add(check, new()); + if (!keyValuePairs.TryGetValue(check, out fileNames)) throw new Exception(); } checkName = fileInfo.Name.ToLower().Replace(".jpeg", ".jpg"); - if (!logOnly && appSettings.RenameToMatch && fileNames.Count == 1 && fileInfo.DirectoryName is not null && fileInfo.Name != fileNames.First()) + if (!fileNames.Any()) + fileNames.Add(checkName); + else if (!logOnly && appSettings.RenameToMatch && fileNames.Count == 1 && fileInfo.DirectoryName is not null && fileInfo.Name != fileNames.First()) renameFiles.Add((fileInfo.FullName, Path.Combine(fileInfo.DirectoryName, fileNames.First()))); + // else if (fileNames.Count == 1) + // deletedFiles.Add(file); else if (fileNames.Contains(checkName)) deletedFiles.Add(file); else diff --git a/Delete-By-Distinct/Models/Binder/AppSettings.cs b/Delete-By-Distinct/Models/Binder/AppSettings.cs index 2594f68..1fade2a 100644 --- a/Delete-By-Distinct/Models/Binder/AppSettings.cs +++ b/Delete-By-Distinct/Models/Binder/AppSettings.cs @@ -40,8 +40,6 @@ public class AppSettings throw new NullReferenceException(nameof(appSettings.SizeForLong)); if (appSettings?.TicksForLong is null) throw new NullReferenceException(nameof(appSettings.TicksForLong)); - if (appSettings.TicksForLong.Value == appSettings.SizeForLong.Value) - throw new Exception("Check appSettings file!"); result = new( appSettings.Company, appSettings.CompareRootDirectory, diff --git a/Drag-Drop-Move/DragDropMove.cs b/Drag-Drop-Move/DragDropMove.cs index 79e1f0f..e456725 100644 --- a/Drag-Drop-Move/DragDropMove.cs +++ b/Drag-Drop-Move/DragDropMove.cs @@ -2,11 +2,9 @@ using Microsoft.Extensions.Configuration; using Phares.Shared; using Serilog; using System.Diagnostics; -using System.Drawing.Imaging; using System.Reflection; using System.Text.Json; using View_by_Distance.Drag_Drop_Explorer.Models; -using View_by_Distance.Shared.Models.Stateless; using View_by_Distance.Shared.Models.Stateless.Methods; using WindowsShortcutFactory; @@ -177,145 +175,12 @@ public partial class DragDropMove : Form } } - public static byte[] GetBytes(string value) - { - byte[] results = new byte[value.Length + 1]; - for (int i = 0; i < value.Length; i++) - results[i] = (byte)value[i]; - results[value.Length] = 0x00; - return results; - } - - private static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, string value) - { - PropertyItem result = (PropertyItem)constructorInfo.Invoke(null); - byte[] bytes = GetBytes(value); - result.Id = id; - result.Len = value.Length + 1; - result.Type = 2; - result.Value = bytes; - return result; - } - - private static List<(string, int, DateTime)> GetCollection(string checkDirectory, DateTime minimumDateTime, DateTime maximumDateTime, long ticks) - { - List<(string, int, DateTime)> results = new(); - DateTime dateTime; - Shared.Models.Property property; - string[] files = Directory.GetFiles(checkDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string file in files) - { - property = Property.Models.A_Property.GetImageProperty(file); - if (property.Id is null || property.DateTimeOriginal is null) - continue; - dateTime = property.DateTimeOriginal.Value.AddTicks(ticks); - if (dateTime < minimumDateTime) - continue; - if (dateTime > maximumDateTime) - continue; - results.Add((file, property.Id.Value, property.DateTimeOriginal.Value.AddTicks(ticks))); - } - if (files.Length != results.Count) - throw new Exception(); - return results; - } - - private static void DateFix(string sourceDirectory, string checkDirectory, DateTime minimumDateTime, DateTime maximumDateTime, long ticks) - { - Bitmap bitmap; - string checkFile; - PropertyItem? propertyItem; - string? ticksDirectory = null; - Shared.Models.Property property; - int dateTimeOriginal = (int)IExif.Tags.DateTimeOriginal; - for (int i = 0; i < int.MaxValue; i++) - { - ticksDirectory = Path.Combine(sourceDirectory, ticks.ToString()); - if (!Directory.Exists(ticksDirectory)) - { - _ = Directory.CreateDirectory(ticksDirectory); - break; - } - ticks++; - - } - List<(string, int, DateTime)> collection = GetCollection(checkDirectory, minimumDateTime, maximumDateTime, ticks); - ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, Array.Empty(), null) ?? throw new Exception(); - foreach ((string file, int id, DateTime dateTime) in collection) - { - if (ticksDirectory is null) - throw new Exception(); - checkFile = Path.Combine(ticksDirectory, Path.GetFileName(file)); - if (File.Exists(checkFile)) - continue; - propertyItem = GetPropertyItem(constructorInfo, dateTimeOriginal, dateTime.ToString("yyyy:MM:dd HH:mm:ss")); - bitmap = new(file); - bitmap.SetPropertyItem(propertyItem); - bitmap.Save(checkFile); - bitmap.Dispose(); - property = Property.Models.A_Property.GetImageProperty(checkFile); - if (property.Id is null || property.Id.Value != id) - throw new Exception(); - } - } - - private void DateFix(string sourceDirectory) - { - string checkDirectory; - long oneYearTicks = DateTime.MinValue.AddYears(1).Ticks; - checkDirectory = Path.Combine(sourceDirectory, oneYearTicks.ToString()); - if (Directory.Exists(checkDirectory)) - DateFix(sourceDirectory, checkDirectory, DateTime.MinValue, DateTime.MaxValue, new TimeSpan(oneYearTicks - DateTime.MinValue.Ticks).Ticks); - else - { - checkDirectory = Path.Combine(sourceDirectory, "1"); - if (!Directory.Exists(checkDirectory)) - _Logger.Error($"<{checkDirectory}> doesn't exist!"); - else - { - string badDirectory = Path.Combine(sourceDirectory, "Bad"); - string targetDirectory = Path.Combine(sourceDirectory, "Target"); - string[] minimumDirectory = Directory.GetDirectories(targetDirectory, "*", SearchOption.TopDirectoryOnly); - if (minimumDirectory.Length != 1) - _Logger.Error($"<{checkDirectory}> doesn't exist!"); - else - { - string format = "yyyy-MM-dd"; - string[] maximumDirectory = Directory.GetDirectories(minimumDirectory.First(), "*", SearchOption.TopDirectoryOnly); - string[] badFiles = !Directory.Exists(badDirectory) ? Array.Empty() : Directory.GetFiles(badDirectory, "*", SearchOption.TopDirectoryOnly); - string[] targetFiles = !Directory.Exists(targetDirectory) ? Array.Empty() : Directory.GetFiles(targetDirectory, "*", SearchOption.TopDirectoryOnly); - if (badFiles.Length != 1 || targetFiles.Length != 1 || maximumDirectory.Length != 1) - _Logger.Error("bad file(s) or target file(s) or maximum directory doesn't equal 1!"); - else - { - DateTime minimumDateTime = DateTime.ParseExact(Path.GetFileName(minimumDirectory.First()), format, null, System.Globalization.DateTimeStyles.None); - DateTime maximumDateTime = DateTime.ParseExact(Path.GetFileName(maximumDirectory.First()), format, null, System.Globalization.DateTimeStyles.None).AddHours(23); - Shared.Models.Property badProperty = Property.Models.A_Property.GetImageProperty(badFiles.First()); - Shared.Models.Property targetProperty = Property.Models.A_Property.GetImageProperty(targetFiles.First()); - if (badProperty.DateTimeOriginal is null || targetProperty.DateTimeOriginal is null) - _Logger.Error("Date is null!"); - else - { - TimeSpan timeSpan = new(targetProperty.DateTimeOriginal.Value.Ticks - badProperty.DateTimeOriginal.Value.Ticks); - DateFix(sourceDirectory, checkDirectory, minimumDateTime, maximumDateTime, timeSpan.Ticks); - } - } - } - } - } - } - private void Form1_DragDrop(object? sender, DragEventArgs e) { try { if (e.Data is not null && e.Data.GetData(DataFormats.FileDrop) is string[] paths && paths.Any()) - { - if (paths.Length == 1 && Directory.Exists(paths.First())) - DateFix(paths.First()); - else - MovePaths(paths); - } + MovePaths(paths); else { _FirstTextBox.Text = string.Empty; diff --git a/Metadata/Models/Stateless/Methods/IMetadata.cs b/Metadata/Models/Stateless/Methods/IMetadata.cs index ce5f849..77b3019 100644 --- a/Metadata/Models/Stateless/Methods/IMetadata.cs +++ b/Metadata/Models/Stateless/Methods/IMetadata.cs @@ -3,15 +3,10 @@ namespace View_by_Distance.Metadata.Models.Stateless.Methods; public interface IMetadata { - DateTime? TestStatic_GetMinimumDateTime(IReadOnlyList directories) => - GetMinimumDateTime(directories); - static DateTime? GetMinimumDateTime(IReadOnlyList directories) => - Metadata.GetMinimumDateTime(directories); - - DateTime? TestStatic_GetMinimumDateTime(IReadOnlyList directories, Shared.Models.FileHolder fileHolder) => - GetMinimumDateTime(directories, fileHolder); - static DateTime? GetMinimumDateTime(IReadOnlyList directories, Shared.Models.FileHolder fileHolder) => - Metadata.GetMinimumDateTime(directories, fileHolder); + (DateTime?, DateTime?[]) TestStatic_GetDateTimes(Shared.Models.FileHolder fileHolder, IReadOnlyList directories) => + GetDateTimes(fileHolder, directories); + static (DateTime?, DateTime?[]) GetDateTimes(Shared.Models.FileHolder fileHolder, IReadOnlyList directories) => + Metadata.GetDateTimes(fileHolder, directories); string? TestStatic_GetModel(IReadOnlyList directories) => GetModel(directories); diff --git a/Metadata/Models/Stateless/Methods/Metadata.cs b/Metadata/Models/Stateless/Methods/Metadata.cs index f7cae7d..02288bf 100644 --- a/Metadata/Models/Stateless/Methods/Metadata.cs +++ b/Metadata/Models/Stateless/Methods/Metadata.cs @@ -53,53 +53,104 @@ internal class Metadata return result; } - internal static DateTime? GetMinimumDateTime(IReadOnlyList directories) + internal static (DateTime?, DateTime?[]) GetDateTimes(Shared.Models.FileHolder fileHolder, IReadOnlyList directories) { - DateTime? result; - DateTime dateTime; - List results = new(); + List results = new(); + DateTime? result = null; + DateTime? dateTime; + DateTime checkDateTime; + string dateTimeFormat = Shared.Models.Stateless.Methods.IProperty.DateTimeFormat(); ExifDirectoryBase? exifDirectoryBase = directories.OfType().FirstOrDefault(); + results.Add(fileHolder.CreationTime); + results.Add(fileHolder.LastWriteTime); if (exifDirectoryBase is not null) { - if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTime, out dateTime)) - results.Add(dateTime); - if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out dateTime)) - results.Add(dateTime); - if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out dateTime)) - results.Add(dateTime); + if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime)) + results.Add(checkDateTime); + else + { + dateTime = Shared.Models.Stateless.Methods.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTime)); + if (dateTime is not null) + results.Add(dateTime.Value); + } + if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime)) + results.Add(checkDateTime); + else + { + dateTime = Shared.Models.Stateless.Methods.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized)); + if (dateTime is not null) + results.Add(dateTime.Value); + } + if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime)) + { + result ??= checkDateTime; + results.Add(checkDateTime); + } + else + { + dateTime = Shared.Models.Stateless.Methods.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeOriginal)); + if (dateTime is not null) + { + result ??= dateTime.Value; + results.Add(dateTime.Value); + } + } } AviDirectory? aviDirectory = directories.OfType().FirstOrDefault(); if (aviDirectory is not null) { - if (aviDirectory.TryGetDateTime(AviDirectory.TagDateTimeOriginal, out dateTime)) - results.Add(dateTime); + if (aviDirectory.TryGetDateTime(AviDirectory.TagDateTimeOriginal, out checkDateTime)) + { + result ??= checkDateTime; + results.Add(checkDateTime); + } + else + { + dateTime = Shared.Models.Stateless.Methods.IProperty.GetDateTime(dateTimeFormat, aviDirectory.GetString(AviDirectory.TagDateTimeOriginal)); + if (dateTime is not null) + { + result ??= dateTime.Value; + results.Add(dateTime.Value); + } + } } QuickTimeMovieHeaderDirectory? quickTimeMovieHeaderDirectory = directories.OfType().FirstOrDefault(); if (quickTimeMovieHeaderDirectory is not null) { - if (quickTimeMovieHeaderDirectory.TryGetDateTime(QuickTimeMovieHeaderDirectory.TagCreated, out dateTime)) - results.Add(dateTime); + if (quickTimeMovieHeaderDirectory.TryGetDateTime(QuickTimeMovieHeaderDirectory.TagCreated, out checkDateTime)) + { + result ??= checkDateTime; + results.Add(checkDateTime); + } + else + { + dateTime = Shared.Models.Stateless.Methods.IProperty.GetDateTime(dateTimeFormat, quickTimeMovieHeaderDirectory.GetString(QuickTimeMovieHeaderDirectory.TagCreated)); + if (dateTime is not null) + { + result ??= dateTime.Value; + results.Add(dateTime.Value); + } + } } QuickTimeTrackHeaderDirectory? quickTimeTrackHeaderDirectory = directories.OfType().FirstOrDefault(); if (quickTimeTrackHeaderDirectory is not null) { - if (quickTimeTrackHeaderDirectory.TryGetDateTime(QuickTimeTrackHeaderDirectory.TagCreated, out dateTime)) - results.Add(dateTime); + if (quickTimeTrackHeaderDirectory.TryGetDateTime(QuickTimeTrackHeaderDirectory.TagCreated, out checkDateTime)) + { + result ??= checkDateTime; + results.Add(checkDateTime); + } + else + { + dateTime = Shared.Models.Stateless.Methods.IProperty.GetDateTime(dateTimeFormat, quickTimeTrackHeaderDirectory.GetString(QuickTimeTrackHeaderDirectory.TagCreated)); + if (dateTime is not null) + { + result ??= dateTime.Value; + results.Add(dateTime.Value); + } + } } - if (results.Any()) - result = results.Min(); - else - result = null; - return result; - } - - internal static DateTime? GetMinimumDateTime(IReadOnlyList directories, Shared.Models.FileHolder fileHolder) - { - DateTime? result; - DateTime? dateTime = GetMinimumDateTime(directories); - List results = new() { fileHolder.CreationTime, fileHolder.LastWriteTime, dateTime }; - result = results.Min(); - return result; + return new(result, results.ToArray()); } internal static string? GetModel(IReadOnlyList directories) diff --git a/Mirror-Length/MirrorLength.cs b/Mirror-Length/MirrorLength.cs index 4170b60..7a21a6b 100644 --- a/Mirror-Length/MirrorLength.cs +++ b/Mirror-Length/MirrorLength.cs @@ -60,11 +60,9 @@ public class MirrorLength { List<(string, string, DateTime, long)[]> results = new(); FileInfo fileInfo; - List lines = new(); List<(string, string, DateTime, long)> collection; foreach (string[] files in filesCollection) { - lines.Clear(); progressBar.Tick(); collection = new(); foreach (string file in files) @@ -83,7 +81,36 @@ public class MirrorLength return results; } - private void Save(ProgressBar progressBar, List<(string directory, string file, DateTime lastWriteTime, long length)[]> results) + private List<(string, string, int)> GetToDoCollectionForMarkDown(string message, string today) + { + string[] files; + ProgressBar progressBar; + string directoryName; + string[] subDirectories; + string subDirectoryName; + List<(string, string, int)> results = new(); + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + string[] directories = Directory.GetDirectories(_PropertyConfiguration.RootDirectory, "*", SearchOption.TopDirectoryOnly); + progressBar = new(directories.Length, message, options); + foreach (string directory in directories) + { + progressBar.Tick(); + if (!directory.Contains(today)) + continue; + directoryName = Path.GetFileName(directory); + subDirectories = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly); + foreach (string subDirectory in subDirectories) + { + subDirectoryName = Path.GetFileName(subDirectory); + files = Directory.GetFiles(subDirectory, "*len", SearchOption.AllDirectories); + results.Add(new(directoryName, subDirectoryName, files.Length)); + } + } + progressBar.Dispose(); + return results; + } + + private void Write(bool inPlaceSave, ProgressBar progressBar, List<(string directory, string file, DateTime lastWriteTime, long length)[]> results) { string checkFile; string checkDirectory; @@ -96,13 +123,17 @@ public class MirrorLength firstDirectory = collection.First().Directory; if (string.IsNullOrEmpty(firstDirectory)) continue; - checkDirectory = $"{_AppSettings.Destination}{firstDirectory[1..]}"; - if (Directory.Exists(checkDirectory)) - continue; - _ = Directory.CreateDirectory(checkDirectory); + checkDirectory = inPlaceSave ? firstDirectory : $"{_AppSettings.Destination}{firstDirectory[1..]}"; + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + else + { + if (!inPlaceSave) + continue; + } foreach ((string directory, string _, DateTime _, long _) in collection) { - checkDirectory = $"{_AppSettings.Destination}{directory[1..]}"; + checkDirectory = inPlaceSave ? directory : $"{_AppSettings.Destination}{directory[1..]}"; if (directories.Contains(checkDirectory)) continue; directories.Add(checkDirectory); @@ -111,7 +142,7 @@ public class MirrorLength } foreach ((string _, string file, DateTime lastWriteTime, long length) in collection) { - checkFile = $"{_AppSettings.Destination}{file[1..]}len"; + checkFile = inPlaceSave ? $"{file}len" : $"{_AppSettings.Destination}{file[1..]}len"; if (!Shared.Models.Stateless.Methods.IPath.WriteAllText(checkFile, length.ToString(), updateToWhenMatches: null, compareBeforeWrite: true, updateDateWhenMatches: false)) continue; File.SetLastWriteTime(checkFile, lastWriteTime); @@ -119,22 +150,77 @@ public class MirrorLength } } + private void Write(string today, List<(string directoryName, string subDirectoryName, int count)> collection) + { + string key; + string text; + string subKey; + string checkFile; + Dictionary> directoryNameToCount = new(); + Dictionary> subDirectoryNameToCount = new(); + foreach ((string directoryName, string subDirectoryName, int count) in collection) + { + key = directoryName.Replace(today, string.Empty).Replace(")", "-"); + subKey = subDirectoryName.Replace(today, string.Empty).Replace(") ", "-").Replace(" ", "-"); + if (!directoryNameToCount.ContainsKey(key)) + directoryNameToCount.Add(key, new() { "---", $"title: {key}", $"desc: '{directoryName}'", "---", string.Empty }); + if (count == 0) + directoryNameToCount[key].Add($"- <00000000> '{subDirectoryName}'"); + else + directoryNameToCount[key].Add($"- <{count:00000000}> [[{subKey}]]"); + if (!subDirectoryNameToCount.ContainsKey(subKey)) + subDirectoryNameToCount.Add(subKey, new() { "---", $"title: {subKey}", $"desc: '{subDirectoryName}'", "---", string.Empty }); + if (count == 0) + subDirectoryNameToCount[subKey].Add($"- <00000000> '{directoryName}'"); + else + subDirectoryNameToCount[subKey].Add($"- <{count:00000000}> [[{key}]]"); + } + foreach (KeyValuePair> keyValuePair in directoryNameToCount) + { + text = string.Join(Environment.NewLine, keyValuePair.Value); + checkFile = Path.Combine(_PropertyConfiguration.RootDirectory, $"{keyValuePair.Key}.md"); + if (!Shared.Models.Stateless.Methods.IPath.WriteAllText(checkFile, text, updateToWhenMatches: null, compareBeforeWrite: true, updateDateWhenMatches: false)) + continue; + } + foreach (KeyValuePair> keyValuePair in subDirectoryNameToCount) + { + text = string.Join(Environment.NewLine, keyValuePair.Value); + checkFile = Path.Combine(_PropertyConfiguration.RootDirectory, $"{keyValuePair.Key}.md"); + if (!Shared.Models.Stateless.Methods.IPath.WriteAllText(checkFile, text, updateToWhenMatches: null, compareBeforeWrite: true, updateDateWhenMatches: false)) + continue; + } + } + private List MirrorLengthFilesInDirectories(ILogger log) { List results = new(); - ProgressBar progressBar; - const string fileSearchFilter = "*"; string message = nameof(MirrorLength); - const string directorySearchFilter = "*"; - List distinctDirectories = new(); - List filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter); - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - progressBar = new(filesCollection.Count, message, options); - List<(string, string, DateTime, long)[]> collection = GetToDoCollection(progressBar, filesCollection); - progressBar.Dispose(); - progressBar = new(filesCollection.Count, message, options); - Save(progressBar, collection); - progressBar.Dispose(); + List<(string, string, int)> collectionForMarkDown; + bool inPlaceSave = _PropertyConfiguration.RootDirectory.First() == _AppSettings.Destination; + if (!inPlaceSave) + collectionForMarkDown = new(); + else + { + string today = DateTime.Now.ToString("yyyy-MM-dd-"); + collectionForMarkDown = GetToDoCollectionForMarkDown(message, today); + if (collectionForMarkDown.Any()) + Write(today, collectionForMarkDown); + } + if (!collectionForMarkDown.Any()) + { + ProgressBar progressBar; + const string fileSearchFilter = "*"; + const string directorySearchFilter = "*"; + List filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter); + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + progressBar = new(filesCollection.Count, message, options); + List<(string, string, DateTime, long)[]> collection = GetToDoCollection(progressBar, filesCollection); + progressBar.Dispose(); + progressBar = new(filesCollection.Count, message, options); + if (collection.Any()) + Write(inPlaceSave, progressBar, collection); + progressBar.Dispose(); + } return results; } diff --git a/Mirror-Length/Models/AppSettings.cs b/Mirror-Length/Models/AppSettings.cs index 3bbf47f..1ac6a6e 100644 --- a/Mirror-Length/Models/AppSettings.cs +++ b/Mirror-Length/Models/AppSettings.cs @@ -2,10 +2,10 @@ using System.Text.Json; namespace View_by_Distance.Mirror.Length.Models; - public record AppSettings(string Company, - char Destination, - int MaxDegreeOfParallelism, - string WorkingDirectoryName) +public record AppSettings(string Company, + char Destination, + int MaxDegreeOfParallelism, + string WorkingDirectoryName) { public override string ToString() diff --git a/Offset-Date-Time-Original/Models/AppSettings.cs b/Offset-Date-Time-Original/Models/AppSettings.cs new file mode 100644 index 0000000..23d8fb2 --- /dev/null +++ b/Offset-Date-Time-Original/Models/AppSettings.cs @@ -0,0 +1,16 @@ +using System.Text.Json; + +namespace View_by_Distance.Offset.Date.Time.Original.Models; + +public record AppSettings(string Company, + int MaxDegreeOfParallelism, + string WorkingDirectoryName) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + +} \ No newline at end of file diff --git a/Offset-Date-Time-Original/Models/Binder/AppSettings.cs b/Offset-Date-Time-Original/Models/Binder/AppSettings.cs new file mode 100644 index 0000000..f904add --- /dev/null +++ b/Offset-Date-Time-Original/Models/Binder/AppSettings.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.Configuration; +using System.Text.Json; + +namespace View_by_Distance.Offset.Date.Time.Original.Models.Binder; + +public class AppSettings +{ + + public string? Company { get; set; } + public int? MaxDegreeOfParallelism { get; set; } + public string? WorkingDirectoryName { get; set; } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + + private static Models.AppSettings Get(AppSettings? appSettings) + { + Models.AppSettings result; + if (appSettings?.Company is null) + throw new NullReferenceException(nameof(appSettings.Company)); + if (appSettings?.MaxDegreeOfParallelism is null) + throw new NullReferenceException(nameof(appSettings.MaxDegreeOfParallelism)); + if (appSettings?.WorkingDirectoryName is null) + throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName)); + result = new( + appSettings.Company, + appSettings.MaxDegreeOfParallelism.Value, + appSettings.WorkingDirectoryName + ); + return result; + } + + public static Models.AppSettings Get(IConfigurationRoot configurationRoot) + { + Models.AppSettings result; + AppSettings? appSettings = configurationRoot.Get(); + result = Get(appSettings); + return result; + } + +} \ No newline at end of file diff --git a/Offset-Date-Time-Original/Models/Binder/Configuration.cs b/Offset-Date-Time-Original/Models/Binder/Configuration.cs new file mode 100644 index 0000000..3aa5ea0 --- /dev/null +++ b/Offset-Date-Time-Original/Models/Binder/Configuration.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Configuration; +using Phares.Shared; +using System.ComponentModel.DataAnnotations; +using System.Text.Json; + +namespace View_by_Distance.Offset.Date.Time.Original.Models.Binder; + +public class Configuration +{ + +#nullable disable + + [Display(Name = "Ignore Extensions"), Required] public string[] IgnoreExtensions { get; set; } + [Display(Name = "Property Configuration"), Required] public Property.Models.Configuration PropertyConfiguration { get; set; } + [Display(Name = "Person Birthday Format"), Required] public string PersonBirthdayFormat { get; set; } + +#nullable restore + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + + private static Models.Configuration Get(Configuration? configuration) + { + Models.Configuration result; + if (configuration is null) + throw new NullReferenceException(nameof(configuration)); + if (configuration.IgnoreExtensions is null) + throw new NullReferenceException(nameof(configuration.IgnoreExtensions)); + if (configuration.PersonBirthdayFormat is null) + throw new NullReferenceException(nameof(configuration.PersonBirthdayFormat)); + result = new( + configuration.IgnoreExtensions, + configuration.PersonBirthdayFormat, + configuration.PropertyConfiguration); + return result; + } + + public static Models.Configuration Get(IsEnvironment isEnvironment, IConfigurationRoot configurationRoot, Property.Models.Configuration propertyConfiguration) + { + Models.Configuration result; + Configuration? configuration; + if (isEnvironment is null) + configuration = configurationRoot.Get(); + else + { + string environmentName = IsEnvironment.GetEnvironmentName(isEnvironment); + string section = string.Concat(environmentName, ":", nameof(Configuration)); + IConfigurationSection configurationSection = configurationRoot.GetSection(section); + configuration = configurationSection.Get(); + } + result = Get(configuration); + result.SetAndUpdate(propertyConfiguration); + return result; + } + +} \ No newline at end of file diff --git a/Offset-Date-Time-Original/Models/Configuration.cs b/Offset-Date-Time-Original/Models/Configuration.cs new file mode 100644 index 0000000..402754c --- /dev/null +++ b/Offset-Date-Time-Original/Models/Configuration.cs @@ -0,0 +1,38 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace View_by_Distance.Offset.Date.Time.Original.Models; + +public class Configuration +{ + + protected Property.Models.Configuration _PropertyConfiguration; + public string[] IgnoreExtensions { init; get; } + public string PersonBirthdayFormat { init; get; } + + public Property.Models.Configuration PropertyConfiguration => _PropertyConfiguration; + + [JsonConstructor] + public Configuration( + string[] ignoreExtensions, + string personBirthdayFormat, + Property.Models.Configuration propertyConfiguration) + { + IgnoreExtensions = ignoreExtensions; + PersonBirthdayFormat = personBirthdayFormat; + _PropertyConfiguration = propertyConfiguration; + } + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + + public void SetAndUpdate(Property.Models.Configuration configuration) + { + _PropertyConfiguration = configuration; + _PropertyConfiguration.Update(); + } + +} \ No newline at end of file diff --git a/Offset-Date-Time-Original/Models/Stateless/SerilogExtensionMethods.cs b/Offset-Date-Time-Original/Models/Stateless/SerilogExtensionMethods.cs new file mode 100644 index 0000000..65edf24 --- /dev/null +++ b/Offset-Date-Time-Original/Models/Stateless/SerilogExtensionMethods.cs @@ -0,0 +1,10 @@ +namespace View_by_Distance.Offset.Date.Time.Original.Models.Stateless; + +public static class SerilogExtensionMethods +{ + + internal static void Warn(this Serilog.ILogger log, string messageTemplate) => log.Warning(messageTemplate); + + internal static void Info(this Serilog.ILogger log, string messageTemplate) => log.Information(messageTemplate); + +} \ No newline at end of file diff --git a/Offset-Date-Time-Original/Offset-Date-Time-Original.csproj b/Offset-Date-Time-Original/Offset-Date-Time-Original.csproj new file mode 100644 index 0000000..6206b71 --- /dev/null +++ b/Offset-Date-Time-Original/Offset-Date-Time-Original.csproj @@ -0,0 +1,60 @@ + + + enable + 10.0 + enable + Exe + win-x64 + net7.0 + ae069946-d0c0-4e4f-94f6-9c526e4ae4e7 + + + Phares.View.by.Distance.Offset.Date.Time.Original + false + 7.0.101.1 + Mike Phares + Phares + true + snupkg + + + true + true + true + + + Windows + + + OSX + + + Linux + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + \ No newline at end of file diff --git a/Offset-Date-Time-Original/OffsetDateTimeOriginal.cs b/Offset-Date-Time-Original/OffsetDateTimeOriginal.cs new file mode 100644 index 0000000..555885c --- /dev/null +++ b/Offset-Date-Time-Original/OffsetDateTimeOriginal.cs @@ -0,0 +1,211 @@ +using Microsoft.Extensions.Configuration; +using Phares.Shared; +using Serilog; +using ShellProgressBar; +using System.Drawing; +using System.Drawing.Imaging; +using System.Reflection; +using View_by_Distance.Offset.Date.Time.Original.Models; +using View_by_Distance.Shared.Models.Methods; +using View_by_Distance.Shared.Models.Stateless; + +namespace View_by_Distance.Offset.Date.Time.Original; + +public class OffsetDateTimeOriginal +{ + + private readonly AppSettings _AppSettings; + private readonly string _WorkingDirectory; + private readonly Configuration _Configuration; + private readonly IsEnvironment _IsEnvironment; + private readonly IConfigurationRoot _ConfigurationRoot; + private readonly Property.Models.Configuration _PropertyConfiguration; + + public OffsetDateTimeOriginal(List args, IsEnvironment isEnvironment, IConfigurationRoot configurationRoot, AppSettings appSettings, string workingDirectory, bool isSilent, IConsole console) + { + if (isSilent) + { } + if (console is null) + { } + _AppSettings = appSettings; + _IsEnvironment = isEnvironment; + _WorkingDirectory = workingDirectory; + _ConfigurationRoot = configurationRoot; + ILogger? log = Log.ForContext(); + Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); + Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); + _PropertyConfiguration = propertyConfiguration; + _Configuration = configuration; + propertyConfiguration.Update(); + log.Information(propertyConfiguration.RootDirectory); + Verify(); + List lines = OffsetDateTimeOriginalFilesInDirectories(log); + if (!lines.Any()) + File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); + } + + private void Verify() + { + if (_AppSettings is null) + { } + if (_IsEnvironment is null) + { } + if (_Configuration is null) + { } + if (_ConfigurationRoot is null) + { } + if (_WorkingDirectory is null) + { } + if (_PropertyConfiguration is null) + { } + } + + private static byte[] GetBytes(string value) + { + byte[] results = new byte[value.Length + 1]; + for (int i = 0; i < value.Length; i++) + results[i] = (byte)value[i]; + results[value.Length] = 0x00; + return results; + } + +#pragma warning disable CA1416 + + private static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, string value) + { + PropertyItem result = (PropertyItem)constructorInfo.Invoke(null); + byte[] bytes = GetBytes(value); + result.Id = id; + result.Len = value.Length + 1; + result.Type = 2; + result.Value = bytes; + return result; + } + +#pragma warning restore CA1416 + + private static List<(string, int, DateTime)> GetCollection(string checkDirectory, DateTime minimumDateTime, DateTime maximumDateTime, long ticks) + { + List<(string, int, DateTime)> results = new(); + DateTime dateTime; + Shared.Models.Property property; + string[] files = Directory.GetFiles(checkDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string file in files) + { + property = Property.Models.A_Property.GetImageProperty(file); + if (property.Id is null || property.DateTimeOriginal is null) + continue; + dateTime = property.DateTimeOriginal.Value.AddTicks(ticks); + if (dateTime < minimumDateTime) + continue; + if (dateTime > maximumDateTime) + continue; + results.Add((file, property.Id.Value, property.DateTimeOriginal.Value.AddTicks(ticks))); + } + if (files.Length != results.Count) + throw new Exception(); + return results; + } + +#pragma warning disable CA1416 + + private static void DateFix(string sourceDirectory, string checkDirectory, DateTime minimumDateTime, DateTime maximumDateTime, long ticks) + { + Bitmap bitmap; + string checkFile; + PropertyItem? propertyItem; + string? ticksDirectory = null; + Shared.Models.Property property; + int dateTimeOriginal = (int)IExif.Tags.DateTimeOriginal; + for (int i = 0; i < int.MaxValue; i++) + { + ticksDirectory = Path.Combine(sourceDirectory, ticks.ToString()); + if (!Directory.Exists(ticksDirectory)) + { + _ = Directory.CreateDirectory(ticksDirectory); + break; + } + ticks++; + + } + List<(string, int, DateTime)> collection = GetCollection(checkDirectory, minimumDateTime, maximumDateTime, ticks); + ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, Array.Empty(), null) ?? throw new Exception(); + string message = nameof(OffsetDateTimeOriginal); + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + ProgressBar progressBar = new(collection.Count, message, options); + foreach ((string file, int id, DateTime dateTime) in collection) + { + progressBar.Tick(); + if (ticksDirectory is null) + throw new Exception(); + checkFile = Path.Combine(ticksDirectory, Path.GetFileName(file)); + if (File.Exists(checkFile)) + continue; + propertyItem = GetPropertyItem(constructorInfo, dateTimeOriginal, dateTime.ToString("yyyy:MM:dd HH:mm:ss")); + bitmap = new(file); + bitmap.SetPropertyItem(propertyItem); + bitmap.Save(checkFile); + bitmap.Dispose(); + property = Property.Models.A_Property.GetImageProperty(checkFile); + if (property.Id is null || property.Id.Value != id) + throw new Exception(); + } + progressBar.Dispose(); + } + +#pragma warning restore CA1416 + + private static void DateFix(ILogger log, string sourceDirectory) + { + string checkDirectory; + long oneYearTicks = DateTime.MinValue.AddYears(1).Ticks; + checkDirectory = Path.Combine(sourceDirectory, oneYearTicks.ToString()); + if (Directory.Exists(checkDirectory)) + DateFix(sourceDirectory, checkDirectory, DateTime.MinValue, DateTime.MaxValue, new TimeSpan(oneYearTicks - DateTime.MinValue.Ticks).Ticks); + else + { + checkDirectory = Path.Combine(sourceDirectory, "1"); + if (!Directory.Exists(checkDirectory)) + log.Error($"<{checkDirectory}> doesn't exist!"); + else + { + string badDirectory = Path.Combine(sourceDirectory, "Bad"); + string targetDirectory = Path.Combine(sourceDirectory, "Target"); + string[] minimumDirectory = Directory.GetDirectories(targetDirectory, "*", SearchOption.TopDirectoryOnly); + if (minimumDirectory.Length != 1) + log.Error($"<{checkDirectory}> doesn't exist!"); + else + { + string format = "yyyy-MM-dd"; + string[] maximumDirectory = Directory.GetDirectories(minimumDirectory.First(), "*", SearchOption.TopDirectoryOnly); + string[] badFiles = !Directory.Exists(badDirectory) ? Array.Empty() : Directory.GetFiles(badDirectory, "*", SearchOption.TopDirectoryOnly); + string[] targetFiles = !Directory.Exists(targetDirectory) ? Array.Empty() : Directory.GetFiles(targetDirectory, "*", SearchOption.TopDirectoryOnly); + if (badFiles.Length != 1 || targetFiles.Length != 1 || maximumDirectory.Length != 1) + log.Error("bad file(s) or target file(s) or maximum directory doesn't equal 1!"); + else + { + DateTime minimumDateTime = DateTime.ParseExact(Path.GetFileName(minimumDirectory.First()), format, null, System.Globalization.DateTimeStyles.None); + DateTime maximumDateTime = DateTime.ParseExact(Path.GetFileName(maximumDirectory.First()), format, null, System.Globalization.DateTimeStyles.None).AddHours(23); + Shared.Models.Property badProperty = Property.Models.A_Property.GetImageProperty(badFiles.First()); + Shared.Models.Property targetProperty = Property.Models.A_Property.GetImageProperty(targetFiles.First()); + if (badProperty.DateTimeOriginal is null || targetProperty.DateTimeOriginal is null) + log.Error("Date is null!"); + else + { + TimeSpan timeSpan = new(targetProperty.DateTimeOriginal.Value.Ticks - badProperty.DateTimeOriginal.Value.Ticks); + DateFix(sourceDirectory, checkDirectory, minimumDateTime, maximumDateTime, timeSpan.Ticks); + } + } + } + } + } + } + + private List OffsetDateTimeOriginalFilesInDirectories(ILogger log) + { + List results = new(); + DateFix(log, _PropertyConfiguration.RootDirectory); + return results; + } + +} \ No newline at end of file diff --git a/Offset-Date-Time-Original/Program.cs b/Offset-Date-Time-Original/Program.cs new file mode 100644 index 0000000..1af8284 --- /dev/null +++ b/Offset-Date-Time-Original/Program.cs @@ -0,0 +1,71 @@ +using Microsoft.Extensions.Configuration; +using Phares.Shared; +using Serilog; +using System.Diagnostics; +using System.Reflection; +using View_by_Distance.Offset.Date.Time.Original.Models; +using View_by_Distance.Shared.Models.Stateless.Methods; + +namespace View_by_Distance.Offset.Date.Time.Original; + +public class Program +{ + + public static void Secondary(List args) + { + LoggerConfiguration loggerConfiguration = new(); + Assembly assembly = Assembly.GetExecutingAssembly(); + bool debuggerWasAttachedAtLineZero = Debugger.IsAttached || assembly.Location.Contains(@"\bin\Debug"); + IsEnvironment isEnvironment = new(processesCount: null, nullASPNetCoreEnvironmentIsDevelopment: debuggerWasAttachedAtLineZero, nullASPNetCoreEnvironmentIsProduction: !debuggerWasAttachedAtLineZero); + IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() + .AddEnvironmentVariables() + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); + IConfigurationRoot configurationRoot = configurationBuilder.Build(); + AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); + if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) + throw new Exception("MaxDegreeOfParallelism must be =< Environment.ProcessorCount!"); + if (string.IsNullOrEmpty(appSettings.WorkingDirectoryName)) + throw new Exception("Working directory name must have a value!"); + string workingDirectory = IWorkingDirectory.GetWorkingDirectory(assembly.GetName().Name, appSettings.WorkingDirectoryName); + Environment.SetEnvironmentVariable(nameof(workingDirectory), workingDirectory); + _ = ConfigurationLoggerConfigurationExtensions.Configuration(loggerConfiguration.ReadFrom, configurationRoot); + Log.Logger = loggerConfiguration.CreateLogger(); + ILogger log = Log.ForContext(); + int silentIndex = args.IndexOf("s"); + if (silentIndex > -1) + args.RemoveAt(silentIndex); + try + { + if (args is null) + throw new Exception("args is null!"); + Shared.Models.Console console = new(); + _ = new OffsetDateTimeOriginal(args, isEnvironment, configurationRoot, appSettings, workingDirectory, silentIndex > -1, console); + } + catch (Exception ex) + { + log.Fatal(string.Concat(ex.Message, Environment.NewLine, ex.StackTrace)); + } + finally + { + Log.CloseAndFlush(); + } + if (silentIndex > -1) + log.Debug("Done. Bye"); + else + { + log.Debug("Done. Press 'Enter' to end"); + _ = Console.ReadLine(); + } + } + + public static void Main(string[] args) + { + if (args is not null) + Secondary(args.ToList()); + else + Secondary(new List()); + } + +} \ No newline at end of file diff --git a/Offset-Date-Time-Original/appsettings.Development.json b/Offset-Date-Time-Original/appsettings.Development.json new file mode 100644 index 0000000..ad9619a --- /dev/null +++ b/Offset-Date-Time-Original/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Log4netProvider": "Debug" + } + }, + "Serilog": { + "MinimumLevel": "Debug" + } +} \ No newline at end of file diff --git a/Offset-Date-Time-Original/appsettings.json b/Offset-Date-Time-Original/appsettings.json new file mode 100644 index 0000000..ed0997e --- /dev/null +++ b/Offset-Date-Time-Original/appsettings.json @@ -0,0 +1,126 @@ +{ + "Company": "Mike Phares", + "Linux": {}, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Log4netProvider": "Debug", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "MaxDegreeOfParallelism": 6, + "Serilog": { + "Using": [ + "Serilog.Sinks.Console", + "Serilog.Sinks.File" + ], + "MinimumLevel": "Information", + "WriteTo": [ + { + "Name": "Debug", + "Args": { + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}.{MethodName}) ({InstanceId}) ({RemoteIpAddress}) {Message}{NewLine}{Exception}" + } + }, + { + "Name": "Console", + "Args": { + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}.{MethodName}) ({InstanceId}) ({RemoteIpAddress}) {Message}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "%workingDirectory% - Log/log-.txt", + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] ({SourceContext}.{MethodName}) ({InstanceId}) ({RemoteIpAddress}) {Message}{NewLine}{Exception}", + "rollingInterval": "Hour" + } + } + ], + "Enrich": [ + "FromLogContext", + "WithMachineName", + "WithThreadId" + ], + "Properties": { + "Application": "Sample" + } + }, + "WorkingDirectoryName": "PharesApps", + "Windows": { + "Configuration": { + "DateGroup": "dd514b88", + "DiffPropertyDirectory": "", + "FileNameDirectorySeparator": ".Z.", + "ForcePropertyLastWriteTimeToCreationTime": false, + "MaxImagesInDirectoryForTopLevelFirstPass": 10, + "OutputExtension": ".jpg", + "Pattern": "[^ABCDEFGHIJKLMNOPQRSTUVWXYZbcdfghjklmnpqrstvwxyz0-9]", + "PersonBirthdayFormat": "yyyy-MM-dd_HH", + "PopulatePropertyId": true, + "PropertiesChangedForProperty": false, + "ResultAllInOne": "_ _ _", + "ResultAllInOneSubdirectoryLength": 2, + "ResultCollection": "[]", + "ResultContent": "()", + "ResultSingleton": "{}", + "RootDirectory": "D:/Images", + "WriteBitmapDataBytes": false, + "IgnoreExtensions": [ + ".gif", + ".GIF", + ".nef", + ".NEF", + ".pdf", + ".PDF" + ], + "ValidImageFormatExtensions": [ + ".bmp", + ".BMP", + ".gif", + ".GIF", + ".jpeg", + ".JPEG", + ".jpg", + ".JPG", + ".png", + ".PNG", + ".tiff", + ".TIFF", + ".tif", + ".TIF" + ], + "ValidMetadataExtensions": [ + ".3gp", + ".3GP", + ".avi", + ".AVI", + ".bmp", + ".BMP", + ".gif", + ".GIF", + ".ico", + ".ICO", + ".jpeg", + ".JPEG", + ".jpg", + ".JPG", + ".m4v", + ".M4V", + ".mov", + ".MOV", + ".mp4", + ".MP4", + ".mta", + ".MTA", + ".png", + ".PNG", + ".tiff", + ".TIFF", + ".tif", + ".TIF" + ] + } + } +} \ No newline at end of file diff --git a/Rename/.vscode/UserSecrets/secrets.json b/Rename/.vscode/UserSecrets/secrets.json index 062dea1..a17d89d 100644 --- a/Rename/.vscode/UserSecrets/secrets.json +++ b/Rename/.vscode/UserSecrets/secrets.json @@ -3,7 +3,7 @@ "ComparePathsFile": "C:/Users/mikep/AppData/Local/PharesApps/Drag-Drop-Explorer/2023_21/638202586000194405.json", "Windows": { "Configuration": { - "RootDirectory": "D:/1) Images A/Images-dd514b88" + "RootDirectory": "D:/1-Images-A/Images-dd514b88" } } } \ No newline at end of file diff --git a/Rename/.vscode/tasks.json b/Rename/.vscode/tasks.json index 062f433..54b6db4 100644 --- a/Rename/.vscode/tasks.json +++ b/Rename/.vscode/tasks.json @@ -28,7 +28,7 @@ "${workspaceFolder}/Rename.csproj", "set", "SaveDirectory", - "D:/1) Images A/Images-dd514b88-Results/A2) People/dd514b88/([])" + "D:/1-Images-A/Images-dd514b88-Results/A2) People/dd514b88/([])" ], "problemMatcher": "$msCompile" }, diff --git a/Rename/Rename.cs b/Rename/Rename.cs index 455f274..d50aaed 100644 --- a/Rename/Rename.cs +++ b/Rename/Rename.cs @@ -151,9 +151,9 @@ public class Rename string? message; string checkFile; DateTime dateTime; + bool? isWrongYear; string seasonName; string? directory; - bool? isWrongYear; TimeSpan? timeSpan; string directoryName; DateTime?[] dateTimes; @@ -167,9 +167,11 @@ public class Rename DateTime? dateTimeFromName; DateTime? dateTimeOriginal; const string jpeg = ".jpeg"; + DateTime?[] metadataDateTimes; List distinct = new(); string[] directoryNameSegments; bool isValidImageFormatExtension; + DateTime? metadataDateTimeOriginal; bool nameWithoutExtensionIsIdFormat; DateTime? metadataMinimumDateTime = null; IReadOnlyList directories; @@ -219,6 +221,7 @@ public class Rename nameWithoutExtensionIsIdFormat = Shared.Models.Stateless.Methods.IProperty.NameWithoutExtensionIsIdFormat(fileHolder); if (nameWithoutExtensionIsIdFormat) continue; + dateTimeFromName = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder); isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered); isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered); if (!isIgnoreExtension && isValidImageFormatExtension) @@ -280,32 +283,37 @@ public class Rename continue; } } - dateTimeFromName = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder); (dateTimeOriginal, dateTimes, id, message) = Shared.Models.Stateless.Methods.IProperty.Get(fileHolder, isIgnoreExtension, isValidImageFormatExtension, _PropertyConfiguration.PopulatePropertyId); + if (ffmpegFiles is not null) + { + fileHolder = new(file); + foreach (string ffmpegFile in ffmpegFiles) + File.Delete(ffmpegFile); + } + if (fileHolder.DirectoryName is null) + continue; minimumDateTime = dateTimes.Min(); if (minimumDateTime is null) - break; - if (dateTimeOriginal is not null && dateTimeFromName is not null) - { - timeSpan = new(Math.Abs(dateTimeOriginal.Value.Ticks - dateTimeFromName.Value.Ticks)); - timeSpan = timeSpan.Value.TotalMinutes > _AppSettings.MaxMinutesDelta ? null : new(Math.Abs(minimumDateTime.Value.Ticks - new long[] { dateTimeOriginal.Value.Ticks, dateTimeFromName.Value.Ticks }.Min())); - } - else if (dateTimeFromName is not null) - timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeFromName.Value.Ticks)); - else if (dateTimeOriginal is not null) - timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks)); + throw new NotSupportedException(); + if (dateTimeOriginal is null) + timeSpan = null; else + timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks)); + if (timeSpan is null || timeSpan.Value.TotalMinutes > _AppSettings.MaxMinutesDelta) { if (_PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) continue; try - { directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); } + { directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); } catch (Exception) { continue; } - metadataMinimumDateTime = Metadata.Models.Stateless.Methods.IMetadata.GetMinimumDateTime(directories); - if (metadataMinimumDateTime is null) + (metadataDateTimeOriginal, metadataDateTimes) = Metadata.Models.Stateless.Methods.IMetadata.GetDateTimes(fileHolder, directories); + dateTimeOriginal ??= metadataDateTimeOriginal; + if (ffmpegFiles is not null && dateTimeOriginal is not null) + minimumDateTime = dateTimeOriginal.Value; + if (dateTimeOriginal is null) timeSpan = null; else - timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - metadataMinimumDateTime.Value.Ticks)); + timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks)); } if (timeSpan is null || timeSpan.Value.TotalMinutes > _AppSettings.MaxMinutesDelta) (isWrongYear, seasonDirectory) = (null, null); @@ -313,17 +321,26 @@ public class Rename { directoryName = Path.GetFileName(fileHolder.DirectoryName); directoryNameSegments = directoryName.Split(' '); - (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(directoryNameSegments, string.Empty); + if (dateTimeFromName is null) + isWrongYear = null; + else + (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(directoryNameSegments, dateTimeFromName.Value.ToString("yyyy")); dateTime = minimumDateTime.Value.AddTicks(timeSpan.Value.Ticks); (season, seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); seasonDirectory = Path.Combine(fileHolder.DirectoryName, $"{dateTime.Year}.{season} {seasonName}"); } if (seasonDirectory is null || (isWrongYear is not null && isWrongYear.Value)) { - if (metadataMinimumDateTime is null) + if (dateTimeFromName is not null && isWrongYear is not null && isWrongYear.Value) + minimumDateTime = dateTimeFromName.Value; + else if (dateTimeOriginal is not null) + minimumDateTime = dateTimeOriginal.Value; + else + minimumDateTime = new DateTime?[] { dateTimeOriginal, metadataMinimumDateTime }.Min(); + if (minimumDateTime is null) continue; checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; - checkFile = Path.Combine(fileHolder.DirectoryName, $"{metadataMinimumDateTime.Value:yyyy-MM-dd}.{metadataMinimumDateTime.Value.Ticks}.{fileHolder.Length}{checkFileExtension}"); + checkFile = Path.Combine(fileHolder.DirectoryName, $"{minimumDateTime.Value:yyyy-MM-dd}.{minimumDateTime.Value.Ticks}.{fileHolder.Length}{checkFileExtension}"); if (checkFile == fileHolder.FullName) continue; if (distinct.Contains(checkFile)) @@ -334,22 +351,11 @@ public class Rename results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), directory, $"{checkFile[..^4]}.tif")); if (nefPresent) results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), directory, $"{checkFile[..^4]}.nef")); - if (ffmpegFiles is not null) - { - foreach (string ffmpegFile in ffmpegFiles) - File.Delete(ffmpegFile); - } } else { if (id is null) continue; - if (ffmpegFiles is not null) - { - foreach (string ffmpegFile in ffmpegFiles) - File.Delete(ffmpegFile); - fileHolder = new(file); - } checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; checkFile = Path.Combine(seasonDirectory, $"{id.Value}{checkFileExtension}"); if (checkFile == fileHolder.FullName) diff --git a/Shared/Models/Stateless/Methods/IProperty.cs b/Shared/Models/Stateless/Methods/IProperty.cs index dbe696f..3c4488d 100644 --- a/Shared/Models/Stateless/Methods/IProperty.cs +++ b/Shared/Models/Stateless/Methods/IProperty.cs @@ -11,6 +11,11 @@ public interface IProperty static int GetDeterministicHashCode(byte[] value) => Property.GetDeterministicHashCode(value); + DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) => + GetDateTime(dateTimeFormat, value); + static DateTime? GetDateTime(string dateTimeFormat, string? value) => + Property.GetDateTime(dateTimeFormat, value); + DateTime TestStatic_GetDateTime(Models.Property? property) => GetDateTime(property); static DateTime GetDateTime(Models.Property? property) => diff --git a/Shared/Models/Stateless/Methods/Property.cs b/Shared/Models/Stateless/Methods/Property.cs index 80c4632..49b245a 100644 --- a/Shared/Models/Stateless/Methods/Property.cs +++ b/Shared/Models/Stateless/Methods/Property.cs @@ -434,45 +434,53 @@ internal abstract class Property value = value[..dateTimeFormat.Length]; if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) dateTime = checkDateTime; + else + dateTime = GetDateTime(dateTimeFormat, value); } - } - if (image.PropertyIdList.Contains((int)IExif.Tags.DateTimeDigitized)) - { - propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTimeDigitized); - if (propertyItem?.Value is not null) + if (image.PropertyIdList.Contains((int)IExif.Tags.DateTimeDigitized)) { - value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); - if (value.Length > dateTimeFormat.Length) - value = value[..dateTimeFormat.Length]; - if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) - dateTimeDigitized = checkDateTime; + propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTimeDigitized); + if (propertyItem?.Value is not null) + { + value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); + if (value.Length > dateTimeFormat.Length) + value = value[..dateTimeFormat.Length]; + if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) + dateTimeDigitized = checkDateTime; + else + dateTimeDigitized = GetDateTime(dateTimeFormat, value); + } } - } - if (image.PropertyIdList.Contains((int)IExif.Tags.DateTimeOriginal)) - { - propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTimeOriginal); - if (propertyItem?.Value is not null) + if (image.PropertyIdList.Contains((int)IExif.Tags.DateTimeOriginal)) { - value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); - if (value.Length > dateTimeFormat.Length) - value = value[..dateTimeFormat.Length]; - if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) - dateTimeOriginal = checkDateTime; + propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTimeOriginal); + if (propertyItem?.Value is not null) + { + value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); + if (value.Length > dateTimeFormat.Length) + value = value[..dateTimeFormat.Length]; + if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) + dateTimeOriginal = checkDateTime; + else + dateTimeOriginal = GetDateTime(dateTimeFormat, value); + } } - } - if (image.PropertyIdList.Contains((int)IExif.Tags.GPSDateStamp)) - { - propertyItem = image.GetPropertyItem((int)IExif.Tags.GPSDateStamp); - if (propertyItem?.Value is not null) + if (image.PropertyIdList.Contains((int)IExif.Tags.GPSDateStamp)) { - value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); - if (value.Length > dateTimeFormat.Length) - value = value[..dateTimeFormat.Length]; - if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) - gpsDateStamp = checkDateTime; + propertyItem = image.GetPropertyItem((int)IExif.Tags.GPSDateStamp); + if (propertyItem?.Value is not null) + { + value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); + if (value.Length > dateTimeFormat.Length) + value = value[..dateTimeFormat.Length]; + if (value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out checkDateTime)) + gpsDateStamp = checkDateTime; + else + gpsDateStamp = GetDateTime(dateTimeFormat, value); + } } + image.Dispose(); } - image.Dispose(); } catch (Exception) { @@ -485,4 +493,19 @@ internal abstract class Property #pragma warning restore CA1416 + internal static DateTime? GetDateTime(string dateTimeFormat, string? value) + { + DateTime? result; + string alternateFormat = "ddd MMM dd HH:mm:ss yyyy"; + if (value is not null && DateTime.TryParse(value, out DateTime dateTime)) + result = dateTime; + else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + result = dateTime; + else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + result = dateTime; + else + result = null; + return result; + } + } \ No newline at end of file diff --git a/View-by-Distance-MKLink-Console.sln b/View-by-Distance-MKLink-Console.sln index 671d203..75ea85e 100644 --- a/View-by-Distance-MKLink-Console.sln +++ b/View-by-Distance-MKLink-Console.sln @@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mirror-Length", "Mirror-Len EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Move-By-Id", "Move-By-Id\Move-By-Id.csproj", "{0FDFBC71-3801-483F-A4AC-CC8CF857D54F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Offset-Date-Time-Original", "Offset-Date-Time-Original\Offset-Date-Time-Original.csproj", "{6B68758E-1AF1-401F-844A-C1199A0B6303}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Person", "Person\Person.csproj", "{C5003A39-334B-444B-9873-39B26E58D667}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhotoPrism", "PhotoPrism\PhotoPrism.csproj", "{DF4B0776-E0E5-4220-8721-8D1E491FF263}" @@ -200,5 +202,9 @@ Global {035A3DB4-1D0B-4872-ABAE-1D637BB89B1C}.Debug|Any CPU.Build.0 = Debug|Any CPU {035A3DB4-1D0B-4872-ABAE-1D637BB89B1C}.Release|Any CPU.ActiveCfg = Release|Any CPU {035A3DB4-1D0B-4872-ABAE-1D637BB89B1C}.Release|Any CPU.Build.0 = Release|Any CPU + {6B68758E-1AF1-401F-844A-C1199A0B6303}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B68758E-1AF1-401F-844A-C1199A0B6303}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B68758E-1AF1-401F-844A-C1199A0B6303}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B68758E-1AF1-401F-844A-C1199A0B6303}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal