From 130e3b652855dd25b8030bac4d43a839c4d9574c Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sat, 22 Mar 2025 16:50:47 -0700 Subject: [PATCH] Mostly Sorting Video Merge as 4, 5, and 6 --- .vscode/tasks.json | 14 + Compare/Compare.cs | 684 +++--- .../Models/Stateless/Methods/Container.cs | 771 +++--- .../Models/Stateless/Methods/IContainer.cs | 70 +- Face/Models/_D_Face.cs | 568 +++-- FaceParts/Models/_D2_FaceParts.cs | 528 ++--- Instance/DlibDotNet.cs | 2058 ++++++++--------- Map/Models/MapLogic.cs | 1904 +++++++-------- Map/Models/Stateless/DistanceLogic.cs | 642 ++--- Map/Models/Stateless/MapLogic.cs | 1812 ++++++++------- Map/Models/Stateless/Methods/IMapLogic.cs | 130 +- Map/Models/Stateless/RelationLogic.cs | 302 +-- Property/Models/A_Property.cs | 232 +- Property/Models/Stateless/IProperty.cs | 123 +- Property/Models/Stateless/IResult.cs | 64 +- Property/Models/Stateless/Property.cs | 336 ++- Property/Models/Stateless/Result.cs | 119 +- Resize/Models/_C_Resize.cs | 738 +++--- Shared/Models/Stateless/Methods/IDirectory.cs | 103 +- Shared/Models/Stateless/Methods/IId.cs | 112 +- Shared/Models/Stateless/Methods/IPath.cs | 152 +- Shared/Models/Stateless/Methods/Id.cs | 90 +- Shared/Models/Stateless/Methods/XDirectory.cs | 382 ++- Shared/Models/Stateless/Methods/XPath.cs | 408 ++-- 24 files changed, 6223 insertions(+), 6119 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 995107a..cc276e3 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -99,6 +99,20 @@ "/consoleloggerparameters:NoSummary" ], "problemMatcher": "$msCompile" + }, + { + "label": "File-Folder-Helper AOT s X Day-Helper-2025-03-20", + "type": "shell", + "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe", + "args": [ + "s", + "X", + "L:/Git/View-by-Distance-MKLink-Console", + "Day-Helper-2025-03-20", + "false", + "4" + ], + "problemMatcher": [] } ] } \ No newline at end of file diff --git a/Compare/Compare.cs b/Compare/Compare.cs index 18e0243..f9607e9 100644 --- a/Compare/Compare.cs +++ b/Compare/Compare.cs @@ -242,6 +242,53 @@ public class Compare _ = Directory.CreateDirectory(currentYearDirectory); } + private static void Verify(Models.Configuration configuration) + { + if (configuration.Spelling is null || !configuration.Spelling.Any()) + throw new NullReferenceException(nameof(configuration.Spelling)); + } + + private A_Property GetPropertyLogic(bool reverse, string outputExtension, Map.Models.MapLogic mapLogic) + { + A_Property result; + if (_Configuration?.PropertyConfiguration is null) + throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration)); + result = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, outputExtension, reverse); + string fromPrepareForOld = "34720-637858334555170379.tsv"; + string fromPrepareForOldFile = Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, fromPrepareForOld); + if (File.Exists(fromPrepareForOldFile)) + { + string[] lines; + string[] columns; + List debug = new(); + long ticks = DateTime.Now.Ticks; + lines = File.ReadAllLines(fromPrepareForOldFile); + string resultsDirectory = $"{_Configuration.PropertyConfiguration.RootDirectory}-Results"; + int[]? zeros = (from l in mapLogic.IndicesFromNew where l.Value.Any() select l.Value[0]).ToArray(); + lines = (from l in mapLogic.IndicesFromNew select string.Concat(l.Key, '\t', string.Join('\t', l.Value))).ToArray(); + if (!Directory.Exists(resultsDirectory)) + _ = Directory.CreateDirectory(resultsDirectory); + File.WriteAllLines(Path.Combine(resultsDirectory, $"{ticks}.tsv"), lines); + string json = JsonSerializer.Serialize(mapLogic.IndicesFromNew, new JsonSerializerOptions { WriteIndented = true }); + File.WriteAllText(Path.Combine(resultsDirectory, $"{ticks}.json"), json); + foreach (string line in lines) + { + columns = line.Split('\t'); + // select $"{l.Index}\t{l.PropertyId}\t{l.RegexResult}\t{new DateTime(l.Ticks):yyyy-MM-dd_HH-mm-ss}\t{l.PropertyTicks}\t{l.RelativeDirectory}\t{l.FileName}" + if (columns.Length != 7) + continue; + if (!int.TryParse(columns[1], out int propertyId)) + continue; + if (!zeros.Contains(propertyId)) + debug.Add(line); + else + debug.Add(propertyId.ToString()); + } + File.WriteAllLines(Path.Combine(resultsDirectory, $"{ticks}-{fromPrepareForOld}"), debug); + } + return result; + } + private string GetRename(string renameA) { string result; @@ -376,12 +423,6 @@ public class Compare return result; } - private static void Verify(Models.Configuration configuration) - { - if (configuration.Spelling is null || !configuration.Spelling.Any()) - throw new NullReferenceException(nameof(configuration.Spelling)); - } - private long LogDelta(long ticks, string? methodName) { long result; @@ -391,121 +432,6 @@ public class Compare return result; } - private A_Property GetPropertyLogic(bool reverse, string outputExtension, Map.Models.MapLogic mapLogic) - { - A_Property result; - if (_Configuration?.PropertyConfiguration is null) - throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration)); - result = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, outputExtension, reverse); - string fromPrepareForOld = "34720-637858334555170379.tsv"; - string fromPrepareForOldFile = Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, fromPrepareForOld); - if (File.Exists(fromPrepareForOldFile)) - { - string[] lines; - string[] columns; - List debug = new(); - long ticks = DateTime.Now.Ticks; - lines = File.ReadAllLines(fromPrepareForOldFile); - string resultsDirectory = $"{_Configuration.PropertyConfiguration.RootDirectory}-Results"; - int[]? zeros = (from l in mapLogic.IndicesFromNew where l.Value.Any() select l.Value[0]).ToArray(); - lines = (from l in mapLogic.IndicesFromNew select string.Concat(l.Key, '\t', string.Join('\t', l.Value))).ToArray(); - if (!Directory.Exists(resultsDirectory)) - _ = Directory.CreateDirectory(resultsDirectory); - File.WriteAllLines(Path.Combine(resultsDirectory, $"{ticks}.tsv"), lines); - string json = JsonSerializer.Serialize(mapLogic.IndicesFromNew, new JsonSerializerOptions { WriteIndented = true }); - File.WriteAllText(Path.Combine(resultsDirectory, $"{ticks}.json"), json); - foreach (string line in lines) - { - columns = line.Split('\t'); - // select $"{l.Index}\t{l.PropertyId}\t{l.RegexResult}\t{new DateTime(l.Ticks):yyyy-MM-dd_HH-mm-ss}\t{l.PropertyTicks}\t{l.RelativeDirectory}\t{l.FileName}" - if (columns.Length != 7) - continue; - if (!int.TryParse(columns[1], out int propertyId)) - continue; - if (!zeros.Contains(propertyId)) - debug.Add(line); - else - debug.Add(propertyId.ToString()); - } - File.WriteAllLines(Path.Combine(resultsDirectory, $"{ticks}-{fromPrepareForOld}"), debug); - } - return result; - } - - private void SaveDiffFilesOrSaveLogAndMoveFiles(Property.Models.Configuration configuration) - { - if (_Configuration?.PropertyConfiguration is null) - throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration)); - string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}"); - _Logger?.LogInformation(aPropertySingletonDirectory); - _Logger?.LogInformation("to"); - _Logger?.LogInformation(_Configuration.DiffPropertyDirectory); - for (int y = 0; y < int.MaxValue; y++) - { - _Logger?.LogInformation("Press \"Y\" key to continue or close console if compare not needed"); - if (Console.ReadKey().Key == ConsoleKey.Y) - break; - } - _Logger?.LogInformation(". . ."); - int loadLessThan = 7; - string diffRootDirectory; - ConsoleKey? consoleKey = null; - List? duplicates = null; - PropertyCompare.Models.PropertyCompare[] diffPropertyCompareCollection; - if (string.IsNullOrEmpty(_Configuration.DiffPropertyDirectory)) - diffRootDirectory = string.Empty; - else - { - if (!_Configuration.DiffPropertyDirectory.EndsWith("{}")) - throw new Exception("Invalid directory should end with {}!"); - diffRootDirectory = Shared.Models.Stateless.Methods.IProperty.GetDiffRootDirectory(_Configuration.DiffPropertyDirectory); - } - PropertyCompare.Models.PropertyCompareLogic propertyCompareLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _SpellingFindReplace, diffRootDirectory); - if (string.IsNullOrEmpty(_Configuration.DiffPropertyDirectory) || !Directory.Exists(_Configuration.DiffPropertyDirectory)) - diffPropertyCompareCollection = Array.Empty(); - else - { - diffPropertyCompareCollection = propertyCompareLogic.Get(_Configuration.DiffPropertyDirectory, loadLessThan, duplicates, deleteExtension: false); - if (!diffPropertyCompareCollection.Any()) - throw new Exception("Invalid directory!"); - } - string aPropertyCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "[{}]"); - PropertyCompare.Models.PropertyCompare[] propertyCompareCollection = propertyCompareLogic.Get(aPropertySingletonDirectory, loadLessThan, duplicates, deleteExtension: false); - { - long ticks = DateTime.Now.Ticks; - string[] lines = (from l in propertyCompareCollection select l.GetSelect()).ToArray(); - File.WriteAllLines(Path.Join(aPropertyCollectionDirectory, $". . . Ids - {ticks}.txt"), lines); - string json = JsonSerializer.Serialize(propertyCompareCollection, new JsonSerializerOptions { WriteIndented = true }); - File.WriteAllText(Path.Join(aPropertyCollectionDirectory, $". . . Ids - {ticks}.nosj"), json); - } - for (int x = 0; x < int.MaxValue; x++) - { - _Logger?.LogInformation($"Press \"D\" key to {nameof(PropertyCompare.Models.PropertyCompareLogic.SaveDiffFiles)}"); - _Logger?.LogInformation($"Press \"M\" key to {nameof(PropertyCompare.Models.PropertyCompareLogic.SaveLogAndMoveFiles)}"); - _Logger?.LogInformation("Press \"End\" key when ready to skip"); - consoleKey = Console.ReadKey().Key; - if (consoleKey is ConsoleKey.D or ConsoleKey.M or ConsoleKey.End) - break; - } - _Logger?.LogInformation(". . ."); - if (consoleKey.HasValue && consoleKey.Value == ConsoleKey.D) - propertyCompareLogic.SaveDiffFiles(aPropertyCollectionDirectory, loadLessThan, propertyCompareCollection, diffPropertyCompareCollection); - else if (consoleKey.HasValue && consoleKey.Value == ConsoleKey.M) - { - for (int x = 0; x < int.MaxValue; x++) - { - _Logger?.LogInformation($"Press \"0 - {loadLessThan}\" key when ready to continue"); - _Logger?.LogInformation("Press \"End\" key when ready to skip"); - consoleKey = Console.ReadKey().Key; - if (consoleKey.Value is ConsoleKey.D0 or ConsoleKey.D1 or ConsoleKey.D2 or ConsoleKey.D3 or ConsoleKey.D4 or ConsoleKey.D5 or ConsoleKey.D6 or ConsoleKey.End) - break; - } - _Logger?.LogInformation(". . ."); - int i = int.Parse(consoleKey.Value.ToString()[1..]); - propertyCompareLogic.SaveLogAndMoveFiles(aPropertyCollectionDirectory, loadLessThan, propertyCompareCollection, diffPropertyCompareCollection, i); - } - } - private void ChangeExtensionFromDeleteToJson(string aPropertySingletonDirectory) { string searchPattern = "*.delete"; @@ -524,227 +450,6 @@ public class Compare } } - private bool PossiblyRename(List topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) - { - bool result = false; - string replaceFile; - string replaceDirectory; - int remainingDirectories = 0; - IEnumerable<(string Find, string Replace)>? found; - foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection) - { - if (!topDirectories.Any()) - continue; - found = from l in _RenameFindReplace where sourceDirectory == l.Find select l; - if (!found.Any()) - continue; - if (!result) - result = true; - replaceDirectory = found.First().Replace; - if (!Directory.Exists(replaceDirectory)) - Directory.Move(sourceDirectory, replaceDirectory); - else - { - if (Directory.EnumerateDirectories(sourceDirectory).Any()) - remainingDirectories += 1; - else - { - foreach (string sourceDirectoryFile in sourceDirectoryFiles) - { - replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile)); - if (File.Exists(replaceFile)) - { - if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture)) - replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg")); - else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture)) - replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg")); - } - if (File.Exists(replaceFile)) - continue; - File.Move(sourceDirectoryFile, replaceFile); - } - } - } - } - return result; - } - - private bool PossiblyRenameB(List topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) - { - bool result = false; - string replaceFile; - string replaceDirectory; - IEnumerable<(string Find, string Replace)>? found; - foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection) - { - if (!topDirectories.Any()) - continue; - found = from l in _RenameBFindReplace where sourceDirectory == l.Find select l; - if (!found.Any()) - continue; - if (!result) - result = true; - replaceDirectory = found.First().Replace; - if (!Directory.Exists(replaceDirectory)) - _ = Directory.CreateDirectory(replaceDirectory); - foreach (string sourceDirectoryFile in sourceDirectoryFiles) - { - replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile)); - if (File.Exists(replaceFile)) - { - if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture)) - replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg")); - else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture)) - replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg")); - } - if (File.Exists(replaceFile)) - continue; - File.Move(sourceDirectoryFile, replaceFile); - } - } - return result; - } - - private bool PossiblyRenameC(List topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) - { - bool result = false; - string replaceFile; - string replaceDirectory; - IEnumerable<(string Find, string Replace)>? found; - foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection) - { - if (!topDirectories.Any()) - continue; - found = from l in _RenameCFindReplace where sourceDirectory == l.Find select l; - if (!found.Any()) - continue; - if (!result) - result = true; - replaceDirectory = found.First().Replace; - if (!Directory.Exists(replaceDirectory)) - _ = Directory.CreateDirectory(replaceDirectory); - foreach (string sourceDirectoryFile in sourceDirectoryFiles) - { - replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile)); - if (File.Exists(replaceFile)) - { - if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture)) - replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg")); - else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture)) - replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg")); - } - if (File.Exists(replaceFile)) - continue; - File.Move(sourceDirectoryFile, replaceFile); - } - } - return result; - } - - private bool PossiblyCorrect(List topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) - { - if (_Configuration?.PropertyConfiguration is null) - throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration)); - bool result = false; - string corrected; - string correctedMoveTo; - string? correctedDirectory; - string filteredSourceDirectoryFile; - string[] filteredSourceDirectoryFiles; - (string Find, string Replace) findReplace; - IEnumerable<(string Find, string Replace)>? found; - foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection) - { - if (!topDirectories.Any()) - continue; - filteredSourceDirectoryFiles = (from l in sourceDirectoryFiles where !_Configuration.PropertyConfiguration.IgnoreExtensions.Contains(Path.GetExtension(l)) select l).ToArray(); - if (!filteredSourceDirectoryFiles.Any()) - continue; - for (int i = 0; i < filteredSourceDirectoryFiles.Length; i++) - { - found = null; - for (int z = 0; z < int.MaxValue; z++) - { - filteredSourceDirectoryFile = filteredSourceDirectoryFiles[i]; - found = from l in _SpellingFindReplace where filteredSourceDirectoryFile.Contains(l.Find) select l; - if (!found.Any()) - break; - findReplace = found.First(); - corrected = filteredSourceDirectoryFile.Replace(findReplace.Find, findReplace.Replace); - correctedDirectory = Path.GetDirectoryName(corrected); - if (string.IsNullOrEmpty(correctedDirectory)) - break; - correctedMoveTo = Path.Combine(correctedDirectory, Path.GetFileName(corrected)); - if (File.Exists(correctedMoveTo)) - break; - if (!Directory.Exists(correctedDirectory)) - _ = Directory.CreateDirectory(correctedDirectory); - if (!result) - result = true; - File.Move(filteredSourceDirectoryFile, correctedMoveTo); - filteredSourceDirectoryFiles[i] = corrected; - } - } - } - return result; - } - - private List GetMissingVerifyToSeasonCollection(List _, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) - { - if (_Configuration?.PropertyConfiguration is null) - throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration)); - List results = new(); - string check; - foreach ((int _, string sourceDirectory, string[] _) in groupCollection) - { - if (sourceDirectory == _Configuration.PropertyConfiguration.RootDirectory) - continue; - check = sourceDirectory[(_Configuration.PropertyConfiguration.RootDirectory.Length + 1)..]; - if (check[0] is '=' || check.StartsWith("zzz =")) - { - if (!_Configuration.PropertyConfiguration.VerifyToSeason.Contains(check)) - results.Add(check); - } - } - return results; - } - - private void CreateWindowsShortcuts((long Ticks, string FilteredSourceDirectoryFile, string PropertyDirectory, int PropertyId)[] collection, bool keepAll) - { - int z = 0; - string fileName; - WindowsShortcut windowsShortcut; - foreach ((long ticks, string filteredSourceDirectoryFile, string propertyDirectory, int propertyId) in collection) - { - z += 1; - if (z % 1000 == 0) - _Log.Debug($"{z}) Loop {propertyDirectory}"); - if (!keepAll) - { - fileName = Path.Combine(propertyDirectory, $"{propertyId}.lnk"); - if (File.Exists(fileName)) - continue; - } - else - { - fileName = string.Empty; - for (short c = 65; c < short.MaxValue; c++) - { - if (c > 95) - break; - fileName = Path.Combine(propertyDirectory, $"{(char)c}", $"{propertyId}.lnk"); - if (File.Exists(fileName)) - continue; - } - } - if (string.IsNullOrEmpty(fileName)) - continue; - windowsShortcut = new() { Path = filteredSourceDirectoryFile }; - windowsShortcut.Save(fileName); - windowsShortcut.Dispose(); - } - } - private void ThirdPassToMove(Property.Models.Configuration configuration, Map.Models.MapLogic mapLogic, A_Property propertyLogic, Shared.Models.Container[] containers, string aPropertyContentCollectionDirectory) { if (_Configuration?.PropertyConfiguration is null) @@ -865,4 +570,299 @@ public class Compare CreateWindowsShortcuts(collection, keepAll); } + private void CreateWindowsShortcuts((long Ticks, string FilteredSourceDirectoryFile, string PropertyDirectory, int PropertyId)[] collection, bool keepAll) + { + int z = 0; + string fileName; + WindowsShortcut windowsShortcut; + foreach ((long ticks, string filteredSourceDirectoryFile, string propertyDirectory, int propertyId) in collection) + { + z += 1; + if (z % 1000 == 0) + _Log.Debug($"{z}) Loop {propertyDirectory}"); + if (!keepAll) + { + fileName = Path.Combine(propertyDirectory, $"{propertyId}.lnk"); + if (File.Exists(fileName)) + continue; + } + else + { + fileName = string.Empty; + for (short c = 65; c < short.MaxValue; c++) + { + if (c > 95) + break; + fileName = Path.Combine(propertyDirectory, $"{(char)c}", $"{propertyId}.lnk"); + if (File.Exists(fileName)) + continue; + } + } + if (string.IsNullOrEmpty(fileName)) + continue; + windowsShortcut = new() { Path = filteredSourceDirectoryFile }; + windowsShortcut.Save(fileName); + windowsShortcut.Dispose(); + } + } + + private void SaveDiffFilesOrSaveLogAndMoveFiles(Property.Models.Configuration configuration) + { + if (_Configuration?.PropertyConfiguration is null) + throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration)); + string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}"); + _Logger?.LogInformation(aPropertySingletonDirectory); + _Logger?.LogInformation("to"); + _Logger?.LogInformation(_Configuration.DiffPropertyDirectory); + for (int y = 0; y < int.MaxValue; y++) + { + _Logger?.LogInformation("Press \"Y\" key to continue or close console if compare not needed"); + if (Console.ReadKey().Key == ConsoleKey.Y) + break; + } + _Logger?.LogInformation(". . ."); + int loadLessThan = 7; + string diffRootDirectory; + ConsoleKey? consoleKey = null; + List? duplicates = null; + PropertyCompare.Models.PropertyCompare[] diffPropertyCompareCollection; + if (string.IsNullOrEmpty(_Configuration.DiffPropertyDirectory)) + diffRootDirectory = string.Empty; + else + { + if (!_Configuration.DiffPropertyDirectory.EndsWith("{}")) + throw new Exception("Invalid directory should end with {}!"); + diffRootDirectory = Shared.Models.Stateless.Methods.IProperty.GetDiffRootDirectory(_Configuration.DiffPropertyDirectory); + } + PropertyCompare.Models.PropertyCompareLogic propertyCompareLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _SpellingFindReplace, diffRootDirectory); + if (string.IsNullOrEmpty(_Configuration.DiffPropertyDirectory) || !Directory.Exists(_Configuration.DiffPropertyDirectory)) + diffPropertyCompareCollection = Array.Empty(); + else + { + diffPropertyCompareCollection = propertyCompareLogic.Get(_Configuration.DiffPropertyDirectory, loadLessThan, duplicates, deleteExtension: false); + if (!diffPropertyCompareCollection.Any()) + throw new Exception("Invalid directory!"); + } + string aPropertyCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "[{}]"); + PropertyCompare.Models.PropertyCompare[] propertyCompareCollection = propertyCompareLogic.Get(aPropertySingletonDirectory, loadLessThan, duplicates, deleteExtension: false); + { + long ticks = DateTime.Now.Ticks; + string[] lines = (from l in propertyCompareCollection select l.GetSelect()).ToArray(); + File.WriteAllLines(Path.Join(aPropertyCollectionDirectory, $". . . Ids - {ticks}.txt"), lines); + string json = JsonSerializer.Serialize(propertyCompareCollection, new JsonSerializerOptions { WriteIndented = true }); + File.WriteAllText(Path.Join(aPropertyCollectionDirectory, $". . . Ids - {ticks}.nosj"), json); + } + for (int x = 0; x < int.MaxValue; x++) + { + _Logger?.LogInformation($"Press \"D\" key to {nameof(PropertyCompare.Models.PropertyCompareLogic.SaveDiffFiles)}"); + _Logger?.LogInformation($"Press \"M\" key to {nameof(PropertyCompare.Models.PropertyCompareLogic.SaveLogAndMoveFiles)}"); + _Logger?.LogInformation("Press \"End\" key when ready to skip"); + consoleKey = Console.ReadKey().Key; + if (consoleKey is ConsoleKey.D or ConsoleKey.M or ConsoleKey.End) + break; + } + _Logger?.LogInformation(". . ."); + if (consoleKey.HasValue && consoleKey.Value == ConsoleKey.D) + propertyCompareLogic.SaveDiffFiles(aPropertyCollectionDirectory, loadLessThan, propertyCompareCollection, diffPropertyCompareCollection); + else if (consoleKey.HasValue && consoleKey.Value == ConsoleKey.M) + { + for (int x = 0; x < int.MaxValue; x++) + { + _Logger?.LogInformation($"Press \"0 - {loadLessThan}\" key when ready to continue"); + _Logger?.LogInformation("Press \"End\" key when ready to skip"); + consoleKey = Console.ReadKey().Key; + if (consoleKey.Value is ConsoleKey.D0 or ConsoleKey.D1 or ConsoleKey.D2 or ConsoleKey.D3 or ConsoleKey.D4 or ConsoleKey.D5 or ConsoleKey.D6 or ConsoleKey.End) + break; + } + _Logger?.LogInformation(". . ."); + int i = int.Parse(consoleKey.Value.ToString()[1..]); + propertyCompareLogic.SaveLogAndMoveFiles(aPropertyCollectionDirectory, loadLessThan, propertyCompareCollection, diffPropertyCompareCollection, i); + } + } + + private bool PossiblyRename(List topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) + { + bool result = false; + string replaceFile; + string replaceDirectory; + int remainingDirectories = 0; + IEnumerable<(string Find, string Replace)>? found; + foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection) + { + if (!topDirectories.Any()) + continue; + found = from l in _RenameFindReplace where sourceDirectory == l.Find select l; + if (!found.Any()) + continue; + if (!result) + result = true; + replaceDirectory = found.First().Replace; + if (!Directory.Exists(replaceDirectory)) + Directory.Move(sourceDirectory, replaceDirectory); + else + { + if (Directory.EnumerateDirectories(sourceDirectory).Any()) + remainingDirectories += 1; + else + { + foreach (string sourceDirectoryFile in sourceDirectoryFiles) + { + replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile)); + if (File.Exists(replaceFile)) + { + if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture)) + replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg")); + else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture)) + replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg")); + } + if (File.Exists(replaceFile)) + continue; + File.Move(sourceDirectoryFile, replaceFile); + } + } + } + } + return result; + } + + private bool PossiblyCorrect(List topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) + { + if (_Configuration?.PropertyConfiguration is null) + throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration)); + bool result = false; + string corrected; + string correctedMoveTo; + string? correctedDirectory; + string filteredSourceDirectoryFile; + string[] filteredSourceDirectoryFiles; + (string Find, string Replace) findReplace; + IEnumerable<(string Find, string Replace)>? found; + foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection) + { + if (!topDirectories.Any()) + continue; + filteredSourceDirectoryFiles = (from l in sourceDirectoryFiles where !_Configuration.PropertyConfiguration.IgnoreExtensions.Contains(Path.GetExtension(l)) select l).ToArray(); + if (!filteredSourceDirectoryFiles.Any()) + continue; + for (int i = 0; i < filteredSourceDirectoryFiles.Length; i++) + { + found = null; + for (int z = 0; z < int.MaxValue; z++) + { + filteredSourceDirectoryFile = filteredSourceDirectoryFiles[i]; + found = from l in _SpellingFindReplace where filteredSourceDirectoryFile.Contains(l.Find) select l; + if (!found.Any()) + break; + findReplace = found.First(); + corrected = filteredSourceDirectoryFile.Replace(findReplace.Find, findReplace.Replace); + correctedDirectory = Path.GetDirectoryName(corrected); + if (string.IsNullOrEmpty(correctedDirectory)) + break; + correctedMoveTo = Path.Combine(correctedDirectory, Path.GetFileName(corrected)); + if (File.Exists(correctedMoveTo)) + break; + if (!Directory.Exists(correctedDirectory)) + _ = Directory.CreateDirectory(correctedDirectory); + if (!result) + result = true; + File.Move(filteredSourceDirectoryFile, correctedMoveTo); + filteredSourceDirectoryFiles[i] = corrected; + } + } + } + return result; + } + + private bool PossiblyRenameB(List topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) + { + bool result = false; + string replaceFile; + string replaceDirectory; + IEnumerable<(string Find, string Replace)>? found; + foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection) + { + if (!topDirectories.Any()) + continue; + found = from l in _RenameBFindReplace where sourceDirectory == l.Find select l; + if (!found.Any()) + continue; + if (!result) + result = true; + replaceDirectory = found.First().Replace; + if (!Directory.Exists(replaceDirectory)) + _ = Directory.CreateDirectory(replaceDirectory); + foreach (string sourceDirectoryFile in sourceDirectoryFiles) + { + replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile)); + if (File.Exists(replaceFile)) + { + if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture)) + replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg")); + else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture)) + replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg")); + } + if (File.Exists(replaceFile)) + continue; + File.Move(sourceDirectoryFile, replaceFile); + } + } + return result; + } + + private bool PossiblyRenameC(List topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) + { + bool result = false; + string replaceFile; + string replaceDirectory; + IEnumerable<(string Find, string Replace)>? found; + foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection) + { + if (!topDirectories.Any()) + continue; + found = from l in _RenameCFindReplace where sourceDirectory == l.Find select l; + if (!found.Any()) + continue; + if (!result) + result = true; + replaceDirectory = found.First().Replace; + if (!Directory.Exists(replaceDirectory)) + _ = Directory.CreateDirectory(replaceDirectory); + foreach (string sourceDirectoryFile in sourceDirectoryFiles) + { + replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile)); + if (File.Exists(replaceFile)) + { + if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture)) + replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg")); + else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture)) + replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg")); + } + if (File.Exists(replaceFile)) + continue; + File.Move(sourceDirectoryFile, replaceFile); + } + } + return result; + } + + private List GetMissingVerifyToSeasonCollection(List _, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection) + { + if (_Configuration?.PropertyConfiguration is null) + throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration)); + List results = new(); + string check; + foreach ((int _, string sourceDirectory, string[] _) in groupCollection) + { + if (sourceDirectory == _Configuration.PropertyConfiguration.RootDirectory) + continue; + check = sourceDirectory[(_Configuration.PropertyConfiguration.RootDirectory.Length + 1)..]; + if (check[0] is '=' || check.StartsWith("zzz =")) + { + if (!_Configuration.PropertyConfiguration.VerifyToSeason.Contains(check)) + results.Add(check); + } + } + return results; + } + } \ No newline at end of file diff --git a/Container/Models/Stateless/Methods/Container.cs b/Container/Models/Stateless/Methods/Container.cs index e274538..8fae951 100644 --- a/Container/Models/Stateless/Methods/Container.cs +++ b/Container/Models/Stateless/Methods/Container.cs @@ -9,16 +9,10 @@ namespace View_by_Distance.Container.Models.Stateless.Methods; internal abstract class Container { - private record FilePair(bool IsUnique, List Collection, FilePath FilePath, Item Item) { } - - internal static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) - { - DateTime[] results; - long containerMinimumTicks = (from l in items select l.FilePath.LastWriteTicks).Min(); - long containerMaximumTicks = (from l in items select l.FilePath.LastWriteTicks).Max(); - results = [new(containerMinimumTicks), new(containerMaximumTicks)]; - return results; - } + private record FilePair(bool IsUnique, + List Collection, + FilePath FilePath, + Item Item); internal static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) { @@ -32,28 +26,140 @@ internal abstract class Container return container.Items.Count == results.Count ? container.Items : results.AsReadOnly(); } - private static IReadOnlyDictionary> GetFilesKeyValuePairs(ReadOnlyCollection> filePathsCollection) + internal static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) { - Dictionary> results = []; - List? collection; - string fileNameWithoutExtensionMinusOne; - foreach (ReadOnlyCollection filePaths in filePathsCollection) + DateTime[] results; + long containerMinimumTicks = (from l in items select l.FilePath.LastWriteTicks).Min(); + long containerMaximumTicks = (from l in items select l.FilePath.LastWriteTicks).Max(); + results = [new(containerMinimumTicks), new(containerMaximumTicks)]; + return results; + } + + internal static List GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) + { + List results = []; + ReadOnlyCollection filteredItems; + foreach (Models.Container container in readOnlyContainers) { - foreach (FilePath filePath in filePaths) + if (container.Items.Count == 0) + continue; + filteredItems = GetValidImageItems(propertyConfiguration, container); + if (filteredItems.Count == 0) + continue; + foreach (Item item in filteredItems) { - fileNameWithoutExtensionMinusOne = filePath.NameWithoutExtension[..^1]; - if (!results.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) - { - results.Add(fileNameWithoutExtensionMinusOne, []); - if (!results.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) - throw new Exception(); - } - collection.Add(filePath.FullName); + if (item.Property?.Id is null || item.ResizedFileHolder is null) + continue; + if (results.Contains(item.Property.Id.Value)) + continue; + results.Add(item.Property.Id.Value); } } return results; } + internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string _) + { + int count; + Models.Container[] results; + const bool useCeilingAverage = true; + const string fileSearchFilter = "*"; + IDlibDotNet dlibDotNet = GetDlibDotNet(); + const string directorySearchFilter = "*"; + ReadOnlyDictionary>? keyValuePairs = null; + ReadOnlyCollection filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); + ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection); + (count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); + return (count, results); + } + + private static IDlibDotNet GetDlibDotNet() => + throw new NotImplementedException(); + + private static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + { + List results = []; + string directory; + List? items; + Models.Container container; + List directories = []; + Dictionary> directoryToItems = []; + foreach (ReadOnlyCollection filePaths in filePathsCollection) + { + if (filePaths.Count == 0) + continue; + directory = filePaths[0].DirectoryFullPath; + if (directory is null) + continue; + if (!directories.Contains(directory)) + directories.Add(directory); + if (!directoryToItems.TryGetValue(directory, out items)) + { + directoryToItems.Add(directory, []); + if (!directoryToItems.TryGetValue(directory, out items)) + throw new Exception(); + } + } + (string aResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories(); + string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(aPropertySingletonDirectory)) + _ = Directory.CreateDirectory(aPropertySingletonDirectory); + List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); + foreach (FilePair filePair in filePairs) + { + if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) + { + directoryToItems.Add(filePair.FilePath.DirectoryFullPath, []); + if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) + throw new Exception(); + } + items.Add(filePair.Item); + } + foreach (KeyValuePair> keyValuePair in directoryToItems) + { + if (keyValuePair.Value.Count == 0) + continue; + container = new(keyValuePair.Key, new(keyValuePair.Value)); + results.Add(container); + } + return (filePairs.Count, results.ToArray()); + } + + internal static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) + { + Models.Container[] results; + const string directorySearchFilter = "*"; + (_, results) = GetContainers(dlibDotNet, propertyConfiguration, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); + if (keyValuePairs is not null) + DoGetFilePairsForRemaining(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filePathsCollection, directorySearchFilter); + return results.AsReadOnly(); + } + + private static void DoGetFilePairsForRemaining(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + { + const string extension = ".json"; + (_, string bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); + IReadOnlyDictionary> fileNamesToFiles = GetFilesKeyValuePairs(filePathsCollection); + string bMetaSingletonDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(bMetaSingletonDirectory)) + _ = Directory.CreateDirectory(bMetaSingletonDirectory); + _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, bMetaSingletonDirectory, filePathsCollection, fileNamesToFiles); + (string cResultsFullGroupDirectory, _, string dResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories("Original"); + string cResizeSingletonDirectory = Path.Combine(cResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(cResizeSingletonDirectory)) + _ = Directory.CreateDirectory(cResizeSingletonDirectory); + _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, cResizeSingletonDirectory, filePathsCollection, fileNamesToFiles); + string dFaceCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultCollection); + if (!Directory.Exists(dFaceCollectionDirectory)) + _ = Directory.CreateDirectory(dFaceCollectionDirectory); + _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, dFaceCollectionDirectory, filePathsCollection, fileNamesToFiles); + string dFaceContentDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultContent); + if (!Directory.Exists(dFaceContentDirectory)) + _ = Directory.CreateDirectory(dFaceContentDirectory); + AnyMovedFace(facesFileNameExtension, facesHiddenFileNameExtension, fileNamesToFiles, dFaceContentDirectory); + AnyMovedDistance(facesFileNameExtension, fileNamesToFiles, eDistanceContentDirectory); + } + private static IReadOnlyDictionary> GetFilesKeyValuePairs(ReadOnlyCollection filesCollection) { Dictionary> results = []; @@ -82,6 +188,75 @@ internal abstract class Container return results; } + private static IReadOnlyDictionary> GetFilesKeyValuePairs(ReadOnlyCollection> filePathsCollection) + { + Dictionary> results = []; + List? collection; + string fileNameWithoutExtensionMinusOne; + foreach (ReadOnlyCollection filePaths in filePathsCollection) + { + foreach (FilePath filePath in filePaths) + { + fileNameWithoutExtensionMinusOne = filePath.NameWithoutExtension[..^1]; + if (!results.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) + { + results.Add(fileNameWithoutExtensionMinusOne, []); + if (!results.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) + throw new Exception(); + } + collection.Add(filePath.FullName); + } + } + return results; + } + + private static List GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles) + { + List? results = null; + int renamed; + const bool useCeilingAverage = true; + ReadOnlyCollection? jsonFilesCollection = null; + IReadOnlyDictionary>? compareFileNamesToFiles = null; + for (int i = 0; i < short.MaxValue; i++) + { + renamed = 0; + jsonFilesCollection = IDirectory.GetFilesCollection(jsonGroupDirectory, directorySearchFilter, extension, useCeilingAverage); + compareFileNamesToFiles = GetFilesKeyValuePairs(jsonFilesCollection); + renamed += LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); + results = IDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); + renamed += IDirectory.MaybeMove(propertyConfiguration, results, jsonGroupDirectory, extension); + if (renamed == 0) + break; + if (i > 10) + throw new NotImplementedException(); + } + if (results is null || jsonFilesCollection is null || compareFileNamesToFiles is null) + throw new NullReferenceException(nameof(results)); + return results; + } + + private static int LookForAbandoned(ReadOnlyCollection jsonFilesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension) + { + int result; + bool check; + bool moved = false; + List renameCollection = []; + foreach (string[] files in jsonFilesCollection) + { + if (files.Length == 0) + continue; + check = AnyMoved(fileNamesToFiles, extension, renameCollection, files); + if (!moved && check) + moved = true; + } + if (renameCollection.Count > 0) + IDirectory.MoveFiles(renameCollection, "{}", "{abd}"); + result = renameCollection.Count; + if (moved && result == 0) + result = 1; + return result; + } + private static bool AnyMoved(IReadOnlyDictionary> fileNamesToFiles, string extension, List renameCollection, string[] files) { bool result = false; @@ -139,125 +314,149 @@ internal abstract class Container return result; } - private static int LookForAbandoned(ReadOnlyCollection jsonFilesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension) - { - int result; - bool check; - bool moved = false; - List renameCollection = []; - foreach (string[] files in jsonFilesCollection) - { - if (files.Length == 0) - continue; - check = AnyMoved(fileNamesToFiles, extension, renameCollection, files); - if (!moved && check) - moved = true; - } - if (renameCollection.Count > 0) - IDirectory.MoveFiles(renameCollection, "{}", "{abd}"); - result = renameCollection.Count; - if (moved && result == 0) - result = 1; - return result; - } - - private static List GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles) - { - List? results = null; - int renamed; - const bool useCeilingAverage = true; - ReadOnlyCollection? jsonFilesCollection = null; - IReadOnlyDictionary>? compareFileNamesToFiles = null; - for (int i = 0; i < short.MaxValue; i++) - { - renamed = 0; - jsonFilesCollection = IDirectory.GetFilesCollection(jsonGroupDirectory, directorySearchFilter, extension, useCeilingAverage); - compareFileNamesToFiles = GetFilesKeyValuePairs(jsonFilesCollection); - renamed += LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); - results = IDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); - renamed += IDirectory.MaybeMove(propertyConfiguration, results, jsonGroupDirectory, extension); - if (renamed == 0) - break; - if (i > 10) - throw new NotImplementedException(); - } - if (results is null || jsonFilesCollection is null || compareFileNamesToFiles is null) - throw new NullReferenceException(nameof(results)); - return results; - } - - private static Property? GetProperty(Shared.Models.FilePair filePair) - { - Property? result; - if (filePair.Match is null) - result = null; - else - { - string json = File.ReadAllText(filePair.Match); - if (string.IsNullOrEmpty(json)) - result = null; - else - result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); - } - return result; - } - - private static ExifDirectory? GetExifDirectory(Shared.Models.FilePair filePair) - { - ExifDirectory? result; - if (filePair.Match is null) - result = null; - else - { - string json = File.ReadAllText(filePair.Match); - if (string.IsNullOrEmpty(json)) - result = null; - else - result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); - } - return result; - } - - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, string fullFileName) - { - string[] segments = directory.Split(cei.Combined); - string? checkDirectory = segments.Length == 1 ? - Path.Combine(segments[0], $"{cei.Combined[2..]}") : - segments.Length == 2 ? - $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : - null; - if (checkDirectory is not null && Directory.Exists(checkDirectory)) - { - string checkFile = Path.Combine(checkDirectory, fileName); - if (File.Exists(checkFile)) - File.Move(checkFile, fullFileName); - } - } - - private static void RenameFile(string extension, Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection filePaths) + private static void AnyMovedFace(string extension, string hiddenExtension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) { + string directory; string checkFile; - if (filePath.DirectoryFullPath.Contains("Results") && filePath.DirectoryFullPath.Contains("Resize")) - File.Delete(filePath.FullName); - if (!string.IsNullOrEmpty(filePair.Match)) + string directoryName; + List files = []; + string[] fileNameSegments; + List directories = []; + files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories)); + files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{hiddenExtension}", SearchOption.AllDirectories)); + foreach (string file in files) { - string directory = Path.GetDirectoryName(filePair.Match) ?? throw new Exception(); - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePair.Match); - string fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(filePair.Match)); - string extensionSecond = Path.GetExtension(fileNameWithoutExtension); - checkFile = Path.Combine(directory, $"{fileNameWithoutExtensionSecond[..^1]}{change}{extensionSecond}{extension}"); - if (!File.Exists(checkFile)) - File.Move(filePair.Match, checkFile); + directory = Path.GetDirectoryName(file) ?? throw new Exception(); + if (!directories.Contains(directory)) + directories.Add(directory); + directoryName = Path.GetFileName(directory); + fileNameSegments = Path.GetFileName(file).Split('.'); + if (fileNameSegments[0] != directoryName) + { + fileNameSegments[0] = string.Empty; + checkFile = Path.Combine(directory, $"{directoryName}{string.Join('.', fileNameSegments)}"); + if (!File.Exists(checkFile)) + File.Move(file, checkFile); + else + { + if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime) + File.Delete(file); + else + File.Move(file, checkFile, true); + } + } } - foreach (FilePath f in filePaths) + if (directories.Count > 0) + AnyMovedFace(fileNamesToFiles, directories); + } + + private static void AnyMovedFace(IReadOnlyDictionary> fileNamesToFiles, List directories) + { + bool result = false; + string checkFile; + string subDirectory; + string directoryName; + string checkDirectory; + List? collection; + string directoryNameWith; + string directoryNameMinusOne; + List directoryNames = []; + foreach (string directory in directories) { - checkFile = Path.Combine(f.DirectoryFullPath, $"{f.NameWithoutExtension[..^1]}{change}{f.ExtensionLowered}"); - if (File.Exists(checkFile)) + directoryNameMinusOne = Path.GetFileName(directory)[..^1]; + if (!fileNamesToFiles.TryGetValue(directoryNameMinusOne, out collection)) + throw new Exception(); + directoryNames.Clear(); + foreach (string f in collection) + directoryNames.Add(Path.GetFileName(Path.GetDirectoryName(f)) ?? throw new Exception()); + if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1) continue; - File.Move(f.FullName, checkFile); + directoryName = Path.GetFileName(Path.GetDirectoryName(directory)) ?? throw new Exception(); + if (directoryName != directoryNames[0]) + { + subDirectory = Path.GetDirectoryName(Path.GetDirectoryName(directory)) ?? throw new Exception(); + checkDirectory = Path.Combine(subDirectory, directoryNames[0]); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + directoryNameWith = collection.Count > 1 ? directoryName : $"{Path.GetFileNameWithoutExtension(collection[0])}"; + checkFile = Path.Combine(checkDirectory, directoryNameWith); + if (!result) + result = true; + if (!Directory.Exists(checkFile)) + Directory.Move(directory, checkFile); + else + { + if (new DirectoryInfo(directory).LastWriteTime > new DirectoryInfo(checkFile).LastWriteTime) + Directory.Delete(directory, recursive: true); + else + { + Directory.Delete(checkFile, recursive: true); + Directory.Move(directory, checkFile); + } + } + } } } + private static void AnyMovedDistance(string extension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) + { + bool result = false; + string fileName; + string checkFile; + string directory; + List? collection; + string[] fileNameSegments; + List fileNames = []; + string fileNameSegmentsZeroMinusOne; + string[] files = Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories); + foreach (string file in files) + { + fileName = Path.GetFileName(file); + fileNameSegments = fileName.Split('.'); + fileNameSegmentsZeroMinusOne = fileNameSegments[0][..^1]; + if (!fileNamesToFiles.TryGetValue(fileNameSegmentsZeroMinusOne, out collection)) + continue; + fileNames.Clear(); + foreach (string f in collection) + fileNames.Add(Path.GetFileNameWithoutExtension(f) ?? throw new Exception()); + if (fileNames.Count == 0 || fileNames.Distinct().Count() != 1) + continue; + if (fileNameSegments[0] != fileNames[0]) + { + fileNameSegments[0] = string.Empty; + directory = Path.GetDirectoryName(file) ?? throw new Exception(); + checkFile = Path.Combine(directory, $"{fileNames[0]}{string.Join('.', fileNameSegments)}"); + if (!result) + result = true; + if (!File.Exists(checkFile)) + File.Move(file, checkFile); + else + { + if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime) + File.Delete(file); + else + File.Move(file, checkFile, true); + } + } + } + } + + private static List GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + { + List results = []; + const string extension = ".json"; + List filePairs; + string jsonGroupDirectory = aPropertySingletonDirectory; + int maxDegreeOfParallelism = Environment.ProcessorCount; + int filesCollectionDirectoryLength = filesCollectionDirectory.Length; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + IReadOnlyDictionary> fileNamesToFiles = GetFilesKeyValuePairs(filePathsCollection); + filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles); + _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => + ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, filePairs[i], results)); + return results; + } + private static void ParallelFor(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, int rootDirectoryLength, Shared.Models.FilePair filePair, List results) { dlibDotNet?.Tick(); @@ -276,8 +475,8 @@ internal abstract class Container char? change; ReadOnlyCollection? filePaths = null; char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePath).ToString()[0]; - char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(filePath).ToString()[0]; - char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(filePath).ToString()[0]; + char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePath).ToString()[0]; + char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePath).ToString()[0]; if (shouldIgnore is not null && shouldIgnore.Value) { if (filePath.NameWithoutExtension[^1] == hasIgnoreKeyword) @@ -337,272 +536,76 @@ internal abstract class Container results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item)); } - private static List GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + private static Property? GetProperty(Shared.Models.FilePair filePair) { - List results = []; - const string extension = ".json"; - List filePairs; - string jsonGroupDirectory = aPropertySingletonDirectory; - int maxDegreeOfParallelism = Environment.ProcessorCount; - int filesCollectionDirectoryLength = filesCollectionDirectory.Length; - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - IReadOnlyDictionary> fileNamesToFiles = GetFilesKeyValuePairs(filePathsCollection); - filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles); - _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => - ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, filePairs[i], results)); - return results; + Property? result; + if (filePair.Match is null) + result = null; + else + { + string json = File.ReadAllText(filePair.Match); + if (string.IsNullOrEmpty(json)) + result = null; + else + result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); + } + return result; } - private static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + private static ExifDirectory? GetExifDirectory(Shared.Models.FilePair filePair) { - List results = []; - string directory; - List? items; - Models.Container container; - List directories = []; - Dictionary> directoryToItems = []; - foreach (ReadOnlyCollection filePaths in filePathsCollection) + ExifDirectory? result; + if (filePair.Match is null) + result = null; + else { - if (filePaths.Count == 0) - continue; - directory = filePaths[0].DirectoryFullPath; - if (directory is null) - continue; - if (!directories.Contains(directory)) - directories.Add(directory); - if (!directoryToItems.TryGetValue(directory, out items)) - { - directoryToItems.Add(directory, []); - if (!directoryToItems.TryGetValue(directory, out items)) - throw new Exception(); - } + string json = File.ReadAllText(filePair.Match); + if (string.IsNullOrEmpty(json)) + result = null; + else + result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); } - (string aResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories(); - string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); - if (!Directory.Exists(aPropertySingletonDirectory)) - _ = Directory.CreateDirectory(aPropertySingletonDirectory); - List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); - foreach (FilePair filePair in filePairs) - { - if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) - { - directoryToItems.Add(filePair.FilePath.DirectoryFullPath, []); - if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) - throw new Exception(); - } - items.Add(filePair.Item); - } - foreach (KeyValuePair> keyValuePair in directoryToItems) - { - if (keyValuePair.Value.Count == 0) - continue; - container = new(keyValuePair.Key, new(keyValuePair.Value)); - results.Add(container); - } - return (filePairs.Count, results.ToArray()); + return result; } - private static void AnyMovedFace(IReadOnlyDictionary> fileNamesToFiles, List directories) + private static void RenameFile(string extension, Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection filePaths) { - bool result = false; string checkFile; - string subDirectory; - string directoryName; - string checkDirectory; - List? collection; - string directoryNameWith; - string directoryNameMinusOne; - List directoryNames = []; - foreach (string directory in directories) + if (filePath.DirectoryFullPath.Contains("Results") && filePath.DirectoryFullPath.Contains("Resize")) + File.Delete(filePath.FullName); + if (!string.IsNullOrEmpty(filePair.Match)) { - directoryNameMinusOne = Path.GetFileName(directory)[..^1]; - if (!fileNamesToFiles.TryGetValue(directoryNameMinusOne, out collection)) - throw new Exception(); - directoryNames.Clear(); - foreach (string f in collection) - directoryNames.Add(Path.GetFileName(Path.GetDirectoryName(f)) ?? throw new Exception()); - if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1) + string directory = Path.GetDirectoryName(filePair.Match) ?? throw new Exception(); + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePair.Match); + string fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(filePair.Match)); + string extensionSecond = Path.GetExtension(fileNameWithoutExtension); + checkFile = Path.Combine(directory, $"{fileNameWithoutExtensionSecond[..^1]}{change}{extensionSecond}{extension}"); + if (!File.Exists(checkFile)) + File.Move(filePair.Match, checkFile); + } + foreach (FilePath f in filePaths) + { + checkFile = Path.Combine(f.DirectoryFullPath, $"{f.NameWithoutExtension[..^1]}{change}{f.ExtensionLowered}"); + if (File.Exists(checkFile)) continue; - directoryName = Path.GetFileName(Path.GetDirectoryName(directory)) ?? throw new Exception(); - if (directoryName != directoryNames[0]) - { - subDirectory = Path.GetDirectoryName(Path.GetDirectoryName(directory)) ?? throw new Exception(); - checkDirectory = Path.Combine(subDirectory, directoryNames[0]); - if (!Directory.Exists(checkDirectory)) - _ = Directory.CreateDirectory(checkDirectory); - directoryNameWith = collection.Count > 1 ? directoryName : $"{Path.GetFileNameWithoutExtension(collection[0])}"; - checkFile = Path.Combine(checkDirectory, directoryNameWith); - if (!result) - result = true; - if (!Directory.Exists(checkFile)) - Directory.Move(directory, checkFile); - else - { - if (new DirectoryInfo(directory).LastWriteTime > new DirectoryInfo(checkFile).LastWriteTime) - Directory.Delete(directory, recursive: true); - else - { - Directory.Delete(checkFile, recursive: true); - Directory.Move(directory, checkFile); - } - } - } + File.Move(f.FullName, checkFile); } } - private static void AnyMovedFace(string extension, string hiddenExtension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) + private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, string fullFileName) { - string directory; - string checkFile; - string directoryName; - List files = []; - string[] fileNameSegments; - List directories = []; - files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories)); - files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{hiddenExtension}", SearchOption.AllDirectories)); - foreach (string file in files) + string[] segments = directory.Split(cei.Combined); + string? checkDirectory = segments.Length == 1 ? + Path.Combine(segments[0], $"{cei.Combined[2..]}") : + segments.Length == 2 ? + $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : + null; + if (checkDirectory is not null && Directory.Exists(checkDirectory)) { - directory = Path.GetDirectoryName(file) ?? throw new Exception(); - if (!directories.Contains(directory)) - directories.Add(directory); - directoryName = Path.GetFileName(directory); - fileNameSegments = Path.GetFileName(file).Split('.'); - if (fileNameSegments[0] != directoryName) - { - fileNameSegments[0] = string.Empty; - checkFile = Path.Combine(directory, $"{directoryName}{string.Join('.', fileNameSegments)}"); - if (!File.Exists(checkFile)) - File.Move(file, checkFile); - else - { - if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime) - File.Delete(file); - else - File.Move(file, checkFile, true); - } - } + string checkFile = Path.Combine(checkDirectory, fileName); + if (File.Exists(checkFile)) + File.Move(checkFile, fullFileName); } - if (directories.Count > 0) - AnyMovedFace(fileNamesToFiles, directories); - } - - private static void AnyMovedDistance(string extension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) - { - bool result = false; - string fileName; - string checkFile; - string directory; - List? collection; - string[] fileNameSegments; - List fileNames = []; - string fileNameSegmentsZeroMinusOne; - string[] files = Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories); - foreach (string file in files) - { - fileName = Path.GetFileName(file); - fileNameSegments = fileName.Split('.'); - fileNameSegmentsZeroMinusOne = fileNameSegments[0][..^1]; - if (!fileNamesToFiles.TryGetValue(fileNameSegmentsZeroMinusOne, out collection)) - continue; - fileNames.Clear(); - foreach (string f in collection) - fileNames.Add(Path.GetFileNameWithoutExtension(f) ?? throw new Exception()); - if (fileNames.Count == 0 || fileNames.Distinct().Count() != 1) - continue; - if (fileNameSegments[0] != fileNames[0]) - { - fileNameSegments[0] = string.Empty; - directory = Path.GetDirectoryName(file) ?? throw new Exception(); - checkFile = Path.Combine(directory, $"{fileNames[0]}{string.Join('.', fileNameSegments)}"); - if (!result) - result = true; - if (!File.Exists(checkFile)) - File.Move(file, checkFile); - else - { - if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime) - File.Delete(file); - else - File.Move(file, checkFile, true); - } - } - } - } - - private static void DoGetFilePairsForRemaining(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) - { - const string extension = ".json"; - (_, string bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); - IReadOnlyDictionary> fileNamesToFiles = GetFilesKeyValuePairs(filePathsCollection); - string bMetaSingletonDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); - if (!Directory.Exists(bMetaSingletonDirectory)) - _ = Directory.CreateDirectory(bMetaSingletonDirectory); - _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, bMetaSingletonDirectory, filePathsCollection, fileNamesToFiles); - (string cResultsFullGroupDirectory, _, string dResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories("Original"); - string cResizeSingletonDirectory = Path.Combine(cResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); - if (!Directory.Exists(cResizeSingletonDirectory)) - _ = Directory.CreateDirectory(cResizeSingletonDirectory); - _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, cResizeSingletonDirectory, filePathsCollection, fileNamesToFiles); - string dFaceCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultCollection); - if (!Directory.Exists(dFaceCollectionDirectory)) - _ = Directory.CreateDirectory(dFaceCollectionDirectory); - _ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, dFaceCollectionDirectory, filePathsCollection, fileNamesToFiles); - string dFaceContentDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultContent); - if (!Directory.Exists(dFaceContentDirectory)) - _ = Directory.CreateDirectory(dFaceContentDirectory); - AnyMovedFace(facesFileNameExtension, facesHiddenFileNameExtension, fileNamesToFiles, dFaceContentDirectory); - AnyMovedDistance(facesFileNameExtension, fileNamesToFiles, eDistanceContentDirectory); - } - - internal static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) - { - Models.Container[] results; - const string directorySearchFilter = "*"; - (_, results) = GetContainers(dlibDotNet, propertyConfiguration, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); - if (keyValuePairs is not null) - DoGetFilePairsForRemaining(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filePathsCollection, directorySearchFilter); - return results.AsReadOnly(); - } - - private static IDlibDotNet GetDlibDotNet() => - throw new NotImplementedException(); - - internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string _) - { - int count; - Models.Container[] results; - const bool useCeilingAverage = true; - const string fileSearchFilter = "*"; - IDlibDotNet dlibDotNet = GetDlibDotNet(); - const string directorySearchFilter = "*"; - ReadOnlyDictionary>? keyValuePairs = null; - ReadOnlyCollection filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); - ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection); - (count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter); - return (count, results); - } - - internal static List GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) - { - List results = []; - ReadOnlyCollection filteredItems; - foreach (Models.Container container in readOnlyContainers) - { - if (container.Items.Count == 0) - continue; - filteredItems = GetValidImageItems(propertyConfiguration, container); - if (filteredItems.Count == 0) - continue; - foreach (Item item in filteredItems) - { - if (item.Property?.Id is null || item.ResizedFileHolder is null) - continue; - if (results.Contains(item.Property.Id.Value)) - continue; - results.Add(item.Property.Id.Value); - } - } - return results; } internal static List GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) diff --git a/Container/Models/Stateless/Methods/IContainer.cs b/Container/Models/Stateless/Methods/IContainer.cs index a877a0f..1d3752a 100644 --- a/Container/Models/Stateless/Methods/IContainer.cs +++ b/Container/Models/Stateless/Methods/IContainer.cs @@ -8,44 +8,52 @@ namespace View_by_Distance.Container.Models.Stateless.Methods; public interface IContainer { - DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection items) => - GetContainerDateTimes(items); - static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) => - Container.GetContainerDateTimes(items); - - ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => - GetValidImageItems(propertyConfiguration, container); - static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => - Container.GetValidImageItems(propertyConfiguration, container); - - (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, aPropertySingletonDirectory); - static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, null, aPropertySingletonDirectory); - - (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); - static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => + public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => Container.GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); - ReadOnlyCollection TestStatic_GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) => - GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); - static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) => - Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); + public static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) => + Container.GetContainerDateTimes(items); - List TestStatic_GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => - GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); - static List GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + public static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => + Container.GetValidImageItems(propertyConfiguration, container); + + public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => + GetContainers(propertyConfiguration, null, aPropertySingletonDirectory); + + public static List GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); - List TestStatic_GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => - GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); - static List GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + public static List GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => Container.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); - ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => - GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); - static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => + public static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => Container.GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); + public static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) => + Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); + + internal DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection items) => + GetContainerDateTimes(items); + + internal ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => + GetValidImageItems(propertyConfiguration, container); + + internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => + GetContainers(propertyConfiguration, aPropertySingletonDirectory); + + internal List TestStatic_GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); + + internal List TestStatic_GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => + GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); + + internal ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => + GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); + + internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => + GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); + + internal ReadOnlyCollection TestStatic_GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection) => + GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); + } \ No newline at end of file diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index 80ea83a..895e4c1 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -95,6 +95,73 @@ public class D_Face : IFaceD _WriteIndentedAndWhenWritingNull = new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; } + void IFaceD.ReSaveFace(ExifDirectory exifDirectory, FilePath filePath, Shared.Models.Face face, bool mappedFile) + { + FileInfo fileInfo = new(filePath.FullName); + if (fileInfo.Exists) + { + string? json; + short type = 2; + string? model; + string? maker; + string checkFile = $"{filePath.FullName}.exif"; + const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; + // const int author = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinAuthor; // 40093 + if (mappedFile) + { + json = IMetadata.GetOutputResolution(exifDirectory); + if (json is not null && json.Contains(nameof(DateTime))) + return; + (maker, model) = Get(json); + } + else + { + maker = IMetadata.GetMaker(exifDirectory); + model = IMetadata.GetModel(exifDirectory); + ExifDirectory? faceExifDirectory = IMetadata.GetExifDirectory(filePath); + json = IMetadata.GetOutputResolution(faceExifDirectory); + if (json is not null && json.Contains(nameof(DateTime))) + return; + } + MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); + FaceFile faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad, + face.Mapping?.MappingFromLocation?.ConfidencePercent, + geoLocation?.ToDmsString(), + face.DateTime, + null, + face.FaceParts, + face.Location, + maker, + null, + model, + face.OutputResolution); + string faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); + ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception(); + PropertyItem? propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(constructorInfo, artist, type, faceFileJson); +#pragma warning disable CA1416 + Bitmap bitmap = new(fileInfo.FullName); + bitmap.SetPropertyItem(propertyItem); + bitmap.Save(checkFile); + bitmap.Dispose(); +#pragma warning restore CA1416 + File.SetLastWriteTime(checkFile, fileInfo.LastWriteTime); + File.Delete(fileInfo.FullName); + File.Move(checkFile, fileInfo.FullName); + } + } + + private static (string?, string?) Get(string? json) + { + string? model; + string? maker; + FaceFile? faceFile = json is null ? null : JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile); + if (faceFile is null || faceFile.Location is null) + (maker, model) = (null, null); + else + (maker, model) = (faceFile.Maker, faceFile.Model); + return (maker, model); + } + public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); @@ -115,194 +182,50 @@ public class D_Face : IFaceD } } - private static (Model model, PredictorModel predictorModel, ModelParameter modelParameter) GetModel(string modelDirectory, string modelName, string predictorModelName) + public List<(Shared.Models.Face, FileHolder?, string, bool)> SaveFaces(FilePath filePath, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces) { - (Model, PredictorModel, ModelParameter) result; - Array array; - Model? model = null; - array = Enum.GetValues(); - PredictorModel? predictorModel = null; - foreach (Model check in array) + List<(Shared.Models.Face, FileHolder?, string, bool Save)> results = []; + bool save; + FileInfo fileInfo; + FileHolder fileHolder; + string deterministicHashCodeKey; + string fileName = mappingFromItem.FilePath.NameWithoutExtension; + string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)]; + List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); + CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath); + string directory = _ResultContentFileGroups[0][cei.Enum][cei.Index]; + DirectoryInfo directoryInfo = new(Path.Combine(directory, fileName)); + MoveIf(fileName, cei, directory, directoryInfo); + foreach (Shared.Models.Face face in faces) { - if (modelName.Contains(check.ToString())) + save = false; + if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) { - model = check; - break; + results.Add(new(face, null, string.Empty, save)); + continue; } + deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); + fileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}")); + fileHolder = FileHolder.Get(fileInfo); + if (!directoryInfo.Exists) + save = true; + else if (_OverrideForFaceImages) + save = true; + else if (!fileHolder.Exists) + save = true; + else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime) + save = true; + results.Add(new(face, fileHolder, Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_HiddenFileNameExtension}"), save)); } - if (model is null) - throw new Exception("Destination directory must have Model name!"); - model = model.Value; - array = Enum.GetValues(); - foreach (PredictorModel check in array) + if (results.Any(l => l.Save)) { - if (predictorModelName.Contains(check.ToString())) - { - predictorModel = check; - break; - } - } - if (predictorModel is null) - throw new Exception("Destination directory must have Predictor Model name!"); - predictorModel = predictorModel.Value; - ModelParameter modelParameter = new() - { - CnnFaceDetectorModel = File.ReadAllBytes(Path.Combine(modelDirectory, "mmod_human_face_detector.dat")), - FaceRecognitionModel = File.ReadAllBytes(Path.Combine(modelDirectory, "dlib_face_recognition_resnet_model_v1.dat")), - PosePredictor5FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_5_face_landmarks.dat")), - PosePredictor68FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_68_face_landmarks.dat")) - }; - result = new(model.Value, predictorModel.Value, modelParameter); - return result; - } - -#pragma warning disable CA1416 - - private void SaveFaces(FileHolder resizedFileHolder, ExifDirectory exifDirectory, List<(Shared.Models.Face, FileHolder?, string, bool)> collection) - { - int width; - int height; - Bitmap bitmap; - short type = 2; - FaceFile faceFile; - Graphics graphics; - Location? location; - Rectangle rectangle; - string faceFileJson; - string faceEncodingJson; - PropertyItem? propertyItem; - string? maker = IMetadata.GetMaker(exifDirectory); - string? model = IMetadata.GetModel(exifDirectory); - using Bitmap source = new(resizedFileHolder.FullName); - MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); - const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315 - const int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment; - foreach ((Shared.Models.Face face, FileHolder? fileHolder, string fileName, bool save) in collection) - { - if (!save) - continue; - if (fileHolder is null) - continue; - if (face.FaceEncoding is null || face?.Location is null || face?.OutputResolution is null) - continue; - if (_OverrideForFaceImages && fileHolder.Exists) - { - IFaceD dFace = this; - FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null); - dFace.ReSaveFace(exifDirectory, filePath, face, mappedFile: false); - continue; - } - location = Shared.Models.Stateless.Methods.ILocation.GetLocation(face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count); - if (location is null) - continue; - width = location.Right - location.Left; - height = location.Bottom - location.Top; - faceEncodingJson = JsonSerializer.Serialize(face.FaceEncoding); - rectangle = new Rectangle(location.Left, location.Top, width, height); - faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad, - face.Mapping?.MappingFromLocation?.ConfidencePercent, - geoLocation?.ToDmsString(), - face.DateTime, - null, - face.FaceParts, - face.Location, - maker, - null, - model, - face.OutputResolution); - faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); - using (bitmap = new(width, height)) - { - using (graphics = Graphics.FromImage(bitmap)) - graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel); - propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson); - bitmap.SetPropertyItem(propertyItem); - propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, userComment, type, faceEncodingJson); - bitmap.SetPropertyItem(propertyItem); - bitmap.Save(fileHolder.FullName, _ImageCodecInfo, _EncoderParameters); - } - if (File.Exists(fileName)) - File.Delete(fileName); - location = Shared.Models.Stateless.Methods.ILocation.GetLocation(_FaceDistanceHiddenImageFactor, face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count); - if (location is null) - continue; - width = location.Right - location.Left; - height = location.Bottom - location.Top; - rectangle = new Rectangle(location.Left, location.Top, width, height); - using (bitmap = new(width, height)) - { - using (graphics = Graphics.FromImage(bitmap)) - graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel); - bitmap.Save(fileName, _HiddenImageCodecInfo, _HiddenEncoderParameters); - } - File.SetAttributes(fileName, FileAttributes.Hidden); - } - } - - private List GetFaces(string outputResolution, string cResultsFullGroupDirectory, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List locations) - { - if (_PropertyConfiguration.NumberOfJitters is null) - throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); - if (_PropertyConfiguration.NumberOfTimesToUpsample is null) - throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample)); - List results = []; - FaceRecognitionDotNet.Image? unknownImage; - try - { - if (mappingFromItem.ResizedFileHolder.ExtensionLowered != ".tif") - unknownImage = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName); - else - { - int outputQuality = 100; - string extension = ".png"; - string file = Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{extension}"); - (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(extension, outputQuality); -#pragma warning disable CA1416 - System.Drawing.Image image = System.Drawing.Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); - image.Save(Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{filenameExtension}"), imageCodecInfo, encoderParameters); - image.Dispose(); -#pragma warning restore CA1416 - unknownImage = FaceRecognition.LoadImageFile(file); - File.Delete(file); - } - } - catch (Exception) - { unknownImage = null; } - if (unknownImage is not null) - { - (int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize); - List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; - FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, _Model, _ModelParameter, _PredictorModel); - collection = faceRecognition.GetCollection(unknownImage, locations, includeFaceEncoding: true, includeFaceParts: true); - if (collection.Count == 0) - results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null)); - else - { - double[] rawEncoding; - Shared.Models.Face face; - Shared.Models.FaceEncoding convertedFaceEncoding; - foreach ((Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary? faceParts) in collection) - { - face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location); - if (faceEncoding is not null) - { - rawEncoding = faceEncoding.GetRawEncoding(); - convertedFaceEncoding = new(rawEncoding, faceEncoding.Size); - face.SetFaceEncoding(convertedFaceEncoding); - } - if (faceParts is not null) - face.SetFaceParts(faceParts); - results.Add(face); - } - } - unknownImage.Dispose(); - faceRecognition.Dispose(); + if (!directoryInfo.Exists) + _ = Directory.CreateDirectory(directoryInfo.FullName); + SaveFaces(mappingFromItem.ResizedFileHolder, exifDirectory, results); } return results; } -#pragma warning restore CA1416 - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo) { string[] segments = directory.Split(cei.Combined); @@ -322,6 +245,25 @@ public class D_Face : IFaceD } } + private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, DirectoryInfo directoryInfo) + { + string[] segments = directory.Split(cei.Combined); + string? checkDirectory = segments.Length == 1 ? + Path.Combine(segments[0], $"{cei.Combined[2..]}") : + segments.Length == 2 ? + $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : + null; + if (checkDirectory is not null && Directory.Exists(checkDirectory)) + { + string checkFile = Path.Combine(checkDirectory, fileName); + if (Directory.Exists(checkFile)) + { + Directory.Move(checkFile, directoryInfo.FullName); + directoryInfo.Refresh(); + } + } + } + public List GetFaces(string outputResolution, string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List? mappingFromPhotoPrismCollection) { List? results; @@ -396,136 +338,190 @@ public class D_Face : IFaceD return results; } - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, DirectoryInfo directoryInfo) + private List GetFaces(string outputResolution, string cResultsFullGroupDirectory, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List locations) { - string[] segments = directory.Split(cei.Combined); - string? checkDirectory = segments.Length == 1 ? - Path.Combine(segments[0], $"{cei.Combined[2..]}") : - segments.Length == 2 ? - $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : - null; - if (checkDirectory is not null && Directory.Exists(checkDirectory)) + if (_PropertyConfiguration.NumberOfJitters is null) + throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); + if (_PropertyConfiguration.NumberOfTimesToUpsample is null) + throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample)); + List results = []; + FaceRecognitionDotNet.Image? unknownImage; + try { - string checkFile = Path.Combine(checkDirectory, fileName); - if (Directory.Exists(checkFile)) + if (mappingFromItem.ResizedFileHolder.ExtensionLowered != ".tif") + unknownImage = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName); + else { - Directory.Move(checkFile, directoryInfo.FullName); - directoryInfo.Refresh(); + int outputQuality = 100; + string extension = ".png"; + string file = Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{extension}"); + (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(extension, outputQuality); +#pragma warning disable CA1416 + System.Drawing.Image image = System.Drawing.Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); + image.Save(Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{filenameExtension}"), imageCodecInfo, encoderParameters); + image.Dispose(); +#pragma warning restore CA1416 + unknownImage = FaceRecognition.LoadImageFile(file); + File.Delete(file); } } - } - - public List<(Shared.Models.Face, FileHolder?, string, bool)> SaveFaces(FilePath filePath, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces) - { - List<(Shared.Models.Face, FileHolder?, string, bool Save)> results = []; - bool save; - FileInfo fileInfo; - FileHolder fileHolder; - string deterministicHashCodeKey; - string fileName = mappingFromItem.FilePath.NameWithoutExtension; - string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)]; - List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); - CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath); - string directory = _ResultContentFileGroups[0][cei.Enum][cei.Index]; - DirectoryInfo directoryInfo = new(Path.Combine(directory, fileName)); - MoveIf(fileName, cei, directory, directoryInfo); - foreach (Shared.Models.Face face in faces) + catch (Exception) + { unknownImage = null; } + if (unknownImage is not null) { - save = false; - if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) + (int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize); + List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; + FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, _Model, _ModelParameter, _PredictorModel); + collection = faceRecognition.GetCollection(unknownImage, locations, includeFaceEncoding: true, includeFaceParts: true); + if (collection.Count == 0) + results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null)); + else { - results.Add(new(face, null, string.Empty, save)); - continue; + double[] rawEncoding; + Shared.Models.Face face; + Shared.Models.FaceEncoding convertedFaceEncoding; + foreach ((Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary? faceParts) in collection) + { + face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location); + if (faceEncoding is not null) + { + rawEncoding = faceEncoding.GetRawEncoding(); + convertedFaceEncoding = new(rawEncoding, faceEncoding.Size); + face.SetFaceEncoding(convertedFaceEncoding); + } + if (faceParts is not null) + face.SetFaceParts(faceParts); + results.Add(face); + } } - deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); - fileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}")); - fileHolder = FileHolder.Get(fileInfo); - if (!directoryInfo.Exists) - save = true; - else if (_OverrideForFaceImages) - save = true; - else if (!fileHolder.Exists) - save = true; - else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime) - save = true; - results.Add(new(face, fileHolder, Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_HiddenFileNameExtension}"), save)); - } - if (results.Any(l => l.Save)) - { - if (!directoryInfo.Exists) - _ = Directory.CreateDirectory(directoryInfo.FullName); - SaveFaces(mappingFromItem.ResizedFileHolder, exifDirectory, results); + unknownImage.Dispose(); + faceRecognition.Dispose(); } return results; } -#pragma warning disable CA1416 - - private static (string?, string?) Get(string? json) + private static (Model model, PredictorModel predictorModel, ModelParameter modelParameter) GetModel(string modelDirectory, string modelName, string predictorModelName) { - string? model; - string? maker; - FaceFile? faceFile = json is null ? null : JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile); - if (faceFile is null || faceFile.Location is null) - (maker, model) = (null, null); - else - (maker, model) = (faceFile.Maker, faceFile.Model); - return (maker, model); + (Model, PredictorModel, ModelParameter) result; + Array array; + Model? model = null; + array = Enum.GetValues(); + PredictorModel? predictorModel = null; + foreach (Model check in array) + { + if (modelName.Contains(check.ToString())) + { + model = check; + break; + } + } + if (model is null) + throw new Exception("Destination directory must have Model name!"); + model = model.Value; + array = Enum.GetValues(); + foreach (PredictorModel check in array) + { + if (predictorModelName.Contains(check.ToString())) + { + predictorModel = check; + break; + } + } + if (predictorModel is null) + throw new Exception("Destination directory must have Predictor Model name!"); + predictorModel = predictorModel.Value; + ModelParameter modelParameter = new() + { + CnnFaceDetectorModel = File.ReadAllBytes(Path.Combine(modelDirectory, "mmod_human_face_detector.dat")), + FaceRecognitionModel = File.ReadAllBytes(Path.Combine(modelDirectory, "dlib_face_recognition_resnet_model_v1.dat")), + PosePredictor5FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_5_face_landmarks.dat")), + PosePredictor68FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_68_face_landmarks.dat")) + }; + result = new(model.Value, predictorModel.Value, modelParameter); + return result; } - void IFaceD.ReSaveFace(ExifDirectory exifDirectory, FilePath filePath, Shared.Models.Face face, bool mappedFile) + private void SaveFaces(FileHolder resizedFileHolder, ExifDirectory exifDirectory, List<(Shared.Models.Face, FileHolder?, string, bool)> collection) { - FileInfo fileInfo = new(filePath.FullName); - if (fileInfo.Exists) + int width; + int height; + Bitmap bitmap; + short type = 2; + FaceFile faceFile; + Graphics graphics; + Location? location; + Rectangle rectangle; + string faceFileJson; + string faceEncodingJson; + PropertyItem? propertyItem; + string? maker = IMetadata.GetMaker(exifDirectory); + string? model = IMetadata.GetModel(exifDirectory); +#pragma warning disable CA1416 + using Bitmap source = new(resizedFileHolder.FullName); + MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); + const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315 + const int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment; + foreach ((Shared.Models.Face face, FileHolder? fileHolder, string fileName, bool save) in collection) { - string? json; - short type = 2; - string? model; - string? maker; - string checkFile = $"{filePath.FullName}.exif"; - const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; - // const int author = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinAuthor; // 40093 - if (mappedFile) + if (!save) + continue; + if (fileHolder is null) + continue; + if (face.FaceEncoding is null || face?.Location is null || face?.OutputResolution is null) + continue; + if (_OverrideForFaceImages && fileHolder.Exists) { - json = IMetadata.GetOutputResolution(exifDirectory); - if (json is not null && json.Contains(nameof(DateTime))) - return; - (maker, model) = Get(json); + IFaceD dFace = this; + FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null); + dFace.ReSaveFace(exifDirectory, filePath, face, mappedFile: false); + continue; } - else + location = Shared.Models.Stateless.Methods.ILocation.GetLocation(face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count); + if (location is null) + continue; + width = location.Right - location.Left; + height = location.Bottom - location.Top; + faceEncodingJson = JsonSerializer.Serialize(face.FaceEncoding); + rectangle = new Rectangle(location.Left, location.Top, width, height); + faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad, + face.Mapping?.MappingFromLocation?.ConfidencePercent, + geoLocation?.ToDmsString(), + face.DateTime, + null, + face.FaceParts, + face.Location, + maker, + null, + model, + face.OutputResolution); + faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); + using (bitmap = new(width, height)) { - maker = IMetadata.GetMaker(exifDirectory); - model = IMetadata.GetModel(exifDirectory); - ExifDirectory? faceExifDirectory = IMetadata.GetExifDirectory(filePath); - json = IMetadata.GetOutputResolution(faceExifDirectory); - if (json is not null && json.Contains(nameof(DateTime))) - return; + using (graphics = Graphics.FromImage(bitmap)) + graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel); + propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson); + bitmap.SetPropertyItem(propertyItem); + propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, userComment, type, faceEncodingJson); + bitmap.SetPropertyItem(propertyItem); + bitmap.Save(fileHolder.FullName, _ImageCodecInfo, _EncoderParameters); } - MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); - FaceFile faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad, - face.Mapping?.MappingFromLocation?.ConfidencePercent, - geoLocation?.ToDmsString(), - face.DateTime, - null, - face.FaceParts, - face.Location, - maker, - null, - model, - face.OutputResolution); - string faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); - ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception(); - PropertyItem? propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(constructorInfo, artist, type, faceFileJson); - Bitmap bitmap = new(fileInfo.FullName); - bitmap.SetPropertyItem(propertyItem); - bitmap.Save(checkFile); - bitmap.Dispose(); - File.SetLastWriteTime(checkFile, fileInfo.LastWriteTime); - File.Delete(fileInfo.FullName); - File.Move(checkFile, fileInfo.FullName); + if (File.Exists(fileName)) + File.Delete(fileName); + location = Shared.Models.Stateless.Methods.ILocation.GetLocation(_FaceDistanceHiddenImageFactor, face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count); + if (location is null) + continue; + width = location.Right - location.Left; + height = location.Bottom - location.Top; + rectangle = new Rectangle(location.Left, location.Top, width, height); + using (bitmap = new(width, height)) + { + using (graphics = Graphics.FromImage(bitmap)) + graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel); + bitmap.Save(fileName, _HiddenImageCodecInfo, _HiddenEncoderParameters); + } +#pragma warning restore CA1416 + File.SetAttributes(fileName, FileAttributes.Hidden); } } -#pragma warning restore CA1416 - } \ No newline at end of file diff --git a/FaceParts/Models/_D2_FaceParts.cs b/FaceParts/Models/_D2_FaceParts.cs index 2164d5b..482d5e4 100644 --- a/FaceParts/Models/_D2_FaceParts.cs +++ b/FaceParts/Models/_D2_FaceParts.cs @@ -65,160 +65,18 @@ public class D2_FaceParts } } - private static void GetPointBounds(PointF[] points, out float xMinimum, out float xMaximum, out float yMinimum, out float yMaximum) + public void SaveFaceLandmarkImages(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces) { - xMinimum = points[0].X; - xMaximum = xMinimum; - yMinimum = points[0].Y; - yMaximum = yMinimum; - foreach (PointF point in points) + bool any = false; + foreach (Shared.Models.Face face in faces) { - if (xMinimum > point.X) - xMinimum = point.X; - if (xMaximum < point.X) - xMaximum = point.X; - if (yMinimum > point.Y) - yMinimum = point.Y; - if (yMaximum < point.Y) - yMaximum = point.Y; - } - } - -#pragma warning disable CA1416 - - private static Bitmap RotateBitmap(Bitmap bitmap, float angle) - { - Bitmap result; -#if Linux - throw new Exception("Built on Linux!"); -#elif OSX - throw new Exception("Built on macOS!"); -#elif Windows - // Make save Matrix to represent rotation - // by this angle. - Matrix rotate_at_origin = new(); - rotate_at_origin.Rotate(angle); - - // Rotate the image's corners to see how big - // it will be after rotation. - PointF[] points = - [ - new(0, 0), - new(bitmap.Width, 0), - new(bitmap.Width, bitmap.Height), - new(0, bitmap.Height), - ]; - rotate_at_origin.TransformPoints(points); - float xMinimum, xMaximum, yMinimum, yMaximum; - GetPointBounds(points, out xMinimum, out xMaximum, out yMinimum, out yMaximum); - - // Make save bitmap to hold the rotated result. - int wid = (int)Math.Round(xMaximum - xMinimum); - int hgt = (int)Math.Round(yMaximum - yMinimum); - result = new Bitmap(wid, hgt); - - // Create the real rotation transformation. - Matrix rotate_at_center = new(); - rotate_at_center.RotateAt(angle, - new PointF(wid / 2f, hgt / 2f)); - - // Draw the image onto the new bitmap rotated. - using (Graphics gr = Graphics.FromImage(result)) - { - // Use smooth image interpolation. - gr.InterpolationMode = InterpolationMode.High; - - // Clear with the color in the image's upper left corner. - gr.Clear(bitmap.GetPixel(0, 0)); - - // For debugging. (It's easier to see the background.) - // gr.Clear(Color.LightBlue); - - // Set up the transformation to rotate. - gr.Transform = rotate_at_center; - - // Draw the image centered on the bitmap. - int x = (wid - bitmap.Width) / 2; - int y = (hgt - bitmap.Height) / 2; - gr.DrawImage(bitmap, x, y); - } -#endif - // Return the result bitmap. - return result; - } - - private static Bitmap RotateBitmap(Image image, float angle) - { - Bitmap result; - Bitmap bitmap = new(image); - result = RotateBitmap(bitmap, angle); - bitmap?.Dispose(); - return result; - } - - private void SaveRotated(MappingFromItem mappingFromItem, List<(Shared.Models.Face, string, string)> collection) - { - double? α; - Bitmap rotated; - foreach ((Shared.Models.Face face, string _, string rotatedFileName) in collection) - { - if (face.FaceParts is null) + if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0) continue; - (_, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts); - if (α is null) - continue; - using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); - rotated = RotateBitmap(image, (float)α.Value); - if (rotated is not null) - { - rotated.Save(rotatedFileName, _ImageCodecInfo, _EncoderParameters); - rotated.Dispose(); - } - } - } - - private string GetSeasonDirectory(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, bool any) - { - string result; - string minimumDateYear = mappingFromItem.MinimumDateTime.ToString("yyyy"); - DateTime dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value; - (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); - string year = mappingFromItem.DateTimeOriginal is null ? $"{minimumDateYear[1..]}{minimumDateYear[0]}" : mappingFromItem.DateTimeOriginal.Value.ToString("yyyy"); - string directory = Path.Combine(d2ResultsFullGroupDirectory, $"[{_PropertyConfiguration.ResultContent}]", $"{year}.{season} {seasonName}"); - result = any ? Path.Combine(directory, "---") : Path.Combine(directory, "Complete"); - if (!Directory.Exists(result)) - _ = Directory.CreateDirectory(result); - return result; - } - - private void SaveImage(MappingFromItem mappingFromItem, string directory, Image image, List faceFiles) - { - short type = 2; - string faceFileJson; - PropertyItem? propertyItem; - const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315 - string fileName = Path.Combine(directory, $"{mappingFromItem.FilePath.Name}{_FileNameExtension}"); - try - { - foreach (int propertyId in image.PropertyIdList) - { - if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation) - continue; - image.RemovePropertyItem(propertyId); - } - faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray); - propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson); - image.SetPropertyItem(propertyItem); - image.Save(fileName, _ImageCodecInfo, _EncoderParameters); - } - catch (Exception ex) - { - if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName)) - File.Delete(fileName); - faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray); - if (!string.IsNullOrEmpty(faceFileJson)) - File.WriteAllText($"{fileName}.json", faceFileJson); + if (!any) + any = true; } + if (any) + SaveAllFaceParts(d2ResultsFullGroupDirectory, mappingFromItem, exifDirectory, faces); } private void SaveAllFaceParts(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces) @@ -237,6 +95,7 @@ public class D2_FaceParts string? maker = IMetadata.GetMaker(exifDirectory); string? model = IMetadata.GetModel(exifDirectory); MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); +#pragma warning disable CA1416 using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); using Graphics graphics = Graphics.FromImage(image); foreach (Shared.Models.Face face in faces) @@ -277,95 +136,11 @@ public class D2_FaceParts } } _ = graphics.Save(); +#pragma warning restore CA1416 string directory = GetSeasonDirectory(d2ResultsFullGroupDirectory, mappingFromItem, any); SaveImage(mappingFromItem, directory, image, faceFiles); } - private void SaveImage(string fileName, Image image, FaceFile faceFile) - { - short type = 2; - string faceFileJson; - PropertyItem? propertyItem; - const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315 - try - { - foreach (int propertyId in image.PropertyIdList) - { - if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation) - continue; - image.RemovePropertyItem(propertyId); - } - faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); - propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson); - image.SetPropertyItem(propertyItem); - image.Save(fileName, _ImageCodecInfo, _EncoderParameters); - } - catch (Exception ex) - { - if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName)) - File.Delete(fileName); - faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); - if (!string.IsNullOrEmpty(faceFileJson)) - File.WriteAllText($"{fileName}.json", faceFileJson); - } - } - - private void SaveFaceParts(MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<(Shared.Models.Face, string, string)> collection) - { - int x; - int y; - Brush brush; - int pointSize; - FaceFile faceFile; - MappingFromPerson? mappingFromPerson; - string? maker = IMetadata.GetMaker(exifDirectory); - string? model = IMetadata.GetModel(exifDirectory); - MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); - foreach ((Shared.Models.Face face, string fileName, string _) in collection) - { - try - { - if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0) - continue; - using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); - mappingFromPerson = face.Mapping?.MappingFromPerson; - brush = mappingFromPerson is null ? Brushes.Red : Brushes.GreenYellow; - faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad, - face.Mapping?.MappingFromLocation?.ConfidencePercent, - geoLocation?.ToDmsString(), - face.DateTime, - face.FaceEncoding, - face.FaceParts, - face.Location, - maker, - mappingFromPerson, - model, - face.OutputResolution); - using Graphics graphics = Graphics.FromImage(image); - pointSize = GetPointSize(face.FaceParts, defaultPointSize: 2); - foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts) - { - foreach (FacePoint facePoint in facePoints) - graphics.FillEllipse(brush, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2); - if (facePart == FacePart.Chin) - continue; - if (facePoints.Length < 3) - continue; - x = (int)(from l in facePoints select l.X).Average(); - y = (int)(from l in facePoints select l.Y).Average(); - graphics.FillEllipse(Brushes.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2); - } - _ = graphics.Save(); - SaveImage(fileName, image, faceFile); - } - catch (Exception) - { - if (File.Exists(fileName)) - File.Delete(fileName); - } - } - } - private int GetPointSize(Dictionary faceParts, int defaultPointSize) { int result; @@ -382,28 +157,53 @@ public class D2_FaceParts return result; } -#pragma warning restore CA1416 - - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, DirectoryInfo directoryInfo) + private string GetSeasonDirectory(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, bool any) { - string[] segments = directory.Split(cei.Combined); - string? checkDirectory = segments.Length == 1 ? - Path.Combine(segments[0], $"{cei.Combined[2..]}") : - segments.Length == 2 ? - $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : - null; - if (checkDirectory is not null && Directory.Exists(checkDirectory)) + string result; + string minimumDateYear = mappingFromItem.MinimumDateTime.ToString("yyyy"); + DateTime dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value; + (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); + string year = mappingFromItem.DateTimeOriginal is null ? $"{minimumDateYear[1..]}{minimumDateYear[0]}" : mappingFromItem.DateTimeOriginal.Value.ToString("yyyy"); + string directory = Path.Combine(d2ResultsFullGroupDirectory, $"[{_PropertyConfiguration.ResultContent}]", $"{year}.{season} {seasonName}"); + result = any ? Path.Combine(directory, "---") : Path.Combine(directory, "Complete"); + if (!Directory.Exists(result)) + _ = Directory.CreateDirectory(result); + return result; + } + + private void SaveImage(MappingFromItem mappingFromItem, string directory, Image image, List faceFiles) + { + short type = 2; + string faceFileJson; + PropertyItem? propertyItem; + const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315 + string fileName = Path.Combine(directory, $"{mappingFromItem.FilePath.Name}{_FileNameExtension}"); + try { - string checkFile = Path.Combine(checkDirectory, fileName); - if (Directory.Exists(checkFile)) +#pragma warning disable CA1416 + foreach (int propertyId in image.PropertyIdList) { - Directory.Move(checkFile, directoryInfo.FullName); - directoryInfo.Refresh(); + if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation) + continue; + image.RemovePropertyItem(propertyId); } + faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray); + propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson); + image.SetPropertyItem(propertyItem); + image.Save(fileName, _ImageCodecInfo, _EncoderParameters); +#pragma warning restore CA1416 + } + catch (Exception ex) + { + if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + File.Delete(fileName); + faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray); + if (!string.IsNullOrEmpty(faceFileJson)) + File.WriteAllText($"{fileName}.json", faceFileJson); } } - public void SaveFaceLandmarkImages(Property.Models.Configuration configuration, string d2ResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces, bool saveRotated) + public void SaveFaceLandmarkImages(Configuration configuration, string d2ResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces, bool saveRotated) { FileInfo fileInfo; bool check = false; @@ -461,6 +261,118 @@ public class D2_FaceParts } } + private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, DirectoryInfo directoryInfo) + { + string[] segments = directory.Split(cei.Combined); + string? checkDirectory = segments.Length == 1 ? + Path.Combine(segments[0], $"{cei.Combined[2..]}") : + segments.Length == 2 ? + $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : + null; + if (checkDirectory is not null && Directory.Exists(checkDirectory)) + { + string checkFile = Path.Combine(checkDirectory, fileName); + if (Directory.Exists(checkFile)) + { + Directory.Move(checkFile, directoryInfo.FullName); + directoryInfo.Refresh(); + } + } + } + + private static Bitmap RotateBitmap(Image image, float angle) + { + Bitmap result; +#pragma warning disable CA1416 + Bitmap bitmap = new(image); + result = RotateBitmap(bitmap, angle); + bitmap?.Dispose(); +#pragma warning restore CA1416 + return result; + } + + private static Bitmap RotateBitmap(Bitmap bitmap, float angle) + { + Bitmap result; +#if Linux + throw new Exception("Built on Linux!"); +#elif OSX + throw new Exception("Built on macOS!"); +#elif Windows + // Make save Matrix to represent rotation + // by this angle. +#pragma warning disable CA1416 + Matrix rotate_at_origin = new(); + rotate_at_origin.Rotate(angle); + + // Rotate the image's corners to see how big + // it will be after rotation. + PointF[] points = + [ + new(0, 0), + new(bitmap.Width, 0), + new(bitmap.Width, bitmap.Height), + new(0, bitmap.Height), + ]; + rotate_at_origin.TransformPoints(points); + float xMinimum, xMaximum, yMinimum, yMaximum; + GetPointBounds(points, out xMinimum, out xMaximum, out yMinimum, out yMaximum); + + // Make save bitmap to hold the rotated result. + int wid = (int)Math.Round(xMaximum - xMinimum); + int hgt = (int)Math.Round(yMaximum - yMinimum); + result = new Bitmap(wid, hgt); + + // Create the real rotation transformation. + Matrix rotate_at_center = new(); + rotate_at_center.RotateAt(angle, + new PointF(wid / 2f, hgt / 2f)); + + // Draw the image onto the new bitmap rotated. + using (Graphics gr = Graphics.FromImage(result)) + { + // Use smooth image interpolation. + gr.InterpolationMode = InterpolationMode.High; + + // Clear with the color in the image's upper left corner. + gr.Clear(bitmap.GetPixel(0, 0)); + + // For debugging. (It's easier to see the background.) + // gr.Clear(Color.LightBlue); + + // Set up the transformation to rotate. + gr.Transform = rotate_at_center; + + // Draw the image centered on the bitmap. + int x = (wid - bitmap.Width) / 2; + int y = (hgt - bitmap.Height) / 2; + gr.DrawImage(bitmap, x, y); + } +#pragma warning restore CA1416 +#endif + // Return the result bitmap. + return result; + } + + private static void GetPointBounds(PointF[] points, out float xMinimum, out float xMaximum, out float yMinimum, out float yMaximum) + { + xMinimum = points[0].X; + xMaximum = xMinimum; + yMinimum = points[0].Y; + yMaximum = yMinimum; + foreach (PointF point in points) + { + if (xMinimum > point.X) + xMinimum = point.X; + if (xMaximum < point.X) + xMaximum = point.X; + if (yMinimum > point.Y) + yMinimum = point.Y; + if (yMaximum < point.Y) + yMaximum = point.Y; + } + } + private static bool GetNotMapped(string facePartsCollectionDirectory, List<(Shared.Models.Face Face, FileHolder?, string, bool)> faceCollection) { bool results = false; @@ -494,18 +406,116 @@ public class D2_FaceParts return results; } - public void SaveFaceLandmarkImages(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List faces) + private void SaveImage(string fileName, Image image, FaceFile faceFile) { - bool any = false; - foreach (Shared.Models.Face face in faces) + short type = 2; + string faceFileJson; + PropertyItem? propertyItem; + const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315 + try { - if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0) - continue; - if (!any) - any = true; +#pragma warning disable CA1416 + foreach (int propertyId in image.PropertyIdList) + { + if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation) + continue; + image.RemovePropertyItem(propertyId); + } + faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); + propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson); + image.SetPropertyItem(propertyItem); + image.Save(fileName, _ImageCodecInfo, _EncoderParameters); +#pragma warning restore CA1416 + } + catch (Exception ex) + { + if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName)) + File.Delete(fileName); + faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile); + if (!string.IsNullOrEmpty(faceFileJson)) + File.WriteAllText($"{fileName}.json", faceFileJson); + } + } + + private void SaveRotated(MappingFromItem mappingFromItem, List<(Shared.Models.Face, string, string)> collection) + { + double? α; + Bitmap rotated; + foreach ((Shared.Models.Face face, string _, string rotatedFileName) in collection) + { +#pragma warning disable CA1416 + if (face.FaceParts is null) + continue; + (_, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts); + if (α is null) + continue; + using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); + rotated = RotateBitmap(image, (float)α.Value); + if (rotated is not null) + { + rotated.Save(rotatedFileName, _ImageCodecInfo, _EncoderParameters); + rotated.Dispose(); + } +#pragma warning restore CA1416 + } + } + + private void SaveFaceParts(MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<(Shared.Models.Face, string, string)> collection) + { + int x; + int y; + Brush brush; + int pointSize; + FaceFile faceFile; + MappingFromPerson? mappingFromPerson; + string? maker = IMetadata.GetMaker(exifDirectory); + string? model = IMetadata.GetModel(exifDirectory); + MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); + foreach ((Shared.Models.Face face, string fileName, string _) in collection) + { + try + { +#pragma warning disable CA1416 + if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0) + continue; + using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); + mappingFromPerson = face.Mapping?.MappingFromPerson; + brush = mappingFromPerson is null ? Brushes.Red : Brushes.GreenYellow; + faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad, + face.Mapping?.MappingFromLocation?.ConfidencePercent, + geoLocation?.ToDmsString(), + face.DateTime, + face.FaceEncoding, + face.FaceParts, + face.Location, + maker, + mappingFromPerson, + model, + face.OutputResolution); + using Graphics graphics = Graphics.FromImage(image); + pointSize = GetPointSize(face.FaceParts, defaultPointSize: 2); + foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts) + { + foreach (FacePoint facePoint in facePoints) + graphics.FillEllipse(brush, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2); + if (facePart == FacePart.Chin) + continue; + if (facePoints.Length < 3) + continue; + x = (int)(from l in facePoints select l.X).Average(); + y = (int)(from l in facePoints select l.Y).Average(); + graphics.FillEllipse(Brushes.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2); + } + _ = graphics.Save(); + SaveImage(fileName, image, faceFile); +#pragma warning restore CA1416 + } + catch (Exception) + { + if (File.Exists(fileName)) + File.Delete(fileName); + } } - if (any) - SaveAllFaceParts(d2ResultsFullGroupDirectory, mappingFromItem, exifDirectory, faces); } } \ No newline at end of file diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 9729cc4..1fca26f 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -25,9 +25,6 @@ namespace View_by_Distance.Instance; public partial class DlibDotNet : IDlibDotNet, IDisposable { - [GeneratedRegex(@"[\\,\/,\:,\*,\?,\"",\<,\>,\|]")] - private static partial Regex CameraRegex(); - private readonly D_Face _Faces; private ProgressBar? _ProgressBar; private readonly C_Resize _Resize; @@ -168,989 +165,6 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable _Logger?.LogInformation("First run completed. Run again if wanted"); } - void IDlibDotNet.Tick() => - _ProgressBar?.Tick(); - - void IDisposable.Dispose() - { - _ProgressBar?.Dispose(); - GC.SuppressFinalize(this); - } - - private static void Verify(Models.Configuration configuration) - { - if (configuration.RangeDaysDeltaTolerance.Length != 3) - throw new NullReferenceException(nameof(configuration.RangeDaysDeltaTolerance)); - if (configuration.RangeDistanceTolerance.Length != 3) - throw new NullReferenceException(nameof(configuration.RangeDistanceTolerance)); - if (configuration.RangeFaceAreaTolerance.Length != 3) - throw new NullReferenceException(nameof(configuration.RangeFaceAreaTolerance)); - if (configuration.RangeFaceConfidence.Length != 3) - throw new NullReferenceException(nameof(configuration.RangeFaceConfidence)); - if (configuration.LocationContainerDistanceTolerance is null && !string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) - throw new NullReferenceException($"{nameof(configuration.LocationContainerDistanceTolerance)} must have a value when {nameof(configuration.LocationContainerDebugDirectory)} is set!"); - _ = DateTime.Now.AddDays(-configuration.RangeDaysDeltaTolerance[1]); - if (configuration.OutputResolutions.Length == 0 || string.IsNullOrEmpty(configuration.OutputResolutions[0]) || !configuration.ValidResolutions.Contains(configuration.OutputResolutions[0])) - throw new NullReferenceException($"{nameof(configuration.OutputResolutions)} must be fileNameToCollection valid outputResolution!"); - if ((from l in configuration.OutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) - throw new Exception($"One or more {nameof(configuration.OutputResolutions)} are not in the ValidResolutions list!"); - if ((from l in configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) - throw new Exception($"One or more {nameof(configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions)} are not in the ValidResolutions list!"); - if ((from l in configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) - throw new Exception($"One or more {nameof(configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions)} are not in the ValidResolutions list!"); - if ((from l in configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) - throw new Exception($"One or more {nameof(configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions)} are not in the ValidResolutions list!"); - if ((from l in configuration.SaveShortcutsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) - throw new Exception($"One or more {nameof(configuration.SaveShortcutsForOutputResolutions)} are not in the ValidResolutions list!"); - if ((from l in configuration.SaveFaceLandmarkForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) - throw new Exception($"One or more {nameof(configuration.SaveFaceLandmarkForOutputResolutions)} are not in the ValidResolutions list!"); - if ((from l in configuration.SaveFaceLandmarkForOutputResolutionsV2 where !configuration.ValidResolutions.Contains(l) select false).Any()) - throw new Exception($"One or more {nameof(configuration.SaveFaceLandmarkForOutputResolutionsV2)} are not in the ValidResolutions list!"); - if (string.IsNullOrEmpty(configuration.ModelName)) - throw new NullReferenceException(nameof(configuration.ModelName)); - if (string.IsNullOrEmpty(configuration.OutputExtension)) - throw new NullReferenceException(nameof(configuration.OutputExtension)); - if (string.IsNullOrEmpty(configuration.PredictorModelName)) - throw new NullReferenceException(nameof(configuration.PredictorModelName)); - if (string.IsNullOrEmpty(configuration.ModelDirectory) || !Directory.Exists(configuration.ModelDirectory)) - throw new NullReferenceException(nameof(configuration.ModelDirectory)); - } - - private void VerifyExtra(List args, Property.Models.Configuration propertyConfiguration, Models.Configuration configuration) - { - string[] sourceDirectoryNames; - if (args.Count == 0) - sourceDirectoryNames = []; - else - { - string? century; - string argZero = Path.GetFullPath(args[0]); - sourceDirectoryNames = argZero.Split(Path.DirectorySeparatorChar); - if (!argZero.StartsWith(propertyConfiguration.RootDirectory)) - throw new Exception($"Source directory must be inside root directory! <{argZero}> <{propertyConfiguration.RootDirectory}>"); - if (_ArgZeroIsConfigurationRootDirectory && propertyConfiguration.RootDirectory != argZero) - { - if (!configuration.MixedYearRelativePaths.Contains(sourceDirectoryNames[0])) - { - string[] segments = sourceDirectoryNames[0].Split(' '); - century = segments[^1].Length == 4 ? segments[^1][..2] : null; - if (segments.Length < 2 || century is null || (century != "18" && century != "19" && century != "20")) - throw new Exception("root subdirectory must have fileNameToCollection year at the end or directory name needs to be added to the exclude list!"); - } - } - } - string[] resizeMatch = (from l in sourceDirectoryNames where configuration.ValidResolutions.Contains(l) select l).ToArray(); - if (resizeMatch.Length > 0) - throw new Exception("Input directory should be the source and not fileNameToCollection resized directory!"); - if (configuration.LocationDigits != Shared.Models.Stateless.ILocation.Digits) - throw new Exception("Configuration has to match interface!"); - if (configuration.LocationFactor != Shared.Models.Stateless.ILocation.Factor) - throw new Exception("Configuration has to match interface!"); - if (configuration.SaveSortingWithoutPerson && configuration.JLinks.Length > 0) - throw new Exception("Configuration has SaveSortingWithoutPerson and JLinks!"); - if (configuration.SaveSortingWithoutPerson && !string.IsNullOrEmpty(configuration.FocusModel)) - throw new Exception("Configuration has SaveSortingWithoutPerson and FocusModel!"); - if (configuration.SaveSortingWithoutPerson && !string.IsNullOrEmpty(configuration.FocusDirectory)) - throw new Exception("Configuration has SaveSortingWithoutPerson and FocusDirectory!"); - } - - private static void DeleteContinueFiles(ReadOnlyCollection personContainers) - { - foreach (PersonContainer personContainer in personContainers) - { - foreach (FilePath filePath in personContainer.DisplayDirectoryAllFilePaths) - { - if (filePath.ExtensionLowered != ".continue") - continue; - File.Delete(filePath.FullName); - } - } - } - - private void MapFaceFileLogic(long ticks, ReadOnlyCollection personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory, ProgressBarOptions options) - { - foreach (string outputResolution in _Configuration.OutputResolutions) - { - if (_Exceptions.Count != 0) - break; - if (!_Configuration.SaveResizedSubfiles) - continue; - if (!_ArgZeroIsConfigurationRootDirectory) - continue; - if (outputResolution != _Configuration.OutputResolutions[0]) - continue; - if (_PropertyRootExistedBefore || a2PeopleContentDirectory is null) - break; - if (!_Configuration.SaveFaceDistancesForOutputResolutions.Contains(outputResolution)) - continue; - if (!_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) - continue; - List saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, options, mapLogic, outputResolution); - if (saveContainers.Count > 0) - { - int updated = 0; - mapLogic.SaveContainers(updated, saveContainers); - throw new NotSupportedException("Done Restart! :)"); - } - } - } - - private void Search(long ticks, ReadOnlyCollection personContainers, string argZero, string propertyRoot) - { - string message; - MapLogic? mapLogic; - A_Property propertyLogic; - IDlibDotNet dlibDotNet = this; - string eDistanceContentDirectory; - string? a2PeopleContentDirectory; - string aResultsFullGroupDirectory; - string bResultsFullGroupDirectory; - string cResultsFullGroupDirectory; - string fPhotoPrismContentDirectory; - const string fileSearchFilter = "*"; - string fPhotoPrismSingletonDirectory; - bool filesCollectionCountIsOne = false; - const string directorySearchFilter = "*"; - string? filesCollectionRootDirectory = null; - bool configurationOutputResolutionsHas = false; - ReadOnlyDictionary> personKeyToIds; - ReadOnlyDictionary? splatNineIdentifiers = null; - ReadOnlyDictionary>? keyValuePairs = null; - ReadOnlyCollection>? filePathsCollection = null; - bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks); - (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(_Configuration.PropertyConfiguration.RootDirectory, ticks); - a2PeopleContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), "([])"); - B_Metadata metadata = new(_Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory); - eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent); - string a2PeopleSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), _Configuration.PropertyConfiguration.ResultSingleton); - _ = Directory.CreateDirectory(Path.Combine(eDistanceContentDirectory, ticks.ToString())); - if (runToDoCollectionFirst) - mapLogic = null; - else - mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); - foreach (string outputResolution in _Configuration.OutputResolutions) - { - if (outputResolution.Any(char.IsNumber)) - continue; - configurationOutputResolutionsHas = true; - if (!runToDoCollectionFirst) - break; - (filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne) = GetFilesCollectionThenCopyOrMove(ticks, fileSearchFilter, directorySearchFilter, options, outputResolution); - keyValuePairs = FilePath.GetKeyValuePairs(filePathsCollection); - splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, keyValuePairs); - break; - } - fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent); - fPhotoPrismSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultSingleton); - propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory); - if (filesCollectionCountIsOne) - { - if (filePathsCollection is null) - throw new NullReferenceException(nameof(filePathsCollection)); - string resultsGroupDirectory; - a2PeopleContentDirectory = null; - eDistanceContentDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultContent); - fPhotoPrismContentDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultContent); - fPhotoPrismSingletonDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultSingleton); - for (int i = 1; i < 10; i++) - { - resultsGroupDirectory = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, string.Empty, create: true); - _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(resultsGroupDirectory); - } - argZero = SaveUrlAndGetNewRootDirectory(filePathsCollection.First()); - _Configuration.PropertyConfiguration.ChangeRootDirectory(argZero); - (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); - propertyRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), create: false); - propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory); - } - if (configurationOutputResolutionsHas) - { - foreach (string outputResolution in _Configuration.OutputResolutions) - { - if (outputResolution.Any(char.IsNumber)) - continue; - (cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); - filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useCeilingAverage: true); - break; - } - } - if (filesCollectionRootDirectory is null || filePathsCollection is null) - throw new NullReferenceException(nameof(filePathsCollection)); - int count = filePathsCollection.Select(l => l.Count).Sum(); - message = $") Building Container(s) - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - _ProgressBar = new(count, message, options); - ReadOnlyCollection readOnlyContainers = Container.Models.Stateless.Methods.IContainer.GetContainers(this, _Configuration.PropertyConfiguration, _Faces.FileNameExtension, _Faces.HiddenFileNameExtension, eDistanceContentDirectory, filesCollectionRootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); - _ProgressBar.Dispose(); - mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); - DeleteContinueFiles(personContainers); - if (!runToDoCollectionFirst) - MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory, options); - FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, fPhotoPrismSingletonDirectory, count, metadata, readOnlyContainers, propertyLogic, mapLogic); - ReadOnlyCollection distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); - if (_Configuration.LookForAbandoned) - { - string dResultsFullGroupDirectory; - string d2ResultsFullGroupDirectory; - foreach (string outputResolution in _Configuration.OutputResolutions) - { - _ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), options); - (cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - mapLogic.LookForAbandoned(this, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); - _ProgressBar.Dispose(); - } - } - _Distance.Clear(); - ReadOnlyCollection distinctValidImageFaces = Map.Models.Stateless.Methods.IMapLogic.GetFaces(distinctValidImageItems); - ReadOnlyCollection distinctValidImageMappingCollection = GetMappings(_Configuration.PropertyConfiguration, eDistanceContentDirectory, readOnlyContainers, mapLogic, distinctItems: true); - if (runToDoCollectionFirst) - { - if (!Directory.Exists(eDistanceContentDirectory)) - _ = Directory.CreateDirectory(eDistanceContentDirectory); - string json = JsonSerializer.Serialize(distinctValidImageMappingCollection); - File.WriteAllText(Path.Combine(eDistanceContentDirectory, $"{ticks}.json"), json); - } - foreach (string outputResolution in _Configuration.OutputResolutions) - { - if (_PropertyRootExistedBefore) - break; - personKeyToIds = mapLogic.GetPersonKeyToIds(); - if (_Configuration.SavePropertyShortcutsForOutputResolutions.Contains(outputResolution)) - SavePropertyShortcutsForOutputResolutions(eDistanceContentDirectory, distinctValidImageItems); - if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && _Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) - mapLogic.SaveShortcutsForOutputResolutionsPreMapLogic(eDistanceContentDirectory, personKeyToIds, distinctValidImageMappingCollection); - if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && _Configuration.JLinks.Where(l => !string.IsNullOrEmpty(l)).Any() && _Configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions.Contains(outputResolution)) - mapLogic.SaveFilteredOriginalImagesFromJLinks(_Configuration.JLinks, personContainers, a2PeopleContentDirectory, personKeyToIds, distinctValidImageMappingCollection); - if (_ArgZeroIsConfigurationRootDirectory - && _Configuration.SaveResizedSubfiles - && outputResolution == _Configuration.OutputResolutions[0] - && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution) - && _Exceptions.Count == 0) - MapLogic(ticks, readOnlyContainers, fPhotoPrismContentDirectory, mapLogic, outputResolution, new(personKeyToIds), distinctValidImageFaces, distinctValidImageMappingCollection); - if (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && splatNineIdentifiers is not null && distinctValidImageMappingCollection.Count > 0) - _Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.ImmichOwnerId, _Configuration.ImmichRoot, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, splatNineIdentifiers, distinctValidImageMappingCollection); - if (_IsEnvironment.Development) - continue; - if (!_IsEnvironment.Development) - { - string dResultsFullGroupDirectory; - string c2ResultsFullGroupDirectory; - string d2ResultsFullGroupDirectory; - (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(aResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); - _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(bResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); - _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); - if (_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) - _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultCollection)); - } - } - } - - private bool GetRunToDoCollectionFirst(Models.Configuration configuration, long ticks) - { - bool result = configuration.SaveSortingWithoutPerson; - if (!result) - result = !IId.IsOffsetDeterministicHashCode(configuration.PropertyConfiguration); - if (!result) - { - string[] directories; - directories = Directory.GetDirectories(_Configuration.PropertyConfiguration.RootDirectory, "*", SearchOption.TopDirectoryOnly); - if (directories.Length == 0) - result = true; - else - { - string seasonDirectory; - DirectoryInfo directoryInfo; - DateTime dateTime = new(ticks); - string rootDirectory = _Configuration.PropertyConfiguration.RootDirectory; - string eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent); - (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); - FileSystemInfo fileSystemInfo = new DirectoryInfo(eDistanceContentDirectory); - string[] checkDirectories = - [ - Path.Combine(rootDirectory, "Ancestry"), - Path.Combine(rootDirectory, "Facebook"), - Path.Combine(rootDirectory, "LinkedIn") - ]; - foreach (string checkDirectory in checkDirectories) - { - if (checkDirectory == rootDirectory) - seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName}"); - else - seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}"); - if (!Directory.Exists(seasonDirectory)) - _ = Directory.CreateDirectory(seasonDirectory); - if (result) - continue; - directories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string directory in directories) - { - directoryInfo = new(directory); - if (directoryInfo.LastWriteTime > fileSystemInfo.LastWriteTime) - { - result = true; - break; - } - } - } - } - } - if (result) - result = true; - if (!result) - result = false; - return result; - } - - private string SaveUrlAndGetNewRootDirectory(ReadOnlyCollection filePaths) - { - string result; - if (filePaths.Count == 0) - throw new NotSupportedException(); - string? sourceDirectory = filePaths[0].DirectoryFullPath; - if (string.IsNullOrEmpty(sourceDirectory)) - throw new NotSupportedException(); - Uri uri; - string? line; - string fileName; - FilePath filePath; - Task task; - string relativePath; - FileHolder fileHolder; - string extensionLowered; - string sourceDirectoryFile; - HttpClient httpClient = new(); - bool isValidImageFormatExtension; - result = Path.GetDirectoryName(Path.GetDirectoryName(sourceDirectory)) ?? throw new NotSupportedException(); - int length = result.Length; - for (int y = 0; y < int.MaxValue; y++) - { - _Logger?.LogInformation("Enter fileNameToCollection url for fileNameToCollection image"); - line = _Console.ReadLine(); - if (string.IsNullOrEmpty(line)) - break; - uri = new(line); - if (uri.HostNameType != UriHostNameType.Dns) - continue; - task = httpClient.GetByteArrayAsync(uri); - fileName = Path.GetFileName(uri.LocalPath); - sourceDirectoryFile = Path.Combine(sourceDirectory, fileName); - File.WriteAllBytes(sourceDirectoryFile, task.Result); - extensionLowered = Path.GetExtension(uri.LocalPath); - relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(sourceDirectoryFile, length, forceExtensionToLower: true); - isValidImageFormatExtension = _Configuration.PropertyConfiguration.ValidImageFormatExtensions.Contains(extensionLowered); - fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(sourceDirectoryFile); - filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: null); - _ = Item.Get(filePath, fileHolder, relativePath, isValidImageFormatExtension); - // container.Items.Add(item); - } - _Logger?.LogInformation(". . ."); - return result; - } - - private void FullDoWork(string argZero, string propertyRoot, long ticks, string aResultsFullGroupDirectory, string fPhotoPrismSingletonDirectory, int count, B_Metadata metadata, ReadOnlyCollection readOnlyContainers, A_Property propertyLogic, MapLogic mapLogic) - { - int total; - int notMapped; - string message; - bool exceptions; - int totalSeconds; - int totalNotMapped = 0; - IDlibDotNet dlibDotNet = this; - bool outputResolutionHasNumber; - bool anyNullOrNoIsUniqueFileName; - string cResultsFullGroupDirectory; - string dResultsFullGroupDirectory; - string c2ResultsFullGroupDirectory; - string d2ResultsFullGroupDirectory; - Container.Models.Container container; - ReadOnlyCollection filteredItems; - List> sourceDirectoryChanges = []; - int maxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism; - Dictionary> fileNameToCollection = !Directory.Exists(fPhotoPrismSingletonDirectory) ? fileNameToCollection = [] : F_PhotoPrism.GetFileNameToCollection(fPhotoPrismSingletonDirectory); - string dResultsDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(D_Face)); - foreach (string outputResolution in _Configuration.OutputResolutions) - { - total = 0; - outputResolutionHasNumber = outputResolution.Any(char.IsNumber); - (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - _Faces.Update(dResultsFullGroupDirectory); - _Resize.Update(cResultsFullGroupDirectory); - _FaceParts.Update(d2ResultsFullGroupDirectory); - _BlurHasher.Update(c2ResultsFullGroupDirectory); - for (int i = 0; i < readOnlyContainers.Count; i++) - { - container = readOnlyContainers[i]; - if (container.Items.Count == 0) - continue; - if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) - continue; - filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, container); - if (filteredItems.Count == 0) - continue; - sourceDirectoryChanges.Clear(); - anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName); - totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - message = $"{totalSeconds} total second(s) - {outputResolution} - {i + 1:000} / {readOnlyContainers.Count:000} - {total} / {count} total - <{container.SourceDirectory}> [{filteredItems.Count:000}] - total not mapped {totalNotMapped:000000}"; - propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, container.SourceDirectory, anyNullOrNoIsUniqueFileName); - if (outputResolutionHasNumber) - _Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory); - (notMapped, exceptions) = FullParallelWork(maxDegreeOfParallelism, - propertyLogic, - metadata, - mapLogic, - outputResolution, - outputResolutionHasNumber, - cResultsFullGroupDirectory, - d2ResultsFullGroupDirectory, - sourceDirectoryChanges, - fileNameToCollection, - container, - filteredItems, - message); - totalNotMapped += notMapped; - if (exceptions) - { - _Exceptions.Add(container.SourceDirectory); - continue; - } - if (Directory.GetFiles(propertyRoot, "*.txt", SearchOption.TopDirectoryOnly).Length > 0) - { - for (int y = 0; y < int.MaxValue; y++) - { - _Logger?.LogInformation("Press \"Y\" key when ready to continue or close console"); - if (_Console.ReadKey() == ConsoleKey.Y) - break; - } - _Logger?.LogInformation(". . ."); - } - total += container.Items.Count; - } - totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - message = $"{totalSeconds} total second(s) - {outputResolution} - ### [###] / {readOnlyContainers.Count:000} - {total} / {count} total - <> - total not mapped {totalNotMapped:000000}"; - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(1, message, options); - progressBar.Tick(); - } - } - - private static ReadOnlyCollection HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(Property.Models.Configuration propertyConfiguration, ReadOnlyDictionary> keyValuePairs) - { - List results = []; - string checkFile; - FileInfo fileInfo; - FilePath? filePath; - string checkDirectory; - List collection = []; - foreach (KeyValuePair> keyValuePair in keyValuePairs) - { - collection.Clear(); - for (int i = 0; i < keyValuePair.Value.Count; i++) - { - filePath = keyValuePair.Value[i]; - if (!filePath.FullName.Contains(" !9")) - collection.Add(filePath); - else - { - fileInfo = new(filePath.FullName); - if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden)) - File.SetAttributes(fileInfo.FullName, FileAttributes.Hidden); - } - } - if (keyValuePair.Value.Count == 1 || collection.Count == keyValuePair.Value.Count) - continue; - foreach (FilePath fp in collection) - { - if (fp.HasDateTimeOriginal is null || !fp.HasDateTimeOriginal.Value) - break; - checkDirectory = Path.Combine(propertyConfiguration.RootDirectory, $"{new DateTime(fp.LastWriteTicks):yyyy MM} Already in Splat Nine"); - if (!Directory.Exists(checkDirectory)) - _ = Directory.CreateDirectory(checkDirectory); - checkFile = Path.Combine(checkDirectory, fp.Name); - if (File.Exists(checkFile)) - continue; - File.Move(fp.FullName, checkFile); - results.Add(fp); - } - } - return results.AsReadOnly(); - } - - private static Identifier GetIdentifier(Property.Models.Configuration propertyConfiguration, FilePath filePath, KeyValuePair> keyValuePair) - { - Identifier result; - if (filePath.Id is null) - throw new Exception(); - string[] directoryNames = keyValuePair.Value.Select(l => l.DirectoryFullPath.Replace('\\', '/')).ToArray(); - string paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, index: null); - result = new(directoryNames, filePath.ExtensionLowered, filePath.HasDateTimeOriginal, filePath.Id.Value, filePath.Length, paddedId, filePath.LastWriteTicks); - return result; - } - - private static ReadOnlyDictionary GetSplatNineIdentifiers(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> keyValuePairs) - { - Dictionary results = []; - string json; - FilePath? filePath; - Identifier identifier; - List identifiers = []; - string rootDirectory = propertyConfiguration.RootDirectory.Replace('\\', '/'); - string bMetadataCollectionDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultCollection); - if (!Directory.Exists(bMetadataCollectionDirectory)) - _ = Directory.CreateDirectory(bMetadataCollectionDirectory); - foreach (KeyValuePair> keyValuePair in keyValuePairs) - { - filePath = null; - for (int i = 0; i < keyValuePair.Value.Count; i++) - { - filePath = keyValuePair.Value[i]; - if (filePath.FullName.Contains(" !9")) - break; - filePath = null; - } - if (filePath is null) - { - filePath = keyValuePair.Value[0]; - if (filePath.Id is null) - continue; - identifier = GetIdentifier(propertyConfiguration, filePath, keyValuePair); - identifiers.Add(identifier); - continue; - } - else if (filePath.Id is null) - continue; - identifier = GetIdentifier(propertyConfiguration, filePath, keyValuePair); - identifiers.Add(identifier); - results.Add(keyValuePair.Key, identifier); - } - json = JsonSerializer.Serialize(results.Values.ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); - _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(bMetadataCollectionDirectory, "!9.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - json = JsonSerializer.Serialize((from l in identifiers orderby l.PaddedId select l).ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); - _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(bMetadataCollectionDirectory, ".json"), json.Replace(rootDirectory, string.Empty), updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - return results.AsReadOnly(); - } - - private static ReadOnlyDictionary GetSplatNineIdentifiersAndHideSplatNine(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> keyValuePairs) - { - ReadOnlyDictionary results; - if (keyValuePairs.Count == 0) - results = new(new Dictionary()); - else - { - ReadOnlyCollection moved = HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(propertyConfiguration, keyValuePairs); - if (moved.Count > 0) - throw new Exception($"House cleaning needed!{Environment.NewLine}{string.Join(Environment.NewLine, moved.Select(l => l.Id))}"); - results = GetSplatNineIdentifiers(propertyConfiguration, bResultsFullGroupDirectory, keyValuePairs); - } - return results; - } - - private ReadOnlyCollection GetMappings(Property.Models.Configuration propertyConfiguration, string eDistanceContentDirectory, ReadOnlyCollection readOnlyContainers, MapLogic mapLogic, bool distinctItems) - { - List results = []; - int count = 0; - int notMapped; - Mapping mapping; - bool anyValidFaces; - List distinct = []; - string focusRelativePath; - bool? isFocusRelativePath; - DateTime[] containerDateTimes; - MappingFromItem mappingFromItem; - ReadOnlyCollection filteredItems; - foreach (Container.Models.Container container in readOnlyContainers) - { - if (container.Items.Count == 0) - continue; - filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(propertyConfiguration, container); - if (filteredItems.Count == 0) - continue; - containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); - focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory)); - isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath); - foreach (Item item in filteredItems) - { - if (item.Property?.Id is null || item.ResizedFileHolder is null) - continue; - mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, item.ResizedFileHolder); - if (distinctItems) - { - if (distinct.Contains(item.Property.Id.Value)) - continue; - distinct.Add(item.Property.Id.Value); - } - count++; - anyValidFaces = false; - foreach (Shared.Models.Face face in item.Faces) - { - if (face.Mapping is null) - throw new NotSupportedException(); - if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) - continue; - anyValidFaces = true; - results.Add(face.Mapping); - } - if (!anyValidFaces) - { - (mapping, notMapped) = GetMappingAndUpdateMappingFromPerson(mapLogic, item, isFocusRelativePath, mappingFromItem); - results.Add(mapping); - } - } - } - if (_Configuration.MoveToDecade && _Configuration.LocationContainerDistanceTolerance is null) - _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(eDistanceContentDirectory); - return results.AsReadOnly(); - } - - private static void SavePropertyShortcutsForOutputResolutions(string eDistanceContentDirectory, ReadOnlyCollection distinctValidImageItems) - { -#if VerifyItem - bool found; - List notFound = new(); - foreach (Item item in distinctFilteredItems) - { - found = false; - if (item.Property?.Id is null) - continue; - foreach (Mapping mapping in distinctFilteredMappingCollection) - { - if (mapping.MappingFromItem.Id != item.Property.Id.Value) - continue; - found = true; - break; - } - if (!found) - notFound.Add(item); - } - if (notFound.Count > 0) - throw new NotSupportedException(); -#endif - string model; - string fileName; - string directory; - bool? isWrongYear; - List dateTimes; - List distinct = []; - WindowsShortcut windowsShortcut; - List<(string, string, string)> collection = []; - foreach (Item item in distinctValidImageItems) - { - if (item.Property?.Id is null) - continue; - if (item.IsNotUniqueAndNeedsReview is null || !item.IsNotUniqueAndNeedsReview.Value) - continue; - directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(item.IsNotUniqueAndNeedsReview)})", item.FilePath.NameWithoutExtension); - fileName = Path.Combine(directory, $"{item.FilePath.Length} {item.FilePath.LastWriteTicks}.lnk"); - collection.Add((item.FilePath.FullName, directory, fileName)); - if (distinct.Contains(directory)) - continue; - distinct.Add(directory); - } - foreach (Item item in distinctValidImageItems) - { - if (item.Property?.Id is null || item.Property.DateTimeOriginal is null) - continue; - dateTimes = item.Property.GetDateTimes(); - (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes); - if (isWrongYear is null || !isWrongYear.Value) - continue; - // Remove-Item -LiteralPath "\\?\D:\Tmp\a\EX-Z70 " - model = string.IsNullOrEmpty(item.Property.Model) ? "Unknown" : CameraRegex().Replace(item.Property.Model.Trim(), "_"); - directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Item)})", item.Property.DateTimeOriginal.Value.Year.ToString(), model); - fileName = item.IsNotUniqueAndNeedsReview is not null && item.IsNotUniqueAndNeedsReview.Value ? Path.Combine(directory, $"{item.FilePath.Name} {item.FilePath.Length}.lnk") : Path.Combine(directory, $"{item.FilePath.Name}.lnk"); - collection.Add((item.FilePath.FullName, directory, fileName)); - if (distinct.Contains(directory)) - continue; - distinct.Add(directory); - } -#if Mapping - foreach (Mapping mapping in distinctFilteredMappingCollection) - { - if (mapping.MappingFromItem.IsWrongYear is null || !mapping.MappingFromItem.IsWrongYear.Value) - continue; - directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Mapping)})", mapping.MappingFromItem.MinimumDateTime.Year.ToString()); - fileName = Path.Combine(directory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk"); - collection.Add((mapping.MappingFromItem.ResizedFileHolder.FullName, directory, fileName)); - if (distinct.Contains(directory)) - continue; - distinct.Add(directory); - } -#endif - foreach (string distinctDirectory in distinct) - { - if (!Directory.Exists(distinctDirectory)) - _ = Directory.CreateDirectory(distinctDirectory); - } - foreach ((string path, string checkDirectory, string checkFile) in collection) - { - if (File.Exists(checkFile)) - continue; - windowsShortcut = new() { Path = path }; - windowsShortcut.Save(checkFile); - windowsShortcut.Dispose(); - } - } - - private ReadOnlyCollection GetFilePath(long ticks, string dFacesContentDirectory) - { - List results = []; - FilePath filePath; - FileHolder fileHolder; - string[] files = Directory.GetFiles(dFacesContentDirectory, $"*{_Faces.FileNameExtension}", SearchOption.AllDirectories); - long? skipOlderThan = _Configuration.SkipOlderThanDays is null ? null : new DateTime(ticks).AddDays(-_Configuration.SkipOlderThanDays.Value).Ticks; - foreach (string file in files) - { - fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(file); - filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: null); - if (filePath.Id is null) - continue; - if (skipOlderThan is not null && (fileHolder.LastWriteTime is null || fileHolder.LastWriteTime.Value.Ticks < skipOlderThan.Value)) - continue; - results.Add(filePath); - } - return results.AsReadOnly(); - } - - public ReadOnlyDictionary GetOnlyOne(IDistanceLimits distanceLimits, ReadOnlyCollection matrix) - { - Dictionary results = []; - List added = []; - LocationContainer? tryGetValue; - foreach (LocationContainer locationContainer in matrix) - { - if (_Configuration.SaveIndividually) - break; - if (locationContainer.LengthSource is null) - continue; - if (_Configuration.UseExtraPersonKeyCheck) - { - if (results.TryGetValue(locationContainer.LengthSource.Name, out tryGetValue)) - { - if (locationContainer.PersonKey is not null && tryGetValue.PersonKey is not null && locationContainer.PersonKey.Value != tryGetValue.PersonKey) - _ = results.Remove(locationContainer.LengthSource.Name); - continue; - } - } - if (added.Contains(locationContainer.LengthSource.Name)) - continue; - added.Add(locationContainer.LengthSource.Name); - results.Add(locationContainer.LengthSource.Name, locationContainer); - } - return results.AsReadOnly(); - } - - private List GetSaveContainers(long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, ProgressBarOptions options, MapLogic mapLogic, string outputResolution) - { - List results; - IDlibDotNet dlibDotNet = this; - long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); - (string cResultsFullGroupDirectory, string _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); - ReadOnlyDictionary> mapped = Map.Models.Stateless.Methods.IMapLogic.GetMapped(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); - if (mapped.Count == 0 && !_Configuration.SaveSortingWithoutPerson) - throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!"); - ReadOnlyCollection filePaths = GetFilePath(ticks, dFacesContentDirectory); - List available = Map.Models.Stateless.Methods.IMapLogic.GetAvailable(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Faces, ticks, filePaths); - if (!string.IsNullOrEmpty(_Configuration.FocusDirectory) && _Configuration.FocusDirectory.Length != 2) - throw new NotSupportedException($"{nameof(_Configuration.FocusDirectory)} currently only works with output directory! Example 00."); - ReadOnlyDictionary> mappedWithEncoding = E_Distance.GetMappedWithEncoding(mapped); - if (mappedWithEncoding.Count == 0 && !_Configuration.SaveSortingWithoutPerson) - throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!"); - List preFiltered = E_Distance.GetPreFilterLocationContainer(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Configuration.FocusDirectory, _Configuration.FocusModel, _Configuration.SkipPersonWithMoreThen, ticks, mapLogic, jLinkResolvedPersonKeys, mapped, available); - if (preFiltered.Count == 0) - results = []; - else - { - DistanceLimits distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh); - List postFiltered = E_Distance.GetPostFilterLocationContainer(mapLogic, preFiltered, distanceLimits); - if (postFiltered.Count == 0) - results = []; - else - { - string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - _ProgressBar = new(postFiltered.Count, message, options); - ReadOnlyCollection matrix = E_Distance.GetMatrixLocationContainers(this, _MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered); - _ProgressBar.Dispose(); - ReadOnlyDictionary onlyOne = GetOnlyOne(distanceLimits, matrix); - if (onlyOne.Count == 0) - results = []; - else - { - ReadOnlyDictionary personKeyToPersonContainer = PersonContainer.Extract(personContainers); - results = mapLogic.GetSaveContainers(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, distanceLimits, onlyOne, personKeyToPersonContainer); - } - } - } - return results; - } - - private void MapLogic(long ticks, ReadOnlyCollection containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageFaces, ReadOnlyCollection distinctValidImageMappingCollection) - { - IDlibDotNet dlibDotNet = this; - (_, _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); - string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); - string d2FacePartsContentCollectionDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContentCollection); - if (distinctValidImageMappingCollection.Count > 0) - { - Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(d2FacePartsContentDirectory, ticks); - if (Directory.Exists(d2FacePartsContentCollectionDirectory)) - Shared.Models.Stateless.Methods.IPath.MakeHiddenIfAllItemsAreHidden(d2FacePartsContentCollectionDirectory); - } - if (Directory.Exists(fPhotoPrismContentDirectory)) - F_PhotoPrism.WriteMatches(fPhotoPrismContentDirectory, _Configuration.PersonBirthdayFormat, _Configuration.RectangleIntersectMinimums, ticks, distinctValidImageFaces, mapLogic); - if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) - mapLogic.SaveShortcutsForOutputResolutionsDuringMapLogic(containers, personKeyToIds, dFacesContentDirectory, distinctValidImageMappingCollection); - ReadOnlyDictionary> idToWholePercentagesToMapping = Map.Models.Stateless.Methods.IMapLogic.GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); - if (_Configuration.SaveMappedForOutputResolutions.Contains(outputResolution)) - mapLogic.SaveMapped(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, personKeyToIds, distinctValidImageMappingCollection, idToWholePercentagesToMapping); - if (_Configuration.SaveFaceDistancesForOutputResolutions.Contains(outputResolution)) - SaveFaceDistances(ticks, mapLogic, distinctValidImageFaces, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping); - } - - private bool? GetIsFocusModel(Shared.Models.Property? property) - { - bool? result; - if (string.IsNullOrEmpty(_Configuration.FocusModel)) - result = null; - else if (property is null || string.IsNullOrEmpty(property.Model)) - result = null; - else - result = property.Model.Contains(_Configuration.FocusModel); - return result; - } - - private void LogItemPropertyIsNull(Item item) - { - if (!item.SourceDirectoryFileHolder.Exists) - _Logger?.LogInformation(string.Concat("NoJson <", item.FilePath.FullName, '>')); - else if (item.FileSizeChanged.HasValue && item.FileSizeChanged.Value) - _Logger?.LogInformation(string.Concat("FileSizeChanged <", item.FilePath.FullName, '>')); - else if (item.LastWriteTimeChanged.HasValue && item.LastWriteTimeChanged.Value) - _Logger?.LogInformation(string.Concat("LastWriteTimeChanged <", item.FilePath.FullName, '>')); - else if (item.Moved.HasValue && item.Moved.Value) - _Logger?.LogInformation(string.Concat("Moved <", item.FilePath.FullName, '>')); - } - - private int GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(MapLogic mapLogic, Item item, bool? isFocusRelativePath, MappingFromItem mappingFromItem, List? mappingFromPhotoPrismCollection, List faces) - { - int result; - double? α; - int? eyeα; - bool? canReMap; - bool? eyeReview; - Mapping mapping; - int notMapped = 0; - bool? isFocusPerson; - int confidencePercent; - int faceAreaPermyriad; - bool? inSkipCollection; - int wholePercentRectangle; - string deterministicHashCodeKey; - MappingFromLocation? mappingFromLocation; - MappingFromFilterPre mappingFromFilterPre; - MappingFromFilterPost mappingFromFilterPost; - bool? isFocusModel = GetIsFocusModel(item.Property); - ReadOnlyDictionary>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.Property?.Id); - long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); - foreach (Shared.Models.Face face in faces) - { - if (item.Property?.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) - { - canReMap = null; - isFocusPerson = null; - inSkipCollection = null; - mappingFromLocation = null; - mappingFromFilterPost = new(canReMap, inSkipCollection, isFocusPerson); - mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath); - } - else - { - if (face.FaceParts is null) - (eyeα, eyeReview) = (null, null); - else - { - (eyeReview, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts); - eyeα = α is null ? null : (int)Math.Round(Math.Abs(α.Value)); - } - confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_Configuration.FaceConfidencePercent, face.Location.Confidence); - faceAreaPermyriad = IMapping.GetAreaPermyriad(_Configuration.FaceAreaPermyriad, face.Location, face.OutputResolution); - wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); - deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); - mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle); - inSkipCollection = mapLogic.InSkipCollection(item.Property.Id.Value, mappingFromLocation); - mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath); - canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); - isFocusPerson = mapLogic.IsFocusPerson(_Configuration.SkipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); - mappingFromFilterPost = new(canReMap, inSkipCollection, isFocusPerson); - } - mapping = Mapping.Get(item.FilePath, mappingFromFilterPost, mappingFromFilterPre, mappingFromItem, mappingFromLocation, mappingFromPhotoPrismCollection); - notMapped += mapLogic.UpdateMappingFromPerson(wholePercentagesToPersonContainers, mapping); - face.SetMapping(mapping); - } - result = notMapped; - return result; - } - - private static Map.Models.Configuration Get(Models.Configuration configuration, IDistanceLimits distanceLimits, string facesFileNameExtension, string facesHiddenFileNameExtension, string facePartsFileNameExtension) - { - Map.Models.Configuration result = new(distanceLimits, - configuration.PropertyConfiguration, - configuration.DeletePossibleDuplicates, - configuration.DistanceMoveUnableToMatch, - configuration.DistanceRenameToMatch, - configuration.FaceConfidencePercent, - configuration.FaceDistancePermyriad, - configuration.LinkedAlpha, - configuration.LocationContainerDebugDirectory, - configuration.LocationContainerDirectoryPattern, - configuration.LocationContainerDistanceGroupMinimum, - configuration.LocationContainerDistanceTake, - configuration.LocationContainerDistanceTolerance, - configuration.LocationDigits, - configuration.MappingDefaultName, - configuration.PersonBirthdayFirstYear, - configuration.PersonBirthdayFormat, - configuration.PersonCharacters.ToArray(), - configuration.RangeDaysDeltaTolerance, - configuration.RangeDistanceTolerance, - configuration.ReMap, - configuration.SaveIndividually, - configuration.SaveSortingWithoutPerson, - configuration.SkipNotSkipDirectories, - configuration.SortingMaximumPerKey, - configuration.SortingMinimumToUseSigma, - facesFileNameExtension, - facesHiddenFileNameExtension, - facePartsFileNameExtension); - return result; - } - - private (Mapping, int) GetMappingAndUpdateMappingFromPerson(MapLogic mapLogic, Item item, bool? isFocusRelativePath, MappingFromItem mappingFromItem) - { - Mapping result; - int? eyeα = null; - bool? eyeReview = null; - int confidencePercent = 0; - int faceAreaPermyriad = 0; - bool? isFocusModel = GetIsFocusModel(item.Property); - long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); - int wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(Shared.Models.Stateless.ILocation.Digits); - string deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, Shared.Models.Stateless.ILocation.Digits); - MappingFromLocation? mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle); - bool? inSkipCollection = mapLogic.InSkipCollection(mappingFromItem.Id, mappingFromLocation); - MappingFromFilterPre mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath); - ReadOnlyDictionary>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(mappingFromItem.Id); - bool? canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); - bool? isFocusPerson = mapLogic.IsFocusPerson(_Configuration.SkipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); - MappingFromFilterPost mappingFromFilterPost = new(canReMap, inSkipCollection, isFocusPerson); - result = Mapping.Get(item.FilePath, mappingFromFilterPost, mappingFromFilterPre, mappingFromItem, mappingFromLocation, mappingFromPhotoPrismCollection: null); - int notMapped = mapLogic.UpdateMappingFromPerson(wholePercentagesToPersonContainers, result); - return (result, notMapped); - } - private int FullParallelForWork(A_Property propertyLogic, B_Metadata metadata, MapLogic mapLogic, @@ -1357,6 +371,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return (result, exceptionsCount > 0); } + void IDlibDotNet.Tick() => + _ProgressBar?.Tick(); + + void IDisposable.Dispose() + { + _ProgressBar?.Dispose(); + GC.SuppressFinalize(this); + } + (string, string) IDlibDotNet.GetResultsFullGroupDirectories() { string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( @@ -1409,63 +432,97 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return new(cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); } - private void SaveFaceDistances(long ticks, MapLogic mapLogic, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping, ReadOnlyCollection faceDistanceEncodings, ReadOnlyCollection faceDistanceContainers) + public ReadOnlyDictionary GetOnlyOne(IDistanceLimits distanceLimits, ReadOnlyCollection matrix) { - int? useFiltersCounter = null; - DistanceLimits distanceLimits; - ReadOnlyCollection sortingContainers; - FaceDistanceContainer[] filteredFaceDistanceContainers; - long? skipOlderThan = _Configuration.SkipOlderThanDays is null ? null : new DateTime(ticks).AddDays(-_Configuration.SkipOlderThanDays.Value).Ticks; - distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh); - filteredFaceDistanceContainers = E_Distance.FilteredPostLoadFaceDistanceContainers(mapLogic, faceDistanceContainers, skipOlderThan, distanceLimits); - if (filteredFaceDistanceContainers.Length == 0) - _Logger?.LogInformation("All images have been filtered!"); - else + Dictionary results = []; + List added = []; + LocationContainer? tryGetValue; + foreach (LocationContainer locationContainer in matrix) { - sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortedSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers); - if (sortingContainers.Count == 0) + if (_Configuration.SaveIndividually) + break; + if (locationContainer.LengthSource is null) + continue; + if (_Configuration.UseExtraPersonKeyCheck) { - for (useFiltersCounter = 1; useFiltersCounter < _Configuration.UseFilterTries; useFiltersCounter++) + if (results.TryGetValue(locationContainer.LengthSource.Name, out tryGetValue)) { - distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh, useFiltersCounter); - filteredFaceDistanceContainers = E_Distance.FilteredPostLoadFaceDistanceContainers(mapLogic, faceDistanceContainers, skipOlderThan, distanceLimits); - if (filteredFaceDistanceContainers.Length == 0) - _Logger?.LogInformation("All images have been filtered!"); - else - { - sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortedSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers); - if (sortingContainers.Count == 0) - break; - } + if (locationContainer.PersonKey is not null && tryGetValue.PersonKey is not null && locationContainer.PersonKey.Value != tryGetValue.PersonKey) + _ = results.Remove(locationContainer.LengthSource.Name); + continue; } } - sortingContainers = mapLogic.GetFilterSortingContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, distanceLimits, sortingContainers); - if (sortingContainers.Count > 0) - E_Distance.SaveFaceDistances(_Configuration.PropertyConfiguration, sortingContainers); - if (filteredFaceDistanceContainers.Length > 0) - { - int updated = sortingContainers.Count == 0 ? 0 : mapLogic.UpdateFromSortingContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, sortingContainers); - List saveContainers = mapLogic.GetSaveContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, distanceLimits, useFiltersCounter, sortingContainers); - if (saveContainers.Count > 0) - mapLogic.SaveContainers(updated, saveContainers); - } + if (added.Contains(locationContainer.LengthSource.Name)) + continue; + added.Add(locationContainer.LengthSource.Name); + results.Add(locationContainer.LengthSource.Name, locationContainer); } + return results.AsReadOnly(); } - private void SaveFaceDistances(long ticks, MapLogic mapLogic, ReadOnlyCollection distinctValidImageFaces, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping) + private static Identifier GetIdentifier(Property.Models.Configuration propertyConfiguration, FilePath filePath, KeyValuePair> keyValuePair) { - E_Distance.PreFilterSetFaceDistances(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, distinctValidImageFaces); - ReadOnlyCollection faceDistanceContainers = E_Distance.GetFaceDistanceContainers(distinctValidImageFaces); - if (faceDistanceContainers.Count > 0) + Identifier result; + if (filePath.Id is null) + throw new Exception(); + string[] directoryNames = keyValuePair.Value.Select(l => l.DirectoryFullPath.Replace('\\', '/')).ToArray(); + string paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, index: null); + result = new(directoryNames, filePath.ExtensionLowered, filePath.HasDateTimeOriginal, filePath.Id.Value, filePath.Length, paddedId, filePath.LastWriteTicks); + return result; + } + + [GeneratedRegex(@"[\\,\/,\:,\*,\?,\"",\<,\>,\|]")] + private static partial Regex CameraRegex(); + + private static void Verify(Models.Configuration configuration) + { + if (configuration.RangeDaysDeltaTolerance.Length != 3) + throw new NullReferenceException(nameof(configuration.RangeDaysDeltaTolerance)); + if (configuration.RangeDistanceTolerance.Length != 3) + throw new NullReferenceException(nameof(configuration.RangeDistanceTolerance)); + if (configuration.RangeFaceAreaTolerance.Length != 3) + throw new NullReferenceException(nameof(configuration.RangeFaceAreaTolerance)); + if (configuration.RangeFaceConfidence.Length != 3) + throw new NullReferenceException(nameof(configuration.RangeFaceConfidence)); + if (configuration.LocationContainerDistanceTolerance is null && !string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) + throw new NullReferenceException($"{nameof(configuration.LocationContainerDistanceTolerance)} must have a value when {nameof(configuration.LocationContainerDebugDirectory)} is set!"); + _ = DateTime.Now.AddDays(-configuration.RangeDaysDeltaTolerance[1]); + if (configuration.OutputResolutions.Length == 0 || string.IsNullOrEmpty(configuration.OutputResolutions[0]) || !configuration.ValidResolutions.Contains(configuration.OutputResolutions[0])) + throw new NullReferenceException($"{nameof(configuration.OutputResolutions)} must be fileNameToCollection valid outputResolution!"); + if ((from l in configuration.OutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) + throw new Exception($"One or more {nameof(configuration.OutputResolutions)} are not in the ValidResolutions list!"); + if ((from l in configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) + throw new Exception($"One or more {nameof(configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions)} are not in the ValidResolutions list!"); + if ((from l in configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) + throw new Exception($"One or more {nameof(configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions)} are not in the ValidResolutions list!"); + if ((from l in configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) + throw new Exception($"One or more {nameof(configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions)} are not in the ValidResolutions list!"); + if ((from l in configuration.SaveShortcutsForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) + throw new Exception($"One or more {nameof(configuration.SaveShortcutsForOutputResolutions)} are not in the ValidResolutions list!"); + if ((from l in configuration.SaveFaceLandmarkForOutputResolutions where !configuration.ValidResolutions.Contains(l) select false).Any()) + throw new Exception($"One or more {nameof(configuration.SaveFaceLandmarkForOutputResolutions)} are not in the ValidResolutions list!"); + if ((from l in configuration.SaveFaceLandmarkForOutputResolutionsV2 where !configuration.ValidResolutions.Contains(l) select false).Any()) + throw new Exception($"One or more {nameof(configuration.SaveFaceLandmarkForOutputResolutionsV2)} are not in the ValidResolutions list!"); + if (string.IsNullOrEmpty(configuration.ModelName)) + throw new NullReferenceException(nameof(configuration.ModelName)); + if (string.IsNullOrEmpty(configuration.OutputExtension)) + throw new NullReferenceException(nameof(configuration.OutputExtension)); + if (string.IsNullOrEmpty(configuration.PredictorModelName)) + throw new NullReferenceException(nameof(configuration.PredictorModelName)); + if (string.IsNullOrEmpty(configuration.ModelDirectory) || !Directory.Exists(configuration.ModelDirectory)) + throw new NullReferenceException(nameof(configuration.ModelDirectory)); + } + + private static void DeleteContinueFiles(ReadOnlyCollection personContainers) + { + foreach (PersonContainer personContainer in personContainers) { - List faceDistanceEncodings = []; - foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers) + foreach (FilePath filePath in personContainer.DisplayDirectoryAllFilePaths) { - if (faceDistanceContainer.FaceDistance.Encoding is null) + if (filePath.ExtensionLowered != ".continue") continue; - faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance); + File.Delete(filePath.FullName); } - SaveFaceDistances(ticks, mapLogic, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, new(faceDistanceEncodings), faceDistanceContainers); } } @@ -1517,6 +574,528 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return result; } + private static void SavePropertyShortcutsForOutputResolutions(string eDistanceContentDirectory, ReadOnlyCollection distinctValidImageItems) + { +#if VerifyItem + bool found; + List notFound = new(); + foreach (Item item in distinctFilteredItems) + { + found = false; + if (item.Property?.Id is null) + continue; + foreach (Mapping mapping in distinctFilteredMappingCollection) + { + if (mapping.MappingFromItem.Id != item.Property.Id.Value) + continue; + found = true; + break; + } + if (!found) + notFound.Add(item); + } + if (notFound.Count > 0) + throw new NotSupportedException(); +#endif + string model; + string fileName; + string directory; + bool? isWrongYear; + List dateTimes; + List distinct = []; + WindowsShortcut windowsShortcut; + List<(string, string, string)> collection = []; + foreach (Item item in distinctValidImageItems) + { + if (item.Property?.Id is null) + continue; + if (item.IsNotUniqueAndNeedsReview is null || !item.IsNotUniqueAndNeedsReview.Value) + continue; + directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(item.IsNotUniqueAndNeedsReview)})", item.FilePath.NameWithoutExtension); + fileName = Path.Combine(directory, $"{item.FilePath.Length} {item.FilePath.LastWriteTicks}.lnk"); + collection.Add((item.FilePath.FullName, directory, fileName)); + if (distinct.Contains(directory)) + continue; + distinct.Add(directory); + } + foreach (Item item in distinctValidImageItems) + { + if (item.Property?.Id is null || item.Property.DateTimeOriginal is null) + continue; + dateTimes = item.Property.GetDateTimes(); + (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes); + if (isWrongYear is null || !isWrongYear.Value) + continue; + // Remove-Item -LiteralPath "\\?\D:\Tmp\a\EX-Z70 " + model = string.IsNullOrEmpty(item.Property.Model) ? "Unknown" : CameraRegex().Replace(item.Property.Model.Trim(), "_"); + directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Item)})", item.Property.DateTimeOriginal.Value.Year.ToString(), model); + fileName = item.IsNotUniqueAndNeedsReview is not null && item.IsNotUniqueAndNeedsReview.Value ? Path.Combine(directory, $"{item.FilePath.Name} {item.FilePath.Length}.lnk") : Path.Combine(directory, $"{item.FilePath.Name}.lnk"); + collection.Add((item.FilePath.FullName, directory, fileName)); + if (distinct.Contains(directory)) + continue; + distinct.Add(directory); + } +#if Mapping + foreach (Mapping mapping in distinctFilteredMappingCollection) + { + if (mapping.MappingFromItem.IsWrongYear is null || !mapping.MappingFromItem.IsWrongYear.Value) + continue; + directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Mapping)})", mapping.MappingFromItem.MinimumDateTime.Year.ToString()); + fileName = Path.Combine(directory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk"); + collection.Add((mapping.MappingFromItem.ResizedFileHolder.FullName, directory, fileName)); + if (distinct.Contains(directory)) + continue; + distinct.Add(directory); + } +#endif + foreach (string distinctDirectory in distinct) + { + if (!Directory.Exists(distinctDirectory)) + _ = Directory.CreateDirectory(distinctDirectory); + } + foreach ((string path, string checkDirectory, string checkFile) in collection) + { + if (File.Exists(checkFile)) + continue; + windowsShortcut = new() { Path = path }; + windowsShortcut.Save(checkFile); + windowsShortcut.Dispose(); + } + } + + private static Map.Models.Configuration Get(Models.Configuration configuration, IDistanceLimits distanceLimits, string facesFileNameExtension, string facesHiddenFileNameExtension, string facePartsFileNameExtension) + { + Map.Models.Configuration result = new(distanceLimits, + configuration.PropertyConfiguration, + configuration.DeletePossibleDuplicates, + configuration.DistanceMoveUnableToMatch, + configuration.DistanceRenameToMatch, + configuration.FaceConfidencePercent, + configuration.FaceDistancePermyriad, + configuration.LinkedAlpha, + configuration.LocationContainerDebugDirectory, + configuration.LocationContainerDirectoryPattern, + configuration.LocationContainerDistanceGroupMinimum, + configuration.LocationContainerDistanceTake, + configuration.LocationContainerDistanceTolerance, + configuration.LocationDigits, + configuration.MappingDefaultName, + configuration.PersonBirthdayFirstYear, + configuration.PersonBirthdayFormat, + configuration.PersonCharacters.ToArray(), + configuration.RangeDaysDeltaTolerance, + configuration.RangeDistanceTolerance, + configuration.ReMap, + configuration.SaveIndividually, + configuration.SaveSortingWithoutPerson, + configuration.SkipNotSkipDirectories, + configuration.SortingMaximumPerKey, + configuration.SortingMinimumToUseSigma, + facesFileNameExtension, + facesHiddenFileNameExtension, + facePartsFileNameExtension); + return result; + } + + private static ReadOnlyCollection HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(Property.Models.Configuration propertyConfiguration, ReadOnlyDictionary> keyValuePairs) + { + List results = []; + string checkFile; + FileInfo fileInfo; + FilePath? filePath; + string checkDirectory; + List collection = []; + foreach (KeyValuePair> keyValuePair in keyValuePairs) + { + collection.Clear(); + for (int i = 0; i < keyValuePair.Value.Count; i++) + { + filePath = keyValuePair.Value[i]; + if (!filePath.FullName.Contains(" !9")) + collection.Add(filePath); + else + { + fileInfo = new(filePath.FullName); + if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden)) + File.SetAttributes(fileInfo.FullName, FileAttributes.Hidden); + } + } + if (keyValuePair.Value.Count == 1 || collection.Count == keyValuePair.Value.Count) + continue; + foreach (FilePath fp in collection) + { + if (fp.HasDateTimeOriginal is null || !fp.HasDateTimeOriginal.Value) + break; + checkDirectory = Path.Combine(propertyConfiguration.RootDirectory, $"{new DateTime(fp.LastWriteTicks):yyyy MM} Already in Splat Nine"); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + checkFile = Path.Combine(checkDirectory, fp.Name); + if (File.Exists(checkFile)) + continue; + File.Move(fp.FullName, checkFile); + results.Add(fp); + } + } + return results.AsReadOnly(); + } + + private static ReadOnlyDictionary GetSplatNineIdentifiers(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> keyValuePairs) + { + Dictionary results = []; + string json; + FilePath? filePath; + Identifier identifier; + List identifiers = []; + string rootDirectory = propertyConfiguration.RootDirectory.Replace('\\', '/'); + string bMetadataCollectionDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultCollection); + if (!Directory.Exists(bMetadataCollectionDirectory)) + _ = Directory.CreateDirectory(bMetadataCollectionDirectory); + foreach (KeyValuePair> keyValuePair in keyValuePairs) + { + filePath = null; + for (int i = 0; i < keyValuePair.Value.Count; i++) + { + filePath = keyValuePair.Value[i]; + if (filePath.FullName.Contains(" !9")) + break; + filePath = null; + } + if (filePath is null) + { + filePath = keyValuePair.Value[0]; + if (filePath.Id is null) + continue; + identifier = GetIdentifier(propertyConfiguration, filePath, keyValuePair); + identifiers.Add(identifier); + continue; + } + else if (filePath.Id is null) + continue; + identifier = GetIdentifier(propertyConfiguration, filePath, keyValuePair); + identifiers.Add(identifier); + results.Add(keyValuePair.Key, identifier); + } + json = JsonSerializer.Serialize(results.Values.ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(bMetadataCollectionDirectory, "!9.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + json = JsonSerializer.Serialize((from l in identifiers orderby l.PaddedId select l).ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(bMetadataCollectionDirectory, ".json"), json.Replace(rootDirectory, string.Empty), updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + return results.AsReadOnly(); + } + + private static ReadOnlyDictionary GetSplatNineIdentifiersAndHideSplatNine(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> keyValuePairs) + { + ReadOnlyDictionary results; + if (keyValuePairs.Count == 0) + results = new(new Dictionary()); + else + { + ReadOnlyCollection moved = HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(propertyConfiguration, keyValuePairs); + if (moved.Count > 0) + throw new Exception($"House cleaning needed!{Environment.NewLine}{string.Join(Environment.NewLine, moved.Select(l => l.Id))}"); + results = GetSplatNineIdentifiers(propertyConfiguration, bResultsFullGroupDirectory, keyValuePairs); + } + return results; + } + + private bool? GetIsFocusModel(Shared.Models.Property? property) + { + bool? result; + if (string.IsNullOrEmpty(_Configuration.FocusModel)) + result = null; + else if (property is null || string.IsNullOrEmpty(property.Model)) + result = null; + else + result = property.Model.Contains(_Configuration.FocusModel); + return result; + } + + private void LogItemPropertyIsNull(Item item) + { + if (!item.SourceDirectoryFileHolder.Exists) + _Logger?.LogInformation(string.Concat("NoJson <", item.FilePath.FullName, '>')); + else if (item.FileSizeChanged.HasValue && item.FileSizeChanged.Value) + _Logger?.LogInformation(string.Concat("FileSizeChanged <", item.FilePath.FullName, '>')); + else if (item.LastWriteTimeChanged.HasValue && item.LastWriteTimeChanged.Value) + _Logger?.LogInformation(string.Concat("LastWriteTimeChanged <", item.FilePath.FullName, '>')); + else if (item.Moved.HasValue && item.Moved.Value) + _Logger?.LogInformation(string.Concat("Moved <", item.FilePath.FullName, '>')); + } + + private string SaveUrlAndGetNewRootDirectory(ReadOnlyCollection filePaths) + { + string result; + if (filePaths.Count == 0) + throw new NotSupportedException(); + string? sourceDirectory = filePaths[0].DirectoryFullPath; + if (string.IsNullOrEmpty(sourceDirectory)) + throw new NotSupportedException(); + Uri uri; + string? line; + string fileName; + FilePath filePath; + Task task; + string relativePath; + FileHolder fileHolder; + string extensionLowered; + string sourceDirectoryFile; + HttpClient httpClient = new(); + bool isValidImageFormatExtension; + result = Path.GetDirectoryName(Path.GetDirectoryName(sourceDirectory)) ?? throw new NotSupportedException(); + int length = result.Length; + for (int y = 0; y < int.MaxValue; y++) + { + _Logger?.LogInformation("Enter fileNameToCollection url for fileNameToCollection image"); + line = _Console.ReadLine(); + if (string.IsNullOrEmpty(line)) + break; + uri = new(line); + if (uri.HostNameType != UriHostNameType.Dns) + continue; + task = httpClient.GetByteArrayAsync(uri); + fileName = Path.GetFileName(uri.LocalPath); + sourceDirectoryFile = Path.Combine(sourceDirectory, fileName); + File.WriteAllBytes(sourceDirectoryFile, task.Result); + extensionLowered = Path.GetExtension(uri.LocalPath); + relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(sourceDirectoryFile, length, forceExtensionToLower: true); + isValidImageFormatExtension = _Configuration.PropertyConfiguration.ValidImageFormatExtensions.Contains(extensionLowered); + fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(sourceDirectoryFile); + filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: null); + _ = Item.Get(filePath, fileHolder, relativePath, isValidImageFormatExtension); + // container.Items.Add(item); + } + _Logger?.LogInformation(". . ."); + return result; + } + + private bool GetRunToDoCollectionFirst(Models.Configuration configuration, long ticks) + { + bool result = configuration.SaveSortingWithoutPerson; + if (!result) + result = !IId.IsOffsetDeterministicHashCode(configuration.PropertyConfiguration); + if (!result) + { + string[] directories; + directories = Directory.GetDirectories(_Configuration.PropertyConfiguration.RootDirectory, "*", SearchOption.TopDirectoryOnly); + if (directories.Length == 0) + result = true; + else + { + string seasonDirectory; + DirectoryInfo directoryInfo; + DateTime dateTime = new(ticks); + string rootDirectory = _Configuration.PropertyConfiguration.RootDirectory; + string eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent); + (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); + FileSystemInfo fileSystemInfo = new DirectoryInfo(eDistanceContentDirectory); + string[] checkDirectories = + [ + Path.Combine(rootDirectory, "Ancestry"), + Path.Combine(rootDirectory, "Facebook"), + Path.Combine(rootDirectory, "LinkedIn") + ]; + foreach (string checkDirectory in checkDirectories) + { + if (checkDirectory == rootDirectory) + seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName}"); + else + seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}"); + if (!Directory.Exists(seasonDirectory)) + _ = Directory.CreateDirectory(seasonDirectory); + if (result) + continue; + directories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) + { + directoryInfo = new(directory); + if (directoryInfo.LastWriteTime > fileSystemInfo.LastWriteTime) + { + result = true; + break; + } + } + } + } + } + if (result) + result = true; + if (!result) + result = false; + return result; + } + + private ReadOnlyCollection GetFilePath(long ticks, string dFacesContentDirectory) + { + List results = []; + FilePath filePath; + FileHolder fileHolder; + string[] files = Directory.GetFiles(dFacesContentDirectory, $"*{_Faces.FileNameExtension}", SearchOption.AllDirectories); + long? skipOlderThan = _Configuration.SkipOlderThanDays is null ? null : new DateTime(ticks).AddDays(-_Configuration.SkipOlderThanDays.Value).Ticks; + foreach (string file in files) + { + fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(file); + filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: null); + if (filePath.Id is null) + continue; + if (skipOlderThan is not null && (fileHolder.LastWriteTime is null || fileHolder.LastWriteTime.Value.Ticks < skipOlderThan.Value)) + continue; + results.Add(filePath); + } + return results.AsReadOnly(); + } + + private void Search(long ticks, ReadOnlyCollection personContainers, string argZero, string propertyRoot) + { + string message; + MapLogic? mapLogic; + A_Property propertyLogic; + IDlibDotNet dlibDotNet = this; + string eDistanceContentDirectory; + string? a2PeopleContentDirectory; + string aResultsFullGroupDirectory; + string bResultsFullGroupDirectory; + string cResultsFullGroupDirectory; + string fPhotoPrismContentDirectory; + const string fileSearchFilter = "*"; + string fPhotoPrismSingletonDirectory; + bool filesCollectionCountIsOne = false; + const string directorySearchFilter = "*"; + string? filesCollectionRootDirectory = null; + bool configurationOutputResolutionsHas = false; + ReadOnlyDictionary> personKeyToIds; + ReadOnlyDictionary? splatNineIdentifiers = null; + ReadOnlyDictionary>? keyValuePairs = null; + ReadOnlyCollection>? filePathsCollection = null; + bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks); + (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(_Configuration.PropertyConfiguration.RootDirectory, ticks); + a2PeopleContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), "([])"); + B_Metadata metadata = new(_Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory); + eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent); + string a2PeopleSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), _Configuration.PropertyConfiguration.ResultSingleton); + _ = Directory.CreateDirectory(Path.Combine(eDistanceContentDirectory, ticks.ToString())); + if (runToDoCollectionFirst) + mapLogic = null; + else + mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); + foreach (string outputResolution in _Configuration.OutputResolutions) + { + if (outputResolution.Any(char.IsNumber)) + continue; + configurationOutputResolutionsHas = true; + if (!runToDoCollectionFirst) + break; + (filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne) = GetFilesCollectionThenCopyOrMove(ticks, fileSearchFilter, directorySearchFilter, options, outputResolution); + keyValuePairs = FilePath.GetKeyValuePairs(filePathsCollection); + splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, keyValuePairs); + break; + } + fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent); + fPhotoPrismSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultSingleton); + propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory); + if (filesCollectionCountIsOne) + { + if (filePathsCollection is null) + throw new NullReferenceException(nameof(filePathsCollection)); + string resultsGroupDirectory; + a2PeopleContentDirectory = null; + eDistanceContentDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultContent); + fPhotoPrismContentDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultContent); + fPhotoPrismSingletonDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultSingleton); + for (int i = 1; i < 10; i++) + { + resultsGroupDirectory = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, string.Empty, create: true); + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(resultsGroupDirectory); + } + argZero = SaveUrlAndGetNewRootDirectory(filePathsCollection.First()); + _Configuration.PropertyConfiguration.ChangeRootDirectory(argZero); + (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); + propertyRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), create: false); + propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory); + } + if (configurationOutputResolutionsHas) + { + foreach (string outputResolution in _Configuration.OutputResolutions) + { + if (outputResolution.Any(char.IsNumber)) + continue; + (cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); + filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); + filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useCeilingAverage: true); + break; + } + } + if (filesCollectionRootDirectory is null || filePathsCollection is null) + throw new NullReferenceException(nameof(filePathsCollection)); + int count = filePathsCollection.Select(l => l.Count).Sum(); + message = $") Building Container(s) - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; + _ProgressBar = new(count, message, options); + ReadOnlyCollection readOnlyContainers = Container.Models.Stateless.Methods.IContainer.GetContainers(this, _Configuration.PropertyConfiguration, _Faces.FileNameExtension, _Faces.HiddenFileNameExtension, eDistanceContentDirectory, filesCollectionRootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection); + _ProgressBar.Dispose(); + mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); + DeleteContinueFiles(personContainers); + if (!runToDoCollectionFirst) + MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory, options); + FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, fPhotoPrismSingletonDirectory, count, metadata, readOnlyContainers, propertyLogic, mapLogic); + ReadOnlyCollection distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); + if (_Configuration.LookForAbandoned) + { + string dResultsFullGroupDirectory; + string d2ResultsFullGroupDirectory; + foreach (string outputResolution in _Configuration.OutputResolutions) + { + _ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), options); + (cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); + mapLogic.LookForAbandoned(this, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); + _ProgressBar.Dispose(); + } + } + _Distance.Clear(); + ReadOnlyCollection distinctValidImageFaces = Map.Models.Stateless.Methods.IMapLogic.GetFaces(distinctValidImageItems); + ReadOnlyCollection distinctValidImageMappingCollection = GetMappings(_Configuration.PropertyConfiguration, eDistanceContentDirectory, readOnlyContainers, mapLogic, distinctItems: true); + if (runToDoCollectionFirst) + { + if (!Directory.Exists(eDistanceContentDirectory)) + _ = Directory.CreateDirectory(eDistanceContentDirectory); + string json = JsonSerializer.Serialize(distinctValidImageMappingCollection); + File.WriteAllText(Path.Combine(eDistanceContentDirectory, $"{ticks}.json"), json); + } + foreach (string outputResolution in _Configuration.OutputResolutions) + { + if (_PropertyRootExistedBefore) + break; + personKeyToIds = mapLogic.GetPersonKeyToIds(); + if (_Configuration.SavePropertyShortcutsForOutputResolutions.Contains(outputResolution)) + SavePropertyShortcutsForOutputResolutions(eDistanceContentDirectory, distinctValidImageItems); + if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && _Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) + mapLogic.SaveShortcutsForOutputResolutionsPreMapLogic(eDistanceContentDirectory, personKeyToIds, distinctValidImageMappingCollection); + if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && _Configuration.JLinks.Where(l => !string.IsNullOrEmpty(l)).Any() && _Configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions.Contains(outputResolution)) + mapLogic.SaveFilteredOriginalImagesFromJLinks(_Configuration.JLinks, personContainers, a2PeopleContentDirectory, personKeyToIds, distinctValidImageMappingCollection); + if (_ArgZeroIsConfigurationRootDirectory + && _Configuration.SaveResizedSubfiles + && outputResolution == _Configuration.OutputResolutions[0] + && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution) + && _Exceptions.Count == 0) + MapLogic(ticks, readOnlyContainers, fPhotoPrismContentDirectory, mapLogic, outputResolution, new(personKeyToIds), distinctValidImageFaces, distinctValidImageMappingCollection); + if (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && splatNineIdentifiers is not null && distinctValidImageMappingCollection.Count > 0) + _Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.ImmichOwnerId, _Configuration.ImmichRoot, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, splatNineIdentifiers, distinctValidImageMappingCollection); + if (_IsEnvironment.Development) + continue; + if (!_IsEnvironment.Development) + { + string dResultsFullGroupDirectory; + string c2ResultsFullGroupDirectory; + string d2ResultsFullGroupDirectory; + (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(aResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(bResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton)); + if (_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultCollection)); + } + } + } + private (string, ReadOnlyCollection>, bool) GetFilesCollectionThenCopyOrMove(long ticks, string fileSearchFilter, string directorySearchFilter, ProgressBarOptions options, string outputResolution) { ProgressBar progressBar; @@ -1543,4 +1122,425 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return (filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne); } + private void MapFaceFileLogic(long ticks, ReadOnlyCollection personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory, ProgressBarOptions options) + { + foreach (string outputResolution in _Configuration.OutputResolutions) + { + if (_Exceptions.Count != 0) + break; + if (!_Configuration.SaveResizedSubfiles) + continue; + if (!_ArgZeroIsConfigurationRootDirectory) + continue; + if (outputResolution != _Configuration.OutputResolutions[0]) + continue; + if (_PropertyRootExistedBefore || a2PeopleContentDirectory is null) + break; + if (!_Configuration.SaveFaceDistancesForOutputResolutions.Contains(outputResolution)) + continue; + if (!_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) + continue; + List saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, options, mapLogic, outputResolution); + if (saveContainers.Count > 0) + { + int updated = 0; + mapLogic.SaveContainers(updated, saveContainers); + throw new NotSupportedException("Done Restart! :)"); + } + } + } + + private List GetSaveContainers(long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, ProgressBarOptions options, MapLogic mapLogic, string outputResolution) + { + List results; + IDlibDotNet dlibDotNet = this; + long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); + (string cResultsFullGroupDirectory, string _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); + string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); + ReadOnlyDictionary> mapped = Map.Models.Stateless.Methods.IMapLogic.GetMapped(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); + if (mapped.Count == 0 && !_Configuration.SaveSortingWithoutPerson) + throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!"); + ReadOnlyCollection filePaths = GetFilePath(ticks, dFacesContentDirectory); + List available = Map.Models.Stateless.Methods.IMapLogic.GetAvailable(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Faces, ticks, filePaths); + if (!string.IsNullOrEmpty(_Configuration.FocusDirectory) && _Configuration.FocusDirectory.Length != 2) + throw new NotSupportedException($"{nameof(_Configuration.FocusDirectory)} currently only works with output directory! Example 00."); + ReadOnlyDictionary> mappedWithEncoding = E_Distance.GetMappedWithEncoding(mapped); + if (mappedWithEncoding.Count == 0 && !_Configuration.SaveSortingWithoutPerson) + throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!"); + List preFiltered = E_Distance.GetPreFilterLocationContainer(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Configuration.FocusDirectory, _Configuration.FocusModel, _Configuration.SkipPersonWithMoreThen, ticks, mapLogic, jLinkResolvedPersonKeys, mapped, available); + if (preFiltered.Count == 0) + results = []; + else + { + DistanceLimits distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh); + List postFiltered = E_Distance.GetPostFilterLocationContainer(mapLogic, preFiltered, distanceLimits); + if (postFiltered.Count == 0) + results = []; + else + { + string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; + _ProgressBar = new(postFiltered.Count, message, options); + ReadOnlyCollection matrix = E_Distance.GetMatrixLocationContainers(this, _MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered); + _ProgressBar.Dispose(); + ReadOnlyDictionary onlyOne = GetOnlyOne(distanceLimits, matrix); + if (onlyOne.Count == 0) + results = []; + else + { + ReadOnlyDictionary personKeyToPersonContainer = PersonContainer.Extract(personContainers); + results = mapLogic.GetSaveContainers(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, distanceLimits, onlyOne, personKeyToPersonContainer); + } + } + } + return results; + } + + private void FullDoWork(string argZero, string propertyRoot, long ticks, string aResultsFullGroupDirectory, string fPhotoPrismSingletonDirectory, int count, B_Metadata metadata, ReadOnlyCollection readOnlyContainers, A_Property propertyLogic, MapLogic mapLogic) + { + int total; + int notMapped; + string message; + bool exceptions; + int totalSeconds; + int totalNotMapped = 0; + IDlibDotNet dlibDotNet = this; + bool outputResolutionHasNumber; + bool anyNullOrNoIsUniqueFileName; + string cResultsFullGroupDirectory; + string dResultsFullGroupDirectory; + string c2ResultsFullGroupDirectory; + string d2ResultsFullGroupDirectory; + Container.Models.Container container; + ReadOnlyCollection filteredItems; + List> sourceDirectoryChanges = []; + int maxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism; + Dictionary> fileNameToCollection = !Directory.Exists(fPhotoPrismSingletonDirectory) ? fileNameToCollection = [] : F_PhotoPrism.GetFileNameToCollection(fPhotoPrismSingletonDirectory); + string dResultsDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(D_Face)); + foreach (string outputResolution in _Configuration.OutputResolutions) + { + total = 0; + outputResolutionHasNumber = outputResolution.Any(char.IsNumber); + (cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); + _Faces.Update(dResultsFullGroupDirectory); + _Resize.Update(cResultsFullGroupDirectory); + _FaceParts.Update(d2ResultsFullGroupDirectory); + _BlurHasher.Update(c2ResultsFullGroupDirectory); + for (int i = 0; i < readOnlyContainers.Count; i++) + { + container = readOnlyContainers[i]; + if (container.Items.Count == 0) + continue; + if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) + continue; + filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, container); + if (filteredItems.Count == 0) + continue; + sourceDirectoryChanges.Clear(); + anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName); + totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + message = $"{totalSeconds} total second(s) - {outputResolution} - {i + 1:000} / {readOnlyContainers.Count:000} - {total} / {count} total - <{container.SourceDirectory}> [{filteredItems.Count:000}] - total not mapped {totalNotMapped:000000}"; + propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, container.SourceDirectory, anyNullOrNoIsUniqueFileName); + if (outputResolutionHasNumber) + _Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory); + (notMapped, exceptions) = FullParallelWork(maxDegreeOfParallelism, + propertyLogic, + metadata, + mapLogic, + outputResolution, + outputResolutionHasNumber, + cResultsFullGroupDirectory, + d2ResultsFullGroupDirectory, + sourceDirectoryChanges, + fileNameToCollection, + container, + filteredItems, + message); + totalNotMapped += notMapped; + if (exceptions) + { + _Exceptions.Add(container.SourceDirectory); + continue; + } + if (Directory.GetFiles(propertyRoot, "*.txt", SearchOption.TopDirectoryOnly).Length > 0) + { + for (int y = 0; y < int.MaxValue; y++) + { + _Logger?.LogInformation("Press \"Y\" key when ready to continue or close console"); + if (_Console.ReadKey() == ConsoleKey.Y) + break; + } + _Logger?.LogInformation(". . ."); + } + total += container.Items.Count; + } + totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + message = $"{totalSeconds} total second(s) - {outputResolution} - ### [###] / {readOnlyContainers.Count:000} - {total} / {count} total - <> - total not mapped {totalNotMapped:000000}"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using ProgressBar progressBar = new(1, message, options); + progressBar.Tick(); + } + } + + private ReadOnlyCollection GetMappings(Property.Models.Configuration propertyConfiguration, string eDistanceContentDirectory, ReadOnlyCollection readOnlyContainers, MapLogic mapLogic, bool distinctItems) + { + List results = []; + int count = 0; + int notMapped; + Mapping mapping; + bool anyValidFaces; + List distinct = []; + string focusRelativePath; + bool? isFocusRelativePath; + DateTime[] containerDateTimes; + MappingFromItem mappingFromItem; + ReadOnlyCollection filteredItems; + foreach (Container.Models.Container container in readOnlyContainers) + { + if (container.Items.Count == 0) + continue; + filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(propertyConfiguration, container); + if (filteredItems.Count == 0) + continue; + containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); + focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory)); + isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath); + foreach (Item item in filteredItems) + { + if (item.Property?.Id is null || item.ResizedFileHolder is null) + continue; + mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, item.ResizedFileHolder); + if (distinctItems) + { + if (distinct.Contains(item.Property.Id.Value)) + continue; + distinct.Add(item.Property.Id.Value); + } + count++; + anyValidFaces = false; + foreach (Shared.Models.Face face in item.Faces) + { + if (face.Mapping is null) + throw new NotSupportedException(); + if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) + continue; + anyValidFaces = true; + results.Add(face.Mapping); + } + if (!anyValidFaces) + { + (mapping, notMapped) = GetMappingAndUpdateMappingFromPerson(mapLogic, item, isFocusRelativePath, mappingFromItem); + results.Add(mapping); + } + } + } + if (_Configuration.MoveToDecade && _Configuration.LocationContainerDistanceTolerance is null) + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(eDistanceContentDirectory); + return results.AsReadOnly(); + } + + private (Mapping, int) GetMappingAndUpdateMappingFromPerson(MapLogic mapLogic, Item item, bool? isFocusRelativePath, MappingFromItem mappingFromItem) + { + Mapping result; + int? eyeα = null; + bool? eyeReview = null; + int confidencePercent = 0; + int faceAreaPermyriad = 0; + bool? isFocusModel = GetIsFocusModel(item.Property); + long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); + int wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(Shared.Models.Stateless.ILocation.Digits); + string deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, Shared.Models.Stateless.ILocation.Digits); + MappingFromLocation? mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle); + bool? inSkipCollection = mapLogic.InSkipCollection(mappingFromItem.Id, mappingFromLocation); + MappingFromFilterPre mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath); + ReadOnlyDictionary>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(mappingFromItem.Id); + bool? canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); + bool? isFocusPerson = mapLogic.IsFocusPerson(_Configuration.SkipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); + MappingFromFilterPost mappingFromFilterPost = new(canReMap, inSkipCollection, isFocusPerson); + result = Mapping.Get(item.FilePath, mappingFromFilterPost, mappingFromFilterPre, mappingFromItem, mappingFromLocation, mappingFromPhotoPrismCollection: null); + int notMapped = mapLogic.UpdateMappingFromPerson(wholePercentagesToPersonContainers, result); + return (result, notMapped); + } + + private void MapLogic(long ticks, ReadOnlyCollection containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageFaces, ReadOnlyCollection distinctValidImageMappingCollection) + { + IDlibDotNet dlibDotNet = this; + (_, _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); + string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); + string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); + string d2FacePartsContentCollectionDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContentCollection); + if (distinctValidImageMappingCollection.Count > 0) + { + Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(d2FacePartsContentDirectory, ticks); + if (Directory.Exists(d2FacePartsContentCollectionDirectory)) + Shared.Models.Stateless.Methods.IPath.MakeHiddenIfAllItemsAreHidden(d2FacePartsContentCollectionDirectory); + } + if (Directory.Exists(fPhotoPrismContentDirectory)) + F_PhotoPrism.WriteMatches(fPhotoPrismContentDirectory, _Configuration.PersonBirthdayFormat, _Configuration.RectangleIntersectMinimums, ticks, distinctValidImageFaces, mapLogic); + if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) + mapLogic.SaveShortcutsForOutputResolutionsDuringMapLogic(containers, personKeyToIds, dFacesContentDirectory, distinctValidImageMappingCollection); + ReadOnlyDictionary> idToWholePercentagesToMapping = Map.Models.Stateless.Methods.IMapLogic.GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); + if (_Configuration.SaveMappedForOutputResolutions.Contains(outputResolution)) + mapLogic.SaveMapped(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, personKeyToIds, distinctValidImageMappingCollection, idToWholePercentagesToMapping); + if (_Configuration.SaveFaceDistancesForOutputResolutions.Contains(outputResolution)) + SaveFaceDistances(ticks, mapLogic, distinctValidImageFaces, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping); + } + + private void SaveFaceDistances(long ticks, MapLogic mapLogic, ReadOnlyCollection distinctValidImageFaces, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping) + { + E_Distance.PreFilterSetFaceDistances(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, distinctValidImageFaces); + ReadOnlyCollection faceDistanceContainers = E_Distance.GetFaceDistanceContainers(distinctValidImageFaces); + if (faceDistanceContainers.Count > 0) + { + List faceDistanceEncodings = []; + foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers) + { + if (faceDistanceContainer.FaceDistance.Encoding is null) + continue; + faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance); + } + SaveFaceDistances(ticks, mapLogic, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, new(faceDistanceEncodings), faceDistanceContainers); + } + } + + private void SaveFaceDistances(long ticks, MapLogic mapLogic, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping, ReadOnlyCollection faceDistanceEncodings, ReadOnlyCollection faceDistanceContainers) + { + int? useFiltersCounter = null; + DistanceLimits distanceLimits; + ReadOnlyCollection sortingContainers; + FaceDistanceContainer[] filteredFaceDistanceContainers; + long? skipOlderThan = _Configuration.SkipOlderThanDays is null ? null : new DateTime(ticks).AddDays(-_Configuration.SkipOlderThanDays.Value).Ticks; + distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh); + filteredFaceDistanceContainers = E_Distance.FilteredPostLoadFaceDistanceContainers(mapLogic, faceDistanceContainers, skipOlderThan, distanceLimits); + if (filteredFaceDistanceContainers.Length == 0) + _Logger?.LogInformation("All images have been filtered!"); + else + { + sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortedSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers); + if (sortingContainers.Count == 0) + { + for (useFiltersCounter = 1; useFiltersCounter < _Configuration.UseFilterTries; useFiltersCounter++) + { + distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh, useFiltersCounter); + filteredFaceDistanceContainers = E_Distance.FilteredPostLoadFaceDistanceContainers(mapLogic, faceDistanceContainers, skipOlderThan, distanceLimits); + if (filteredFaceDistanceContainers.Length == 0) + _Logger?.LogInformation("All images have been filtered!"); + else + { + sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortedSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers); + if (sortingContainers.Count == 0) + break; + } + } + } + sortingContainers = mapLogic.GetFilterSortingContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, distanceLimits, sortingContainers); + if (sortingContainers.Count > 0) + E_Distance.SaveFaceDistances(_Configuration.PropertyConfiguration, sortingContainers); + if (filteredFaceDistanceContainers.Length > 0) + { + int updated = sortingContainers.Count == 0 ? 0 : mapLogic.UpdateFromSortingContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, sortingContainers); + List saveContainers = mapLogic.GetSaveContainers(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, distanceLimits, useFiltersCounter, sortingContainers); + if (saveContainers.Count > 0) + mapLogic.SaveContainers(updated, saveContainers); + } + } + } + + private void VerifyExtra(List args, Property.Models.Configuration propertyConfiguration, Models.Configuration configuration) + { + string[] sourceDirectoryNames; + if (args.Count == 0) + sourceDirectoryNames = []; + else + { + string? century; + string argZero = Path.GetFullPath(args[0]); + sourceDirectoryNames = argZero.Split(Path.DirectorySeparatorChar); + if (!argZero.StartsWith(propertyConfiguration.RootDirectory)) + throw new Exception($"Source directory must be inside root directory! <{argZero}> <{propertyConfiguration.RootDirectory}>"); + if (_ArgZeroIsConfigurationRootDirectory && propertyConfiguration.RootDirectory != argZero) + { + if (!configuration.MixedYearRelativePaths.Contains(sourceDirectoryNames[0])) + { + string[] segments = sourceDirectoryNames[0].Split(' '); + century = segments[^1].Length == 4 ? segments[^1][..2] : null; + if (segments.Length < 2 || century is null || (century != "18" && century != "19" && century != "20")) + throw new Exception("root subdirectory must have fileNameToCollection year at the end or directory name needs to be added to the exclude list!"); + } + } + } + string[] resizeMatch = (from l in sourceDirectoryNames where configuration.ValidResolutions.Contains(l) select l).ToArray(); + if (resizeMatch.Length > 0) + throw new Exception("Input directory should be the source and not fileNameToCollection resized directory!"); + if (configuration.LocationDigits != Shared.Models.Stateless.ILocation.Digits) + throw new Exception("Configuration has to match interface!"); + if (configuration.LocationFactor != Shared.Models.Stateless.ILocation.Factor) + throw new Exception("Configuration has to match interface!"); + if (configuration.SaveSortingWithoutPerson && configuration.JLinks.Length > 0) + throw new Exception("Configuration has SaveSortingWithoutPerson and JLinks!"); + if (configuration.SaveSortingWithoutPerson && !string.IsNullOrEmpty(configuration.FocusModel)) + throw new Exception("Configuration has SaveSortingWithoutPerson and FocusModel!"); + if (configuration.SaveSortingWithoutPerson && !string.IsNullOrEmpty(configuration.FocusDirectory)) + throw new Exception("Configuration has SaveSortingWithoutPerson and FocusDirectory!"); + } + + private int GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(MapLogic mapLogic, Item item, bool? isFocusRelativePath, MappingFromItem mappingFromItem, List? mappingFromPhotoPrismCollection, List faces) + { + int result; + double? α; + int? eyeα; + bool? canReMap; + bool? eyeReview; + Mapping mapping; + int notMapped = 0; + bool? isFocusPerson; + int confidencePercent; + int faceAreaPermyriad; + bool? inSkipCollection; + int wholePercentRectangle; + string deterministicHashCodeKey; + MappingFromLocation? mappingFromLocation; + MappingFromFilterPre mappingFromFilterPre; + MappingFromFilterPost mappingFromFilterPost; + bool? isFocusModel = GetIsFocusModel(item.Property); + ReadOnlyDictionary>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.Property?.Id); + long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); + foreach (Shared.Models.Face face in faces) + { + if (item.Property?.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) + { + canReMap = null; + isFocusPerson = null; + inSkipCollection = null; + mappingFromLocation = null; + mappingFromFilterPost = new(canReMap, inSkipCollection, isFocusPerson); + mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath); + } + else + { + if (face.FaceParts is null) + (eyeα, eyeReview) = (null, null); + else + { + (eyeReview, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts); + eyeα = α is null ? null : (int)Math.Round(Math.Abs(α.Value)); + } + confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_Configuration.FaceConfidencePercent, face.Location.Confidence); + faceAreaPermyriad = IMapping.GetAreaPermyriad(_Configuration.FaceAreaPermyriad, face.Location, face.OutputResolution); + wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); + deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); + mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle); + inSkipCollection = mapLogic.InSkipCollection(item.Property.Id.Value, mappingFromLocation); + mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath); + canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); + isFocusPerson = mapLogic.IsFocusPerson(_Configuration.SkipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); + mappingFromFilterPost = new(canReMap, inSkipCollection, isFocusPerson); + } + mapping = Mapping.Get(item.FilePath, mappingFromFilterPost, mappingFromFilterPre, mappingFromItem, mappingFromLocation, mappingFromPhotoPrismCollection); + notMapped += mapLogic.UpdateMappingFromPerson(wholePercentagesToPersonContainers, mapping); + face.SetMapping(mapping); + } + result = notMapped; + return result; + } + } \ No newline at end of file diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 7bbc980..975b1b6 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -15,9 +15,6 @@ namespace View_by_Distance.Map.Models; public partial class MapLogic : Shared.Models.Methods.IMapLogic { - [GeneratedRegex("[\\\\,\\/,\\:,\\*,\\?,\\\",\\<,\\>,\\|]")] - private static partial Regex FileSystemSafe(); - internal record Record(string? DebugDirectory, string? Directory, long? Ticks, @@ -31,6 +28,138 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic string PersonDirectory, string CheckFile); + private readonly long _Ticks; + private readonly Configuration? _Configuration; + private readonly string _EDistanceContentTicksDirectory; + private readonly ReadOnlyDictionary _PersonKeyToCount; + private readonly List _NotMappedPersonContainers; + private readonly ReadOnlyDictionary> _SkipCollection; + private readonly ReadOnlyDictionary> _SkipNotSkipCollection; + private readonly Shared.Models.Properties.IPropertyConfiguration _PropertyConfiguration; + private readonly ReadOnlyDictionary> _IdToLocationContainers; + private readonly ReadOnlyDictionary>> _IdThenWholePercentagesToPersonContainers; + + (bool, ReadOnlyDictionary>?) Shared.Models.Methods.IMapLogic.GetWholePercentagesToPersonContainers(int id) + { + bool result = _IdThenWholePercentagesToPersonContainers.TryGetValue(id, out ReadOnlyDictionary>? wholePercentagesToPersonContainers); + return new(result, wholePercentagesToPersonContainers); + } + + public MapLogic(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration? configuration, Shared.Models.Methods.IDistance distance, ReadOnlyCollection personContainers, long ticks, string? a2PeopleContentDirectory, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) + { + _Ticks = ticks; + _Configuration = configuration; + _PropertyConfiguration = propertyConfiguration; + List locationContainers = []; + List notMappedPersonContainers = []; + ReadOnlyDictionary readOnlyPersonKeyToCount; + Dictionary> skipCollection = []; + Dictionary> skipNotSkipCollection = []; + ReadOnlyDictionary> readOnlySkipCollection; + string? rootDirectoryParent = Path.GetDirectoryName(propertyConfiguration.RootDirectory); + string eDistanceContentTicksDirectory = Path.Combine(eDistanceContentDirectory, ticks.ToString()); + ReadOnlyDictionary>> idThenWholePercentagesToPersonContainers; + if (string.IsNullOrEmpty(rootDirectoryParent)) + throw new NullReferenceException(nameof(rootDirectoryParent)); + if (!Directory.Exists(eDistanceContentDirectory)) + _ = Directory.CreateDirectory(eDistanceContentDirectory); + if (configuration is null) + { + readOnlyPersonKeyToCount = new(new Dictionary()); + idThenWholePercentagesToPersonContainers = new(new Dictionary>>()); + } + else + { + ReadOnlyCollection readOnlyPersonKeyFormattedCollection; + ReadOnlyDictionary readOnlyPersonKeyFormattedToNewestPersonKeyFormatted; + ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer; + ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection; + int copied = Stateless.MapLogic.CopyManualFiles(configuration, ticks, personContainers, eDistanceContentTicksDirectory); + if (copied > 0) + throw new Exception("Confirm Manual files and then restart!"); + Stateless.MapLogic.SetSkipCollections(configuration, personContainers, a2PeopleSingletonDirectory, skipCollection, skipNotSkipCollection); + { + List personKeyFormattedCollection = []; + Dictionary personKeyFormattedToNewestPersonKeyFormatted = []; + Stateless.MapLogic.SetPersonCollectionsAfterSetSkipCollections(configuration, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection); + readOnlyPersonKeyFormattedCollection = new(personKeyFormattedCollection); + readOnlyPersonKeyFormattedToNewestPersonKeyFormatted = new(personKeyFormattedToNewestPersonKeyFormatted); + } + List records = Stateless.DistanceLogic.DeleteEmptyDirectoriesAndGetCollection(propertyConfiguration, configuration, ticks, eDistanceContentDirectory, readOnlyPersonKeyFormattedToNewestPersonKeyFormatted, readOnlyPersonKeyFormattedCollection); + ReadOnlyCollection<(Stateless.MapLogic.PersonKeyFormattedIdThenWholePercentages, PersonContainer)> readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer; + ReadOnlyCollection personKeyFormattedIdThenWholePercentagesCollection = Stateless.MapLogic.GetPersonKeyFormattedIdThenWholePercentages(configuration, ticks, records); + // + { + Dictionary personKeyToCount = []; + Dictionary personKeyToPersonContainer = []; + Dictionary personKeyFormattedToPersonContainer = []; + Dictionary> personKeyToPersonContainerCollection = []; + List<(Stateless.MapLogic.PersonKeyFormattedIdThenWholePercentages, PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer = []; + Stateless.MapLogic.SetKeyValuePairsAndAddToCollections(configuration, + personContainers, + personKeyToPersonContainer, + personKeyFormattedIdThenWholePercentagesCollection, + personKeyToCount, + personKeyFormattedToPersonContainer, + personKeyToPersonContainerCollection, + possiblyNewPersonDisplayDirectoryNamesAndPersonContainer); + readOnlyPersonKeyToCount = new(personKeyToCount); + readOnlyPersonKeyFormattedToPersonContainer = new(personKeyFormattedToPersonContainer); + readOnlyPersonKeyToPersonContainerCollection = new(personKeyToPersonContainerCollection); + readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer = new(possiblyNewPersonDisplayDirectoryNamesAndPersonContainer); + Stateless.MapLogic.SetPersonKeyToPersonContainer(configuration, + personContainers, + readOnlyPersonKeyToCount, + personKeyToPersonContainer, + readOnlyPersonKeyToPersonContainerCollection); + } + Stateless.MapLogic.PossiblyRebuildPersonContainers(configuration, + ticks, + a2PeopleSingletonDirectory, + readOnlyPersonKeyToCount, + readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer); + idThenWholePercentagesToPersonContainers = Stateless.MapLogic.GetIdThenWholePercentagesToPersonContainers(configuration, + skipCollection, + readOnlyPersonKeyFormattedToPersonContainer, + personKeyFormattedIdThenWholePercentagesCollection); + readOnlySkipCollection = new(skipCollection); + notMappedPersonContainers.AddRange(Stateless.MapLogic.GetNotMappedPersonContainers(configuration, + ticks, + personContainers, + readOnlyPersonKeyToCount)); + locationContainers.AddRange(Stateless.MapLogic.GetLocationContainers(maxDegreeOfParallelism, + propertyConfiguration, + configuration, + ticks, + personContainers, + readOnlySkipCollection, + records)); + int lossCount = records.Count - locationContainers.Count; + int unableToMatchCount = records.Count - personKeyFormattedIdThenWholePercentagesCollection.Count; + if (lossCount != 0 || unableToMatchCount != 0) + if (lossCount != 0 || unableToMatchCount != 0) + { } + if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && configuration.LocationContainerDistanceTolerance is not null) + Stateless.RelationLogic.SaveMappedRelations(configuration, distance, a2PeopleContentDirectory, eDistanceContentDirectory, ticks, locationContainers, readOnlyPersonKeyFormattedToPersonContainer, readOnlyPersonKeyToPersonContainerCollection); + if (!string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) + throw new Exception($"{nameof(configuration.LocationContainerDebugDirectory)} is not IsNullOrEmpty!"); + } + _PersonKeyToCount = readOnlyPersonKeyToCount; + _EDistanceContentTicksDirectory = eDistanceContentTicksDirectory; + _SkipCollection = Stateless.MapLogic.ConvertSkip(skipCollection); + Stateless.MapLogic.CheckCollection(propertyConfiguration, rootDirectoryParent); + _IdThenWholePercentagesToPersonContainers = idThenWholePercentagesToPersonContainers; + _SkipNotSkipCollection = Stateless.MapLogic.ConvertSkipNotSkip(skipNotSkipCollection); + _IdToLocationContainers = Stateless.MapLogic.ConvertLocationContainers(locationContainers); + _NotMappedPersonContainers = new(notMappedPersonContainers.OrderByDescending(l => l.Key).ToArray()); + } + + public bool InSkipCollection(int id, int wholePercentages) => + _SkipCollection.TryGetValue(id, out List? wholePercentagesCollection) && wholePercentagesCollection.Contains(wholePercentages); + + public bool InSkipCollection(int id, MappingFromLocation mappingFromLocation) => + InSkipCollection(id, mappingFromLocation.WholePercentages); + public void SaveContainers(int? updated, List saveContainers) { if (_Configuration is null) @@ -145,6 +274,235 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic } } + public override string ToString() + { + string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + return result; + } + + public ReadOnlyDictionary> GetPersonKeyToIds() + { + Dictionary> results = []; + long personKey; + const int zero = 0; + List? collection; + PersonBirthday personBirthday; + List shouldMove = []; + foreach (KeyValuePair>> idToCollection in _IdThenWholePercentagesToPersonContainers) + { + foreach (KeyValuePair> wholePercentagesToPersonContainers in idToCollection.Value) + { + foreach (PersonContainer personContainer in wholePercentagesToPersonContainers.Value) + { + if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) + continue; + if (personContainer.DisplayDirectoryName.Contains(@"{}\~\")) + shouldMove.Add(personContainer.DisplayDirectoryName); + personBirthday = personContainer.Birthdays[zero]; + personKey = personBirthday.Value.Ticks; + if (!results.TryGetValue(personKey, out collection)) + { + results.Add(personKey, []); + if (!results.TryGetValue(personKey, out collection)) + throw new Exception(); + } + if (collection.Contains(idToCollection.Key)) + continue; + collection.Add(idToCollection.Key); + } + } + } + if (shouldMove.Count > 0) + throw new Exception(string.Join(Environment.NewLine, shouldMove)); + return results.AsReadOnly(); + } + + public ReadOnlyCollection GetLocationContainers(Item item) + { + LocationContainer[] results; + if (item.Property?.Id is null) + results = []; + else + { + List? locationContainers; + if (_IdToLocationContainers.TryGetValue(item.Property.Id.Value, out locationContainers)) + results = locationContainers.ToArray(); + else + results = []; + } + return results.AsReadOnly(); + } + + public ReadOnlyDictionary>? GetWholePercentagesToPersonContainers(int? id) + { + ReadOnlyDictionary>? result; + if (id is null) + result = null; + else + _ = _IdThenWholePercentagesToPersonContainers.TryGetValue(id.Value, out result); + return result; + } + + public List GetSortingCollection(int i, Face face, FaceDistance faceDistanceEncoding, List faceDistanceLengths) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + List results = []; + Sorting sorting; + FaceDistance faceDistanceLength; + for (int j = 0; j < faceDistanceLengths.Count; j++) + { + if (faceDistanceEncoding.WholePercentages is null) + throw new NotSupportedException(); + if (face.Mapping?.MappingFromFilterPost is null) + throw new NotSupportedException(); + if (j == i) + continue; + if (face.Mapping.MappingFromFilterPre.InSkipCollection is not null && face.Mapping.MappingFromFilterPre.InSkipCollection.Value) + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + if (face.Mapping.MappingFromFilterPre.IsFocusModel is not null && !face.Mapping.MappingFromFilterPre.IsFocusModel.Value) + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + if (face.Mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !face.Mapping.MappingFromFilterPre.IsFocusRelativePath.Value) + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + if (face.Mapping.MappingFromFilterPost.InSkipCollection is not null && face.Mapping.MappingFromFilterPost.InSkipCollection.Value) + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + if (face.Mapping.MappingFromFilterPost.IsFocusPerson is not null && !face.Mapping.MappingFromFilterPost.IsFocusPerson.Value) + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + if (face.Mapping.MappingFromFilterPost.InSkipCollection is not null && face.Mapping.MappingFromFilterPost.InSkipCollection.Value) + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + faceDistanceLength = faceDistanceLengths[j]; + if (faceDistanceLength.WholePercentages is null || faceDistanceLength.Length is null) + throw new NotSupportedException(); + if (faceDistanceLength.Length == 0) + continue; + if (faceDistanceLength.Id == faceDistanceEncoding.Id) + continue; + if (faceDistanceLength.MappingFromFilterPost is null) + throw new NotSupportedException(); + if (faceDistanceLength.MappingFromFilterPost.CanReMap is not null && !_Configuration.ReMap) + continue; + if (faceDistanceLength.MappingFromFilterPost.CanReMap is not null && !faceDistanceLength.MappingFromFilterPost.CanReMap.Value) + continue; + if (faceDistanceLength.MappingFromFilterPost.InSkipCollection is not null && faceDistanceLength.MappingFromFilterPost.InSkipCollection.Value) + continue; + if (faceDistanceLength.MappingFromFilterPost.IsFocusPerson is not null && !faceDistanceLength.MappingFromFilterPost.IsFocusPerson.Value) + continue; + sorting = ISorting.Get(_Configuration.FaceDistancePermyriad, faceDistanceEncoding, faceDistanceLength); + if (sorting.DistancePermyriad == 0) + continue; + if (sorting.Id == faceDistanceEncoding.Id) + { + if (sorting.WholePercentages == faceDistanceEncoding.WholePercentages.Value) + continue; + continue; + } + results.Add(sorting); + } + return results; + } + + public int UpdateMappingFromPerson(ReadOnlyDictionary>? wholePercentagesToPersonContainers, Mapping mapping) + { + int result = 0; + if (mapping.MappingFromLocation is not null) + { + if (wholePercentagesToPersonContainers is null) + result += 1; + else + { + ReadOnlyCollection? personContainers; + if (!wholePercentagesToPersonContainers.TryGetValue(mapping.MappingFromLocation.WholePercentages, out personContainers)) + result += 1; + else + { + const int zero = 0; + string mappingSegmentB; + PersonBirthday personBirthday; + foreach (PersonContainer personContainer in personContainers) + { + if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) + continue; + personBirthday = personContainer.Birthdays[zero]; + mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(_Ticks, personBirthday, personContainer.ApproximateYears, mapping.MappingFromItem); + mapping.UpdateMappingFromPerson(personContainer.ApproximateYears, personContainer.DisplayDirectoryName, personContainer.Key.Value, mappingSegmentB); + } + } + } + } + return result; + } + + public bool? IsFocusPerson(int? skipPersonWithMoreThen, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, int wholePercentages) + { + bool? result; + ReadOnlyCollection? personContainers; + if (skipPersonWithMoreThen is null && jLinkResolvedPersonKeys.Length == 0) + result = null; + else if (wholePercentagesToPersonContainers is null) + result = null; + else if (!wholePercentagesToPersonContainers.TryGetValue(wholePercentages, out personContainers)) + result = null; + else + { + result = false; + foreach (PersonContainer personContainer in personContainers) + { + if (personContainer.Key is null) + continue; + if (skipPersonWithMoreThen is not null && _PersonKeyToCount.TryGetValue(personContainer.Key.Value, out int count) && count > 2 && count < skipPersonWithMoreThen.Value) + { + result = true; + break; + } + if (jLinkResolvedPersonKeys.Contains(personContainer.Key.Value)) + { + result = true; + break; + } + } + } + return result; + } + + public void SaveShortcutsForOutputResolutionsPreMapLogic(string eDistanceContentDirectory, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageMappingCollection) + { + string hiddenFile; + WindowsShortcut windowsShortcut; + List collection = []; + collection = GetCollectionForSaveShortcutsForOutputResolutionsPreMapLogic(eDistanceContentDirectory, personKeyToIds, distinctValidImageMappingCollection); + string[] distinctDirectories = (from l in collection select l.Directory).Distinct().ToArray(); + foreach (string directory in distinctDirectories) + { + if (string.IsNullOrEmpty(directory)) + continue; + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + } + foreach (SaveShortcutsForOutputResolutions s in collection) + { + hiddenFile = $"{s.FileName}.lvs"; + if (File.Exists(hiddenFile)) + continue; + if (s.Description is not null) + { + File.WriteAllLines(hiddenFile, [s.FullName, s.Description]); + File.SetAttributes(hiddenFile, FileAttributes.Hidden); + File.SetLastWriteTime(hiddenFile, s.DateTime); + } + if (File.Exists(s.FileName)) + continue; + try + { + windowsShortcut = new() { Path = s.FullName, Description = s.Description }; + windowsShortcut.Save(s.FileName); + windowsShortcut.Dispose(); + File.SetLastWriteTime(s.FileName, s.DateTime); + } + catch (Exception) + { } + } + } + private List GetCollectionForSaveShortcutsForOutputResolutionsPreMapLogic(string eDistanceContentDirectory, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageMappingCollection) { List results = []; @@ -199,6 +557,409 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return results; } + [GeneratedRegex("[\\\\,\\/,\\:,\\*,\\?,\\\",\\<,\\>,\\|]")] + private static partial Regex FileSystemSafe(); + + public bool? IsFocusPerson(int? skipPersonWithMoreThen, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => + IsFocusPerson(skipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages); + + public void SaveShortcutsForOutputResolutionsDuringMapLogic(ReadOnlyCollection containers, ReadOnlyDictionary> personKeyToIds, string dFacesContentDirectory, ReadOnlyCollection distinctValidImageMappingCollection) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + WindowsShortcut windowsShortcut; + List<(string, DateTime[])> directoriesAndDateTimes; + List collection; + ReadOnlyCollection validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_PropertyConfiguration, containers, distinctItems: true, filterItems: true); + (directoriesAndDateTimes, collection) = GetCollectionForSaveShortcutsForOutputResolutionsDuringMapLogic(personKeyToIds, dFacesContentDirectory, validImageItems, distinctValidImageMappingCollection); + string[] directories = (from l in collection select l.Directory).Distinct().ToArray(); + foreach (string directory in directories) + { + if (string.IsNullOrEmpty(directory)) + continue; + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + } + foreach (SaveShortcutsForOutputResolutions s in collection) + { + if (File.Exists(s.FileName)) + continue; + try + { + windowsShortcut = new() { Path = s.FullName, Description = s.Description }; + windowsShortcut.Save(s.FileName); + windowsShortcut.Dispose(); + if (s.MakeAllHidden) + File.SetAttributes(s.FileName, FileAttributes.Hidden); + File.SetLastWriteTime(s.FileName, s.DateTime); + } + catch (Exception) + { } + } + foreach ((string directory, DateTime[] dateTimes) in directoriesAndDateTimes) + { + if (dateTimes.Length == 0) + continue; + Directory.SetCreationTime(directory, dateTimes[0]); + Directory.SetLastWriteTime(directory, dateTimes[1]); + } + } + + private (List<(string, DateTime[])>, List) GetCollectionForSaveShortcutsForOutputResolutionsDuringMapLogic(ReadOnlyDictionary> personKeyToIds, string dFacesContentDirectory, ReadOnlyCollection validImageItems, ReadOnlyCollection distinctValidImageMappingCollection) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + List? ids; + string fileName; + string fullName; + string directory; + DateTime dateTime; + string facesDirectory; + string? directoryName; + string personDirectory; + string personKeyFormatted; + List distinct = []; + List collection = []; + List<(string, DateTime[])> directoriesAndDateTimes = []; + foreach (Item item in validImageItems) + { + if (item.ResizedFileHolder is null) + continue; + if (item.Faces.Any(l => l.FaceEncoding is not null)) + continue; + foreach (Face face in item.Faces) + { + if (face.Mapping is null) + continue; + directoryName = Path.GetDirectoryName(face.Mapping.MappingFromItem.RelativePath); + if (directoryName is null) + throw new NotSupportedException(); + if (item.ResizedFileHolder?.DirectoryFullPath is null || !item.ResizedFileHolder.Exists) + continue; + directory = Path.Combine(item.ResizedFileHolder.DirectoryFullPath, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", _PropertyConfiguration.ResultAllInOne); + personDirectory = Path.Combine(directory, "No Faces"); + fileName = Path.Combine(personDirectory, $"{item.ResizedFileHolder.Name}.lnk"); + collection.Add(new(item.ResizedFileHolder.FullName, personDirectory, new(item.FilePath.LastWriteTicks), fileName, face.Mapping.MappingFromItem.Id.ToString(), MakeAllHidden: false)); + if (face.Mapping.MappingFromItem.ContainerDateTimes.Length > 0 && !distinct.Contains(item.ResizedFileHolder.DirectoryFullPath)) + { + distinct.Add(item.ResizedFileHolder.DirectoryFullPath); + directoriesAndDateTimes.Add(new(item.ResizedFileHolder.DirectoryFullPath, face.Mapping.MappingFromItem.ContainerDateTimes)); + } + } + } + foreach (Mapping mapping in distinctValidImageMappingCollection) + { + directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); + if (directoryName is null) + throw new NotSupportedException(); + if (mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath is null || !mapping.MappingFromItem.ResizedFileHolder.Exists) + continue; + dateTime = mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(); + if (mapping.By is null or Shared.Models.Stateless.IMapLogic.Sorting || mapping.MappingFromPerson?.ApproximateYears is null) + { + if (mapping.MappingFromItem.ContainerDateTimes.Length > 0 && !distinct.Contains(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath)) + { + distinct.Add(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath); + directoriesAndDateTimes.Add(new(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath, mapping.MappingFromItem.ContainerDateTimes)); + } + directory = Path.Combine(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", _PropertyConfiguration.ResultAllInOne); + personDirectory = Path.Combine(directory, "Unknown"); + fileName = Path.Combine(personDirectory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk"); + collection.Add(new(mapping.MappingFromItem.ResizedFileHolder.FullName, personDirectory, dateTime, fileName, mapping.MappingFromLocation?.DeterministicHashCodeKey, MakeAllHidden: false)); + facesDirectory = Stateless.MapLogic.GetFacesDirectory(_PropertyConfiguration, dFacesContentDirectory, mapping.FilePath, mapping.MappingFromItem); + if (mapping.MappingFromLocation is null) + continue; + fullName = Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{_Configuration.FacesFileNameExtension}"); + fileName = Path.Combine(personDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ResizedFileHolder.ExtensionLowered}{_Configuration.FacesFileNameExtension}.lnk"); + collection.Add(new(fullName, personDirectory, dateTime, fileName, mapping.MappingFromLocation.DeterministicHashCodeKey, MakeAllHidden: true)); + } + else + { + if (string.IsNullOrEmpty(mapping.MappingFromPerson.SegmentB)) + throw new NotSupportedException(); + if (string.IsNullOrEmpty(mapping.MappingFromPerson.DisplayDirectoryName)) + throw new NotSupportedException(); + personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mapping.MappingFromPerson.PersonKey); + if (mapping.MappingFromItem.ContainerDateTimes.Length > 0 && !distinct.Contains(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath)) + { + distinct.Add(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath); + directoriesAndDateTimes.Add(new(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath, mapping.MappingFromItem.ContainerDateTimes)); + } + directory = Path.Combine(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", personKeyFormatted); + if (!personKeyToIds.TryGetValue(mapping.MappingFromPerson.PersonKey, out ids)) + personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); + else + personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName, $"{ids.Count} Face(s)"); + fileName = Path.Combine(directory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk"); + collection.Add(new(mapping.MappingFromItem.ResizedFileHolder.FullName, personDirectory, dateTime, fileName, mapping.MappingFromLocation?.DeterministicHashCodeKey, MakeAllHidden: false)); + } + } + return new(directoriesAndDateTimes, collection); + } + + public void SaveFilteredOriginalImagesFromJLinks(string[] jLinks, ReadOnlyCollection personContainers, string a2PeopleContentDirectory, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageMappingCollection) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + int? updated = null; + FileHolder fileHolder; + SaveContainer? saveContainer; + List saveContainers = []; + FilteredOriginalImage[] filteredOriginalImages = GetCollectionForSaveFilteredOriginalImagesFromJLinks(jLinks, a2PeopleContentDirectory, personContainers, distinctValidImageMappingCollection, personKeyToIds); + foreach (FilteredOriginalImage filteredOriginalImage in filteredOriginalImages) + { + fileHolder = FileHolder.Get(filteredOriginalImage.FilePath); + saveContainer = new(filteredOriginalImage.PersonDirectory); + saveContainers.Add(saveContainer); + saveContainer = new(fileHolder, filteredOriginalImage.CheckFile, filteredOriginalImage.Directory); + saveContainers.Add(saveContainer); + } + SaveContainers(updated, saveContainers); + } + + private FilteredOriginalImage[] GetCollectionForSaveFilteredOriginalImagesFromJLinks(string[] jLinks, string a2PeopleContentDirectory, ReadOnlyCollection personContainers, ReadOnlyCollection distinctValidImageMappingCollection, ReadOnlyDictionary> personKeyToIds) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + FilteredOriginalImage[] results; + int count = 0; + int group = 65; + string checkFile; + string directory; + string? directoryName; + string personDirectory; + string personKeyFormatted; + List distinctCollection = []; + bool usePersonKeyAndDeterministicHashCodeKey = false; + List filteredOriginalImages = []; + List personKeyFormattedCollection = GetPersonKeyFormattedCollection(jLinks, a2PeopleContentDirectory, personContainers, personKeyToIds); + foreach (Mapping mapping in distinctValidImageMappingCollection) + { + if (distinctCollection.Contains(mapping.MappingFromItem.Id)) + continue; + directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); + if (directoryName is null) + throw new NotSupportedException(); + if (mapping.By is null or Shared.Models.Stateless.IMapLogic.Sorting) + continue; + if (mapping.MappingFromPerson is null || mapping.MappingFromLocation is null) + continue; + if (string.IsNullOrEmpty(mapping.MappingFromPerson.SegmentB)) + throw new NotSupportedException(); + if (string.IsNullOrEmpty(mapping.MappingFromPerson.DisplayDirectoryName)) + throw new NotSupportedException(); + personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mapping.MappingFromPerson.PersonKey); + if (personKeyFormatted == "1501-04-10_00") + continue; + if (!personKeyFormattedCollection.Contains(personKeyFormatted)) + continue; + if (!usePersonKeyAndDeterministicHashCodeKey) + { + if (count > 499) + { + count = 0; + group += 1; + } + directory = Path.Combine(_EDistanceContentTicksDirectory, ((char)group).ToString()); + personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); + checkFile = Path.Combine(directory, $"{mapping.MappingFromItem.Id}{mapping.MappingFromItem.FilePath.ExtensionLowered}"); + } + else + { + directory = Path.Combine(_EDistanceContentTicksDirectory, "Images", personKeyFormatted); + personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); + checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}"); + } + if (mapping is null) + continue; + if (mapping.MappingFromPerson.ApproximateYears is null) + filteredOriginalImages.Add(new(mapping.MappingFromItem.Id, mapping.MappingFromItem.FilePath, -1, personKeyFormatted, directory, personDirectory, checkFile)); + else + filteredOriginalImages.Add(new(mapping.MappingFromItem.Id, mapping.MappingFromItem.FilePath, mapping.MappingFromPerson.ApproximateYears.Value, personKeyFormatted, directory, personDirectory, checkFile)); + distinctCollection.Add(mapping.MappingFromItem.Id); + count += 1; + } + results = (from l in filteredOriginalImages orderby l.ApproximateYears descending, l.PersonKeyFormatted descending select l).ToArray(); + return results; + } + + private List GetPersonKeyFormattedCollection(string[] jLinks, string a2PeopleContentDirectory, ReadOnlyCollection personContainers, ReadOnlyDictionary> personKeyToIds) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + List results = []; + string[] files; + List? ids; + string checkDirectory; + string[] checkDirectories; + string personKeyFormatted; + PersonContainer[] matches; + PersonBirthday? personBirthday; + string fileNameWithoutExtension; + WindowsShortcut windowsShortcut; + List<(long, string)> collection = []; + foreach (string directoryName in jLinks) + { + checkDirectory = Path.Combine(a2PeopleContentDirectory, directoryName); + if (!Directory.Exists(checkDirectory)) + continue; + checkDirectories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly); + files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly); + foreach (string file in files) + { + collection.Clear(); + windowsShortcut = WindowsShortcut.Load(file); + fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); + if (string.IsNullOrEmpty(windowsShortcut.Path)) + continue; + if (!Directory.Exists(windowsShortcut.Path)) + { + if (files.Length != checkDirectories.Length) + throw new NotSupportedException(fileNameWithoutExtension); + continue; + } + (personKeyFormatted, personBirthday) = GetPersonBirthday(windowsShortcut.Path); + if (personBirthday is null) + throw new NotSupportedException(fileNameWithoutExtension); + if (!personKeyToIds.TryGetValue(personBirthday.Value.Ticks, out ids)) + collection.Add(new(personBirthday.Value.Ticks, Path.Combine(checkDirectory, personKeyFormatted, fileNameWithoutExtension))); + else + collection.Add(new(personBirthday.Value.Ticks, Path.Combine(checkDirectory, personKeyFormatted, fileNameWithoutExtension, $"{ids.Count} Face(s)"))); + foreach ((long personKey, string displayDirectoryName) in collection) + { + matches = (from l in personContainers where l.Key == personKey select l).ToArray(); + if (matches.Length == 0) + continue; + personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personKey); + if (!displayDirectoryName.Contains(personKeyFormatted)) + continue; + if (!Directory.Exists(displayDirectoryName)) + _ = Directory.CreateDirectory(displayDirectoryName); + results.Add(personKeyFormatted); + } + } + } + return results; + } + + private (string, PersonBirthday?) GetPersonBirthday(string[] directoryNames) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + PersonBirthday? personBirthday = null; + string personKeyFormatted = string.Empty; + foreach (string directoryName in directoryNames) + { + personBirthday = IPersonBirthday.GetPersonBirthday(_Configuration.PersonBirthdayFormat, directoryName); + if (personBirthday is not null) + { + personKeyFormatted = directoryName; + break; + } + } + return new(personKeyFormatted, personBirthday); + } + + private (string, PersonBirthday?) GetPersonBirthday(string windowsShortcutPath) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + string[] directoryNames = IPath.GetDirectoryNames(windowsShortcutPath); + (string personKeyFormatted, PersonBirthday? personBirthday) = GetPersonBirthday(directoryNames); + if (personBirthday is null) + { + string[] directories = Directory.GetDirectories(windowsShortcutPath, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) + { + directoryNames = IPath.GetDirectoryNames(directory); + (personKeyFormatted, personBirthday) = GetPersonBirthday(directoryNames); + if (personBirthday is not null) + break; + } + } + return new(personKeyFormatted, personBirthday); + } + + public int UpdateFromSortingContainers(string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping, ReadOnlyCollection sortingContainers) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + int result = 0; + string key; + string segmentB; + string? segmentC; + string personKeyFormatted; + MappingFromPerson mappingFromPerson; + Dictionary keyToCount = []; + foreach (SortingContainer sortingContainer in sortingContainers) + { + if (sortingContainer.Question is null) + continue; + if (sortingContainer.Source.MappingFromPerson is null) + { + sortingContainer.Question.UpdateMappingFromUnknownPerson(_Configuration.SaveIndividually, sortingContainer); + result += 1; + } + else + { + mappingFromPerson = sortingContainer.Source.MappingFromPerson; + personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mappingFromPerson.PersonKey); + segmentB = IMapLogic.GetDecade(sortingContainer.Question.MappingFromItem); + key = string.Concat(personKeyFormatted, '\t', segmentB); + if (!keyToCount.ContainsKey(key)) + keyToCount.Add(key, new()); + _ = keyToCount.TryAdd(key, 0); + keyToCount[key]++; + if (!_Configuration.SaveIndividually && keyToCount[key] <= _Configuration.SortingMaximumPerKey) + segmentC = null; + else + segmentC = sortingContainer.Sorting.DistancePermyriad.ToString(); + sortingContainer.Question.UpdateMappingFromPerson(mappingFromPerson.ApproximateYears, mappingFromPerson.DisplayDirectoryName, mappingFromPerson.PersonKey, segmentB, segmentC, sortingContainer); + result += 1; + } + } + return result; + } + + public void LookForAbandoned(IDlibDotNet dlibDotNet, Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) + { + string[] directories; + string? directoryName; + List distinctFilteredIds = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); + LookForAbandoned(propertyConfiguration, distinctFilteredIds); + dlibDotNet.Tick(); + List distinctFilteredFileNameFirstSegments = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); + Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, bResultsFullGroupDirectory, distinctFilteredFileNameFirstSegments); + dlibDotNet.Tick(); + directories = Directory.GetDirectories(cResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) + { + directoryName = Path.GetFileName(directory); + if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) + continue; + Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); + } + dlibDotNet.Tick(); + directories = Directory.GetDirectories(dResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) + { + directoryName = Path.GetFileName(directory); + if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) + continue; + Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); + } + dlibDotNet.Tick(); + directories = Directory.GetDirectories(d2ResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) + { + directoryName = Path.GetFileName(directory); + if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) + continue; + Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); + } + dlibDotNet.Tick(); + } + private void LookForAbandoned(Property.Models.Configuration propertyConfiguration, List distinctFilteredIds) { List renameCollection = []; @@ -217,249 +978,146 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic IDirectory.MoveFiles(renameCollection, propertyConfiguration.ResultContent, "(abd)"); } - private readonly long _Ticks; - private readonly Configuration? _Configuration; - private readonly string _EDistanceContentTicksDirectory; - private readonly ReadOnlyDictionary _PersonKeyToCount; - private readonly List _NotMappedPersonContainers; - private readonly ReadOnlyDictionary> _SkipCollection; - private readonly ReadOnlyDictionary> _SkipNotSkipCollection; - private readonly Shared.Models.Properties.IPropertyConfiguration _PropertyConfiguration; - private readonly ReadOnlyDictionary> _IdToLocationContainers; - private readonly ReadOnlyDictionary>> _IdThenWholePercentagesToPersonContainers; - - public MapLogic(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration? configuration, Shared.Models.Methods.IDistance distance, ReadOnlyCollection personContainers, long ticks, string? a2PeopleContentDirectory, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) - { - _Ticks = ticks; - _Configuration = configuration; - _PropertyConfiguration = propertyConfiguration; - List locationContainers = []; - List notMappedPersonContainers = []; - ReadOnlyDictionary readOnlyPersonKeyToCount; - Dictionary> skipCollection = []; - Dictionary> skipNotSkipCollection = []; - ReadOnlyDictionary> readOnlySkipCollection; - string? rootDirectoryParent = Path.GetDirectoryName(propertyConfiguration.RootDirectory); - string eDistanceContentTicksDirectory = Path.Combine(eDistanceContentDirectory, ticks.ToString()); - ReadOnlyDictionary>> idThenWholePercentagesToPersonContainers; - if (string.IsNullOrEmpty(rootDirectoryParent)) - throw new NullReferenceException(nameof(rootDirectoryParent)); - if (!Directory.Exists(eDistanceContentDirectory)) - _ = Directory.CreateDirectory(eDistanceContentDirectory); - if (configuration is null) - { - readOnlyPersonKeyToCount = new(new Dictionary()); - idThenWholePercentagesToPersonContainers = new(new Dictionary>>()); - } - else - { - ReadOnlyCollection readOnlyPersonKeyFormattedCollection; - ReadOnlyDictionary readOnlyPersonKeyFormattedToNewestPersonKeyFormatted; - ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer; - ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection; - int copied = Stateless.MapLogic.CopyManualFiles(configuration, ticks, personContainers, eDistanceContentTicksDirectory); - if (copied > 0) - throw new Exception("Confirm Manual files and then restart!"); - Stateless.MapLogic.SetSkipCollections(configuration, personContainers, a2PeopleSingletonDirectory, skipCollection, skipNotSkipCollection); - { - List personKeyFormattedCollection = []; - Dictionary personKeyFormattedToNewestPersonKeyFormatted = []; - Stateless.MapLogic.SetPersonCollectionsAfterSetSkipCollections(configuration, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection); - readOnlyPersonKeyFormattedCollection = new(personKeyFormattedCollection); - readOnlyPersonKeyFormattedToNewestPersonKeyFormatted = new(personKeyFormattedToNewestPersonKeyFormatted); - } - List records = Stateless.DistanceLogic.DeleteEmptyDirectoriesAndGetCollection(propertyConfiguration, configuration, ticks, eDistanceContentDirectory, readOnlyPersonKeyFormattedToNewestPersonKeyFormatted, readOnlyPersonKeyFormattedCollection); - ReadOnlyCollection<(Stateless.MapLogic.PersonKeyFormattedIdThenWholePercentages, PersonContainer)> readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer; - ReadOnlyCollection personKeyFormattedIdThenWholePercentagesCollection = Stateless.MapLogic.GetPersonKeyFormattedIdThenWholePercentages(configuration, ticks, records); - // - { - Dictionary personKeyToCount = []; - Dictionary personKeyToPersonContainer = []; - Dictionary personKeyFormattedToPersonContainer = []; - Dictionary> personKeyToPersonContainerCollection = []; - List<(Stateless.MapLogic.PersonKeyFormattedIdThenWholePercentages, PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer = []; - Stateless.MapLogic.SetKeyValuePairsAndAddToCollections(configuration, - personContainers, - personKeyToPersonContainer, - personKeyFormattedIdThenWholePercentagesCollection, - personKeyToCount, - personKeyFormattedToPersonContainer, - personKeyToPersonContainerCollection, - possiblyNewPersonDisplayDirectoryNamesAndPersonContainer); - readOnlyPersonKeyToCount = new(personKeyToCount); - readOnlyPersonKeyFormattedToPersonContainer = new(personKeyFormattedToPersonContainer); - readOnlyPersonKeyToPersonContainerCollection = new(personKeyToPersonContainerCollection); - readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer = new(possiblyNewPersonDisplayDirectoryNamesAndPersonContainer); - Stateless.MapLogic.SetPersonKeyToPersonContainer(configuration, - personContainers, - readOnlyPersonKeyToCount, - personKeyToPersonContainer, - readOnlyPersonKeyToPersonContainerCollection); - } - Stateless.MapLogic.PossiblyRebuildPersonContainers(configuration, - ticks, - a2PeopleSingletonDirectory, - readOnlyPersonKeyToCount, - readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer); - idThenWholePercentagesToPersonContainers = Stateless.MapLogic.GetIdThenWholePercentagesToPersonContainers(configuration, - skipCollection, - readOnlyPersonKeyFormattedToPersonContainer, - personKeyFormattedIdThenWholePercentagesCollection); - readOnlySkipCollection = new(skipCollection); - notMappedPersonContainers.AddRange(Stateless.MapLogic.GetNotMappedPersonContainers(configuration, - ticks, - personContainers, - readOnlyPersonKeyToCount)); - locationContainers.AddRange(Stateless.MapLogic.GetLocationContainers(maxDegreeOfParallelism, - propertyConfiguration, - configuration, - ticks, - personContainers, - readOnlySkipCollection, - records)); - int lossCount = records.Count - locationContainers.Count; - int unableToMatchCount = records.Count - personKeyFormattedIdThenWholePercentagesCollection.Count; - if (lossCount != 0 || unableToMatchCount != 0) - if (lossCount != 0 || unableToMatchCount != 0) - { } - if (!string.IsNullOrEmpty(a2PeopleContentDirectory) && configuration.LocationContainerDistanceTolerance is not null) - Stateless.RelationLogic.SaveMappedRelations(configuration, distance, a2PeopleContentDirectory, eDistanceContentDirectory, ticks, locationContainers, readOnlyPersonKeyFormattedToPersonContainer, readOnlyPersonKeyToPersonContainerCollection); - if (!string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) - throw new Exception($"{nameof(configuration.LocationContainerDebugDirectory)} is not IsNullOrEmpty!"); - } - _PersonKeyToCount = readOnlyPersonKeyToCount; - _EDistanceContentTicksDirectory = eDistanceContentTicksDirectory; - _SkipCollection = Stateless.MapLogic.ConvertSkip(skipCollection); - Stateless.MapLogic.CheckCollection(propertyConfiguration, rootDirectoryParent); - _IdThenWholePercentagesToPersonContainers = idThenWholePercentagesToPersonContainers; - _SkipNotSkipCollection = Stateless.MapLogic.ConvertSkipNotSkip(skipNotSkipCollection); - _IdToLocationContainers = Stateless.MapLogic.ConvertLocationContainers(locationContainers); - _NotMappedPersonContainers = new(notMappedPersonContainers.OrderByDescending(l => l.Key).ToArray()); - } - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); - return result; - } - - public ReadOnlyCollection GetLocationContainers(Item item) - { - LocationContainer[] results; - if (item.Property?.Id is null) - results = []; - else - { - List? locationContainers; - if (_IdToLocationContainers.TryGetValue(item.Property.Id.Value, out locationContainers)) - results = locationContainers.ToArray(); - else - results = []; - } - return results.AsReadOnly(); - } - - public ReadOnlyDictionary> GetPersonKeyToIds() - { - Dictionary> results = []; - long personKey; - const int zero = 0; - List? collection; - PersonBirthday personBirthday; - List shouldMove = []; - foreach (KeyValuePair>> idToCollection in _IdThenWholePercentagesToPersonContainers) - { - foreach (KeyValuePair> wholePercentagesToPersonContainers in idToCollection.Value) - { - foreach (PersonContainer personContainer in wholePercentagesToPersonContainers.Value) - { - if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) - continue; - if (personContainer.DisplayDirectoryName.Contains(@"{}\~\")) - shouldMove.Add(personContainer.DisplayDirectoryName); - personBirthday = personContainer.Birthdays[zero]; - personKey = personBirthday.Value.Ticks; - if (!results.TryGetValue(personKey, out collection)) - { - results.Add(personKey, []); - if (!results.TryGetValue(personKey, out collection)) - throw new Exception(); - } - if (collection.Contains(idToCollection.Key)) - continue; - collection.Add(idToCollection.Key); - } - } - } - if (shouldMove.Count > 0) - throw new Exception(string.Join(Environment.NewLine, shouldMove)); - return results.AsReadOnly(); - } - - (bool, ReadOnlyDictionary>?) Shared.Models.Methods.IMapLogic.GetWholePercentagesToPersonContainers(int id) - { - bool result = _IdThenWholePercentagesToPersonContainers.TryGetValue(id, out ReadOnlyDictionary>? wholePercentagesToPersonContainers); - return new(result, wholePercentagesToPersonContainers); - } - - public int UpdateMappingFromPerson(ReadOnlyDictionary>? wholePercentagesToPersonContainers, Mapping mapping) - { - int result = 0; - if (mapping.MappingFromLocation is not null) - { - if (wholePercentagesToPersonContainers is null) - result += 1; - else - { - ReadOnlyCollection? personContainers; - if (!wholePercentagesToPersonContainers.TryGetValue(mapping.MappingFromLocation.WholePercentages, out personContainers)) - result += 1; - else - { - const int zero = 0; - string mappingSegmentB; - PersonBirthday personBirthday; - foreach (PersonContainer personContainer in personContainers) - { - if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) - continue; - personBirthday = personContainer.Birthdays[zero]; - mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(_Ticks, personBirthday, personContainer.ApproximateYears, mapping.MappingFromItem); - mapping.UpdateMappingFromPerson(personContainer.ApproximateYears, personContainer.DisplayDirectoryName, personContainer.Key.Value, mappingSegmentB); - } - } - } - } - return result; - } - - private SaveContainer? GetMatchSaveContainer(string dFacesContentDirectory, string d2FacePartsContentDirectory, string directory, Mapping mapping) + public List GetSaveContainers(string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, Shared.Models.Methods.IDistanceLimits distanceLimits, ReadOnlyDictionary onlyOne, ReadOnlyDictionary personKeyToPersonContainer) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); - SaveContainer? result; - if (mapping.MappingFromLocation is null) - result = null; - else + List results = []; + string by; + Record record; + string segmentB; + bool isBySorting; + string checkFile; + string? directory; + string shortcutFile; + string facesDirectory; + bool isCounterPersonYear; + string facePartsDirectory; + FileHolder? faceFileHolder; + SaveContainer? saveContainer; + string? displayDirectoryName; + FileHolder? resizedFileHolder; + int? useFiltersCounter = null; + string resizeContentDirectory; + FileHolder? facePartsFileHolder; + FileHolder? hiddenFaceFileHolder; + LocationContainer locationContainer; + bool sortingContainersAny = onlyOne.Count > 0; + string cContentDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); + string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title); + string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); + foreach (KeyValuePair keyValuePair in onlyOne) { - string checkFile; - string facesDirectory = Stateless.MapLogic.GetFacesDirectory(_PropertyConfiguration, dFacesContentDirectory, mapping.FilePath, mapping.MappingFromItem); - FileHolder faceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{_Configuration.FacesFileNameExtension}")); - if (!faceFileHolder.Exists) - result = null; - else + if (_Configuration.SaveIndividually) + break; + locationContainer = keyValuePair.Value; + if (locationContainer.LengthPermyriad is null || locationContainer.LengthSource is null) + continue; + segmentB = locationContainer.LengthPermyriad.Value.ToString().PadLeft(2, '0')[..2]; + displayDirectoryName = GetDisplayDirectoryName(locationContainer.DisplayDirectoryName, locationContainer, personKeyToPersonContainer); + isCounterPersonYear = locationContainer.PersonKey is not null && IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKey.Value); + (by, _, isBySorting) = Stateless.MapLogic.Get(useFiltersCounter, _Configuration.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKey, displayDirectoryName); + record = Get(_Configuration, by, locationContainer, displayDirectoryName, segmentB); + if (string.IsNullOrEmpty(record.Directory) || string.IsNullOrEmpty(record.PersonDirectory)) + continue; + directory = record.Directory; + if (!string.IsNullOrEmpty(record.DebugDirectory)) + results.Add(new(record.DebugDirectory)); + if (locationContainer.PersonKey is null) { - string shortcutFile = string.Empty; - string facePartsDirectory = Stateless.MapLogic.GetFacePartsDirectory(_PropertyConfiguration, d2FacePartsContentDirectory, mapping.FilePath, mapping.MappingFromItem); - checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}"); - FileHolder hiddenFaceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{_Configuration.FacesHiddenFileNameExtension}")); - FileHolder facePartsFileHolder = IFileHolder.Get(Path.Combine(facePartsDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{_Configuration.FacePartsFileNameExtension}")); - result = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, mapping.MappingFromItem.ResizedFileHolder, shortcutFile); + if (!_Configuration.SaveSortingWithoutPerson) + throw new NotSupportedException(); + if (record.Ticks is null) + continue; + } + results.Add(new(record.PersonDirectory)); + facesDirectory = locationContainer.LengthSource.DirectoryFullPath; + faceFileHolder = IFileHolder.Get(locationContainer.LengthSource.FullName); + checkFile = Path.Combine(directory, $"{locationContainer.LengthSource.Name}"); + shortcutFile = Path.Combine(record.PersonDirectory, $"{locationContainer.LengthSource.Name}.lnk"); + resizeContentDirectory = Stateless.MapLogic.GetResizeContentDirectory(_PropertyConfiguration, cContentDirectory, locationContainer.LengthSource); + facePartsDirectory = Stateless.MapLogic.GetFacePartsDirectoryX(_PropertyConfiguration, d2FacePartsContentDirectory, locationContainer.LengthSource); + hiddenFaceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{_Configuration.FacesHiddenFileNameExtension}")); + facePartsFileHolder = IFileHolder.Get(Path.Combine(facePartsDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{_Configuration.FacePartsFileNameExtension}")); + resizedFileHolder = IFileHolder.Get(Path.Combine(resizeContentDirectory, $"{locationContainer.LengthSource.FileNameFirstSegment}{Path.GetExtension(locationContainer.LengthSource.NameWithoutExtension)}")); + saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, resizedFileHolder, shortcutFile); + results.Add(saveContainer); + } + return results; + } + + private string? GetDisplayDirectoryName(string? displayDirectoryName, LocationContainer locationContainer, ReadOnlyDictionary personKeyToPersonContainer) + { + string? result = displayDirectoryName; + if (personKeyToPersonContainer.Count != 0) + { + ReadOnlyDictionary>? wholePercentagesToPersonContainers = GetWholePercentagesToPersonContainers(locationContainer.Id); + if (wholePercentagesToPersonContainers is not null) + { + foreach (KeyValuePair> keyValuePair in wholePercentagesToPersonContainers) + { + if (keyValuePair.Key != locationContainer.WholePercentages) + continue; + if (keyValuePair.Value.Count != 1) + continue; + result = keyValuePair.Value[0].DisplayDirectoryName; + } } } return result; } + private Record Get(Configuration configuration, bool saveIndividually, int padLeft, string by, Mapping question) + { + long? ticks; + string? directory; + string? debugDirectory; + string? personDirectory; + if (question.MappingFromPerson is null) + { + debugDirectory = null; + (ticks, directory) = GetDirectory(configuration, saveIndividually, padLeft, question.SegmentC, by, question.MappingFromItem); + personDirectory = directory is null ? null : Path.Combine(directory, $"X+{ticks}"); + } + else + { + ticks = null; + string personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, question.MappingFromPerson.PersonKey); + debugDirectory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, question.MappingFromPerson.DisplayDirectoryName); + if (string.IsNullOrEmpty(question.SegmentC)) + directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, question.MappingFromPerson.SegmentB); + else if (!saveIndividually) + directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, $"Z-{question.MappingFromPerson.SegmentB}-{question.SegmentC}"); + else + directory = Path.Combine(_EDistanceContentTicksDirectory, by, question.SegmentC.PadLeft(padLeft, '0'), personKeyFormatted, question.MappingFromPerson.SegmentB); + personDirectory = Path.Combine(directory, question.MappingFromPerson.DisplayDirectoryName, "lnk"); + } + return new(debugDirectory, directory, ticks, personDirectory); + } + + private Record Get(Configuration configuration, string by, LocationContainer locationContainer, string? displayDirectoryName, string segmentB) + { + long? ticks; + string? directory; + string? debugDirectory; + string? personDirectory; + if (locationContainer.PersonKey is null || string.IsNullOrEmpty(displayDirectoryName)) + { + debugDirectory = null; + (ticks, directory) = GetDirectory(configuration, by, locationContainer, segmentB); + personDirectory = directory is null ? null : Path.Combine(directory, $"X+{ticks}"); + } + else + { + ticks = null; + string personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, locationContainer.PersonKey.Value); + debugDirectory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, displayDirectoryName); + directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, segmentB); + personDirectory = Path.Combine(directory, displayDirectoryName, "lnk"); + } + return new(debugDirectory, directory, ticks, personDirectory); + } + private (long?, string?) GetDirectory(Configuration configuration, string by, LocationContainer _, string segmentB) { long? ticks = null; @@ -513,79 +1171,18 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return (ticks, directory); } - private static bool PreAndPostContinue(Configuration configuration, Mapping mapping, Mapping keyMapping) + public void SaveMapped(string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageMappingCollection, ReadOnlyDictionary> idToWholePercentagesToMapping) { - bool result = true; - if (result && mapping.MappingFromFilterPre.InSkipCollection is not null && mapping.MappingFromFilterPre.InSkipCollection.Value) - result = false; - if (result && mapping.MappingFromFilterPre.IsFocusModel is not null && !mapping.MappingFromFilterPre.IsFocusModel.Value) - result = false; - if (result && mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !mapping.MappingFromFilterPre.IsFocusRelativePath.Value) - result = false; - if (result && mapping.MappingFromFilterPost.InSkipCollection is not null && mapping.MappingFromFilterPost.InSkipCollection.Value) - result = false; - if (result && mapping.MappingFromFilterPost.IsFocusPerson is not null && !mapping.MappingFromFilterPost.IsFocusPerson.Value) - result = false; - if (result && keyMapping.MappingFromFilterPost.CanReMap is not null && !configuration.ReMap) - result = false; - if (result && keyMapping.MappingFromFilterPost.CanReMap is not null && (!keyMapping.MappingFromFilterPost.CanReMap.Value || (mapping.MappingFromPerson is not null && IPerson.IsDefaultName(mapping.MappingFromPerson.DisplayDirectoryName)))) - result = false; - if (result && keyMapping.MappingFromFilterPost.InSkipCollection is not null && keyMapping.MappingFromFilterPost.InSkipCollection.Value) - result = false; - if (result && keyMapping.MappingFromFilterPost.IsFocusPerson is not null && keyMapping.MappingFromFilterPost.IsFocusPerson.Value) - result = false; - return result; - } - - private Record Get(Configuration configuration, string by, LocationContainer locationContainer, string? displayDirectoryName, string segmentB) - { - long? ticks; - string? directory; - string? debugDirectory; - string? personDirectory; - if (locationContainer.PersonKey is null || string.IsNullOrEmpty(displayDirectoryName)) - { - debugDirectory = null; - (ticks, directory) = GetDirectory(configuration, by, locationContainer, segmentB); - personDirectory = directory is null ? null : Path.Combine(directory, $"X+{ticks}"); - } - else - { - ticks = null; - string personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, locationContainer.PersonKey.Value); - debugDirectory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, displayDirectoryName); - directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, segmentB); - personDirectory = Path.Combine(directory, displayDirectoryName, "lnk"); - } - return new(debugDirectory, directory, ticks, personDirectory); - } - - private Record Get(Configuration configuration, bool saveIndividually, int padLeft, string by, Mapping question) - { - long? ticks; - string? directory; - string? debugDirectory; - string? personDirectory; - if (question.MappingFromPerson is null) - { - debugDirectory = null; - (ticks, directory) = GetDirectory(configuration, saveIndividually, padLeft, question.SegmentC, by, question.MappingFromItem); - personDirectory = directory is null ? null : Path.Combine(directory, $"X+{ticks}"); - } - else - { - ticks = null; - string personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, question.MappingFromPerson.PersonKey); - debugDirectory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, question.MappingFromPerson.DisplayDirectoryName); - if (string.IsNullOrEmpty(question.SegmentC)) - directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, question.MappingFromPerson.SegmentB); - else if (!saveIndividually) - directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, $"Z-{question.MappingFromPerson.SegmentB}-{question.SegmentC}"); - else - directory = Path.Combine(_EDistanceContentTicksDirectory, by, question.SegmentC.PadLeft(padLeft, '0'), personKeyFormatted, question.MappingFromPerson.SegmentB); - personDirectory = Path.Combine(directory, question.MappingFromPerson.DisplayDirectoryName, "lnk"); - } - return new(debugDirectory, directory, ticks, personDirectory); + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + int? updated = null; + bool saveMapped = true; + int? useFiltersCounter = null; + string mappingDirectory = Path.Combine(_EDistanceContentTicksDirectory, nameof(Shared.Models.Stateless.IMapLogic.Mapping)); + List saveContainers = GetSaveContainers(dFacesContentDirectory, d2FacePartsContentDirectory, distinctValidImageMappingCollection, idToWholePercentagesToMapping, personKeyToIds, useFiltersCounter, saveMapped, sortingContainersAny: true, saveIndividually: false); + SaveContainers(updated, saveContainers); + if (!string.IsNullOrEmpty(_EDistanceContentTicksDirectory) && Directory.Exists(mappingDirectory)) + Stateless.MapLogic.SaveMappingShortcuts(mappingDirectory); } private List GetSaveContainers(string dFacesContentDirectory, string d2FacePartsContentDirectory, ReadOnlyCollection distinctValidImageMappingCollection, ReadOnlyDictionary> idToWholePercentagesToMapping, ReadOnlyDictionary> personKeyToIds, int? useFiltersCounter, bool saveMapped, bool saveIndividually, bool sortingContainersAny) @@ -707,208 +1304,30 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return results; } - public void SaveMapped(string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageMappingCollection, ReadOnlyDictionary> idToWholePercentagesToMapping) + private static bool PreAndPostContinue(Configuration configuration, Mapping mapping, Mapping keyMapping) { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - int? updated = null; - bool saveMapped = true; - int? useFiltersCounter = null; - string mappingDirectory = Path.Combine(_EDistanceContentTicksDirectory, nameof(Shared.Models.Stateless.IMapLogic.Mapping)); - List saveContainers = GetSaveContainers(dFacesContentDirectory, d2FacePartsContentDirectory, distinctValidImageMappingCollection, idToWholePercentagesToMapping, personKeyToIds, useFiltersCounter, saveMapped, sortingContainersAny: true, saveIndividually: false); - SaveContainers(updated, saveContainers); - if (!string.IsNullOrEmpty(_EDistanceContentTicksDirectory) && Directory.Exists(mappingDirectory)) - Stateless.MapLogic.SaveMappingShortcuts(mappingDirectory); - } - - public List GetSortingCollection(int i, Face face, FaceDistance faceDistanceEncoding, List faceDistanceLengths) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - List results = []; - Sorting sorting; - FaceDistance faceDistanceLength; - for (int j = 0; j < faceDistanceLengths.Count; j++) - { - if (faceDistanceEncoding.WholePercentages is null) - throw new NotSupportedException(); - if (face.Mapping?.MappingFromFilterPost is null) - throw new NotSupportedException(); - if (j == i) - continue; - if (face.Mapping.MappingFromFilterPre.InSkipCollection is not null && face.Mapping.MappingFromFilterPre.InSkipCollection.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); - if (face.Mapping.MappingFromFilterPre.IsFocusModel is not null && !face.Mapping.MappingFromFilterPre.IsFocusModel.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); - if (face.Mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !face.Mapping.MappingFromFilterPre.IsFocusRelativePath.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); - if (face.Mapping.MappingFromFilterPost.InSkipCollection is not null && face.Mapping.MappingFromFilterPost.InSkipCollection.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); - if (face.Mapping.MappingFromFilterPost.IsFocusPerson is not null && !face.Mapping.MappingFromFilterPost.IsFocusPerson.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); - if (face.Mapping.MappingFromFilterPost.InSkipCollection is not null && face.Mapping.MappingFromFilterPost.InSkipCollection.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); - faceDistanceLength = faceDistanceLengths[j]; - if (faceDistanceLength.WholePercentages is null || faceDistanceLength.Length is null) - throw new NotSupportedException(); - if (faceDistanceLength.Length == 0) - continue; - if (faceDistanceLength.Id == faceDistanceEncoding.Id) - continue; - if (faceDistanceLength.MappingFromFilterPost is null) - throw new NotSupportedException(); - if (faceDistanceLength.MappingFromFilterPost.CanReMap is not null && !_Configuration.ReMap) - continue; - if (faceDistanceLength.MappingFromFilterPost.CanReMap is not null && !faceDistanceLength.MappingFromFilterPost.CanReMap.Value) - continue; - if (faceDistanceLength.MappingFromFilterPost.InSkipCollection is not null && faceDistanceLength.MappingFromFilterPost.InSkipCollection.Value) - continue; - if (faceDistanceLength.MappingFromFilterPost.IsFocusPerson is not null && !faceDistanceLength.MappingFromFilterPost.IsFocusPerson.Value) - continue; - sorting = ISorting.Get(_Configuration.FaceDistancePermyriad, faceDistanceEncoding, faceDistanceLength); - if (sorting.DistancePermyriad == 0) - continue; - if (sorting.Id == faceDistanceEncoding.Id) - { - if (sorting.WholePercentages == faceDistanceEncoding.WholePercentages.Value) - continue; - continue; - } - results.Add(sorting); - } - return results; - } - - public int UpdateFromSortingContainers(string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping, ReadOnlyCollection sortingContainers) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - int result = 0; - string key; - string segmentB; - string? segmentC; - string personKeyFormatted; - MappingFromPerson mappingFromPerson; - Dictionary keyToCount = []; - foreach (SortingContainer sortingContainer in sortingContainers) - { - if (sortingContainer.Question is null) - continue; - if (sortingContainer.Source.MappingFromPerson is null) - { - sortingContainer.Question.UpdateMappingFromUnknownPerson(_Configuration.SaveIndividually, sortingContainer); - result += 1; - } - else - { - mappingFromPerson = sortingContainer.Source.MappingFromPerson; - personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mappingFromPerson.PersonKey); - segmentB = IMapLogic.GetDecade(sortingContainer.Question.MappingFromItem); - key = string.Concat(personKeyFormatted, '\t', segmentB); - if (!keyToCount.ContainsKey(key)) - keyToCount.Add(key, new()); - _ = keyToCount.TryAdd(key, 0); - keyToCount[key]++; - if (!_Configuration.SaveIndividually && keyToCount[key] <= _Configuration.SortingMaximumPerKey) - segmentC = null; - else - segmentC = sortingContainer.Sorting.DistancePermyriad.ToString(); - sortingContainer.Question.UpdateMappingFromPerson(mappingFromPerson.ApproximateYears, mappingFromPerson.DisplayDirectoryName, mappingFromPerson.PersonKey, segmentB, segmentC, sortingContainer); - result += 1; - } - } + bool result = true; + if (result && mapping.MappingFromFilterPre.InSkipCollection is not null && mapping.MappingFromFilterPre.InSkipCollection.Value) + result = false; + if (result && mapping.MappingFromFilterPre.IsFocusModel is not null && !mapping.MappingFromFilterPre.IsFocusModel.Value) + result = false; + if (result && mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !mapping.MappingFromFilterPre.IsFocusRelativePath.Value) + result = false; + if (result && mapping.MappingFromFilterPost.InSkipCollection is not null && mapping.MappingFromFilterPost.InSkipCollection.Value) + result = false; + if (result && mapping.MappingFromFilterPost.IsFocusPerson is not null && !mapping.MappingFromFilterPost.IsFocusPerson.Value) + result = false; + if (result && keyMapping.MappingFromFilterPost.CanReMap is not null && !configuration.ReMap) + result = false; + if (result && keyMapping.MappingFromFilterPost.CanReMap is not null && (!keyMapping.MappingFromFilterPost.CanReMap.Value || (mapping.MappingFromPerson is not null && IPerson.IsDefaultName(mapping.MappingFromPerson.DisplayDirectoryName)))) + result = false; + if (result && keyMapping.MappingFromFilterPost.InSkipCollection is not null && keyMapping.MappingFromFilterPost.InSkipCollection.Value) + result = false; + if (result && keyMapping.MappingFromFilterPost.IsFocusPerson is not null && keyMapping.MappingFromFilterPost.IsFocusPerson.Value) + result = false; return result; } - private string? GetDisplayDirectoryName(string? displayDirectoryName, LocationContainer locationContainer, ReadOnlyDictionary personKeyToPersonContainer) - { - string? result = displayDirectoryName; - if (personKeyToPersonContainer.Count != 0) - { - ReadOnlyDictionary>? wholePercentagesToPersonContainers = GetWholePercentagesToPersonContainers(locationContainer.Id); - if (wholePercentagesToPersonContainers is not null) - { - foreach (KeyValuePair> keyValuePair in wholePercentagesToPersonContainers) - { - if (keyValuePair.Key != locationContainer.WholePercentages) - continue; - if (keyValuePair.Value.Count != 1) - continue; - result = keyValuePair.Value[0].DisplayDirectoryName; - } - } - } - return result; - } - - public List GetSaveContainers(string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, Shared.Models.Methods.IDistanceLimits distanceLimits, ReadOnlyDictionary onlyOne, ReadOnlyDictionary personKeyToPersonContainer) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - List results = []; - string by; - Record record; - string segmentB; - bool isBySorting; - string checkFile; - string? directory; - string shortcutFile; - string facesDirectory; - bool isCounterPersonYear; - string facePartsDirectory; - FileHolder? faceFileHolder; - SaveContainer? saveContainer; - string? displayDirectoryName; - FileHolder? resizedFileHolder; - int? useFiltersCounter = null; - string resizeContentDirectory; - FileHolder? facePartsFileHolder; - FileHolder? hiddenFaceFileHolder; - LocationContainer locationContainer; - bool sortingContainersAny = onlyOne.Count > 0; - string cContentDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); - string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title); - string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); - foreach (KeyValuePair keyValuePair in onlyOne) - { - if (_Configuration.SaveIndividually) - break; - locationContainer = keyValuePair.Value; - if (locationContainer.LengthPermyriad is null || locationContainer.LengthSource is null) - continue; - segmentB = locationContainer.LengthPermyriad.Value.ToString().PadLeft(2, '0')[..2]; - displayDirectoryName = GetDisplayDirectoryName(locationContainer.DisplayDirectoryName, locationContainer, personKeyToPersonContainer); - isCounterPersonYear = locationContainer.PersonKey is not null && IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKey.Value); - (by, _, isBySorting) = Stateless.MapLogic.Get(useFiltersCounter, _Configuration.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKey, displayDirectoryName); - record = Get(_Configuration, by, locationContainer, displayDirectoryName, segmentB); - if (string.IsNullOrEmpty(record.Directory) || string.IsNullOrEmpty(record.PersonDirectory)) - continue; - directory = record.Directory; - if (!string.IsNullOrEmpty(record.DebugDirectory)) - results.Add(new(record.DebugDirectory)); - if (locationContainer.PersonKey is null) - { - if (!_Configuration.SaveSortingWithoutPerson) - throw new NotSupportedException(); - if (record.Ticks is null) - continue; - } - results.Add(new(record.PersonDirectory)); - facesDirectory = locationContainer.LengthSource.DirectoryFullPath; - faceFileHolder = IFileHolder.Get(locationContainer.LengthSource.FullName); - checkFile = Path.Combine(directory, $"{locationContainer.LengthSource.Name}"); - shortcutFile = Path.Combine(record.PersonDirectory, $"{locationContainer.LengthSource.Name}.lnk"); - resizeContentDirectory = Stateless.MapLogic.GetResizeContentDirectory(_PropertyConfiguration, cContentDirectory, locationContainer.LengthSource); - facePartsDirectory = Stateless.MapLogic.GetFacePartsDirectoryX(_PropertyConfiguration, d2FacePartsContentDirectory, locationContainer.LengthSource); - hiddenFaceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{_Configuration.FacesHiddenFileNameExtension}")); - facePartsFileHolder = IFileHolder.Get(Path.Combine(facePartsDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{_Configuration.FacePartsFileNameExtension}")); - resizedFileHolder = IFileHolder.Get(Path.Combine(resizeContentDirectory, $"{locationContainer.LengthSource.FileNameFirstSegment}{Path.GetExtension(locationContainer.LengthSource.NameWithoutExtension)}")); - saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, resizedFileHolder, shortcutFile); - results.Add(saveContainer); - } - return results; - } - public List GetSaveContainers(string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping, Shared.Models.Methods.IDistanceLimits distanceLimits, int? useFiltersCounter, ReadOnlyCollection sortingContainers) { if (_Configuration is null) @@ -1005,6 +1424,33 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return results; } + private SaveContainer? GetMatchSaveContainer(string dFacesContentDirectory, string d2FacePartsContentDirectory, string directory, Mapping mapping) + { + if (_Configuration is null) + throw new NullReferenceException(nameof(_Configuration)); + SaveContainer? result; + if (mapping.MappingFromLocation is null) + result = null; + else + { + string checkFile; + string facesDirectory = Stateless.MapLogic.GetFacesDirectory(_PropertyConfiguration, dFacesContentDirectory, mapping.FilePath, mapping.MappingFromItem); + FileHolder faceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{_Configuration.FacesFileNameExtension}")); + if (!faceFileHolder.Exists) + result = null; + else + { + string shortcutFile = string.Empty; + string facePartsDirectory = Stateless.MapLogic.GetFacePartsDirectory(_PropertyConfiguration, d2FacePartsContentDirectory, mapping.FilePath, mapping.MappingFromItem); + checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}"); + FileHolder hiddenFaceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{_Configuration.FacesHiddenFileNameExtension}")); + FileHolder facePartsFileHolder = IFileHolder.Get(Path.Combine(facePartsDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{_Configuration.FacePartsFileNameExtension}")); + result = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, mapping.MappingFromItem.ResizedFileHolder, shortcutFile); + } + } + return result; + } + public ReadOnlyCollection GetFilterSortingContainers(string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping, Shared.Models.Methods.IDistanceLimits distanceLimits, ReadOnlyCollection sortingContainers) { if (_Configuration is null) @@ -1092,450 +1538,4 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return results.AsReadOnly(); } - private (string, PersonBirthday?) GetPersonBirthday(string[] directoryNames) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - PersonBirthday? personBirthday = null; - string personKeyFormatted = string.Empty; - foreach (string directoryName in directoryNames) - { - personBirthday = IPersonBirthday.GetPersonBirthday(_Configuration.PersonBirthdayFormat, directoryName); - if (personBirthday is not null) - { - personKeyFormatted = directoryName; - break; - } - } - return new(personKeyFormatted, personBirthday); - } - - private (string, PersonBirthday?) GetPersonBirthday(string windowsShortcutPath) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - string[] directoryNames = IPath.GetDirectoryNames(windowsShortcutPath); - (string personKeyFormatted, PersonBirthday? personBirthday) = GetPersonBirthday(directoryNames); - if (personBirthday is null) - { - string[] directories = Directory.GetDirectories(windowsShortcutPath, "*", SearchOption.TopDirectoryOnly); - foreach (string directory in directories) - { - directoryNames = IPath.GetDirectoryNames(directory); - (personKeyFormatted, personBirthday) = GetPersonBirthday(directoryNames); - if (personBirthday is not null) - break; - } - } - return new(personKeyFormatted, personBirthday); - } - - private List GetPersonKeyFormattedCollection(string[] jLinks, string a2PeopleContentDirectory, ReadOnlyCollection personContainers, ReadOnlyDictionary> personKeyToIds) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - List results = []; - string[] files; - List? ids; - string checkDirectory; - string[] checkDirectories; - string personKeyFormatted; - PersonContainer[] matches; - PersonBirthday? personBirthday; - string fileNameWithoutExtension; - WindowsShortcut windowsShortcut; - List<(long, string)> collection = []; - foreach (string directoryName in jLinks) - { - checkDirectory = Path.Combine(a2PeopleContentDirectory, directoryName); - if (!Directory.Exists(checkDirectory)) - continue; - checkDirectories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly); - files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly); - foreach (string file in files) - { - collection.Clear(); - windowsShortcut = WindowsShortcut.Load(file); - fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - if (string.IsNullOrEmpty(windowsShortcut.Path)) - continue; - if (!Directory.Exists(windowsShortcut.Path)) - { - if (files.Length != checkDirectories.Length) - throw new NotSupportedException(fileNameWithoutExtension); - continue; - } - (personKeyFormatted, personBirthday) = GetPersonBirthday(windowsShortcut.Path); - if (personBirthday is null) - throw new NotSupportedException(fileNameWithoutExtension); - if (!personKeyToIds.TryGetValue(personBirthday.Value.Ticks, out ids)) - collection.Add(new(personBirthday.Value.Ticks, Path.Combine(checkDirectory, personKeyFormatted, fileNameWithoutExtension))); - else - collection.Add(new(personBirthday.Value.Ticks, Path.Combine(checkDirectory, personKeyFormatted, fileNameWithoutExtension, $"{ids.Count} Face(s)"))); - foreach ((long personKey, string displayDirectoryName) in collection) - { - matches = (from l in personContainers where l.Key == personKey select l).ToArray(); - if (matches.Length == 0) - continue; - personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personKey); - if (!displayDirectoryName.Contains(personKeyFormatted)) - continue; - if (!Directory.Exists(displayDirectoryName)) - _ = Directory.CreateDirectory(displayDirectoryName); - results.Add(personKeyFormatted); - } - } - } - return results; - } - - private FilteredOriginalImage[] GetCollectionForSaveFilteredOriginalImagesFromJLinks(string[] jLinks, string a2PeopleContentDirectory, ReadOnlyCollection personContainers, ReadOnlyCollection distinctValidImageMappingCollection, ReadOnlyDictionary> personKeyToIds) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - FilteredOriginalImage[] results; - int count = 0; - int group = 65; - string checkFile; - string directory; - string? directoryName; - string personDirectory; - string personKeyFormatted; - List distinctCollection = []; - bool usePersonKeyAndDeterministicHashCodeKey = false; - List filteredOriginalImages = []; - List personKeyFormattedCollection = GetPersonKeyFormattedCollection(jLinks, a2PeopleContentDirectory, personContainers, personKeyToIds); - foreach (Mapping mapping in distinctValidImageMappingCollection) - { - if (distinctCollection.Contains(mapping.MappingFromItem.Id)) - continue; - directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); - if (directoryName is null) - throw new NotSupportedException(); - if (mapping.By is null or Shared.Models.Stateless.IMapLogic.Sorting) - continue; - if (mapping.MappingFromPerson is null || mapping.MappingFromLocation is null) - continue; - if (string.IsNullOrEmpty(mapping.MappingFromPerson.SegmentB)) - throw new NotSupportedException(); - if (string.IsNullOrEmpty(mapping.MappingFromPerson.DisplayDirectoryName)) - throw new NotSupportedException(); - personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mapping.MappingFromPerson.PersonKey); - if (personKeyFormatted == "1501-04-10_00") - continue; - if (!personKeyFormattedCollection.Contains(personKeyFormatted)) - continue; - if (!usePersonKeyAndDeterministicHashCodeKey) - { - if (count > 499) - { - count = 0; - group += 1; - } - directory = Path.Combine(_EDistanceContentTicksDirectory, ((char)group).ToString()); - personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); - checkFile = Path.Combine(directory, $"{mapping.MappingFromItem.Id}{mapping.MappingFromItem.FilePath.ExtensionLowered}"); - } - else - { - directory = Path.Combine(_EDistanceContentTicksDirectory, "Images", personKeyFormatted); - personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); - checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}"); - } - if (mapping is null) - continue; - if (mapping.MappingFromPerson.ApproximateYears is null) - filteredOriginalImages.Add(new(mapping.MappingFromItem.Id, mapping.MappingFromItem.FilePath, -1, personKeyFormatted, directory, personDirectory, checkFile)); - else - filteredOriginalImages.Add(new(mapping.MappingFromItem.Id, mapping.MappingFromItem.FilePath, mapping.MappingFromPerson.ApproximateYears.Value, personKeyFormatted, directory, personDirectory, checkFile)); - distinctCollection.Add(mapping.MappingFromItem.Id); - count += 1; - } - results = (from l in filteredOriginalImages orderby l.ApproximateYears descending, l.PersonKeyFormatted descending select l).ToArray(); - return results; - } - - public void SaveFilteredOriginalImagesFromJLinks(string[] jLinks, ReadOnlyCollection personContainers, string a2PeopleContentDirectory, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageMappingCollection) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - int? updated = null; - FileHolder fileHolder; - SaveContainer? saveContainer; - List saveContainers = []; - FilteredOriginalImage[] filteredOriginalImages = GetCollectionForSaveFilteredOriginalImagesFromJLinks(jLinks, a2PeopleContentDirectory, personContainers, distinctValidImageMappingCollection, personKeyToIds); - foreach (FilteredOriginalImage filteredOriginalImage in filteredOriginalImages) - { - fileHolder = FileHolder.Get(filteredOriginalImage.FilePath); - saveContainer = new(filteredOriginalImage.PersonDirectory); - saveContainers.Add(saveContainer); - saveContainer = new(fileHolder, filteredOriginalImage.CheckFile, filteredOriginalImage.Directory); - saveContainers.Add(saveContainer); - } - SaveContainers(updated, saveContainers); - } - - public void SaveShortcutsForOutputResolutionsPreMapLogic(string eDistanceContentDirectory, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageMappingCollection) - { - string hiddenFile; - WindowsShortcut windowsShortcut; - List collection = []; - collection = GetCollectionForSaveShortcutsForOutputResolutionsPreMapLogic(eDistanceContentDirectory, personKeyToIds, distinctValidImageMappingCollection); - string[] distinctDirectories = (from l in collection select l.Directory).Distinct().ToArray(); - foreach (string directory in distinctDirectories) - { - if (string.IsNullOrEmpty(directory)) - continue; - if (!Directory.Exists(directory)) - _ = Directory.CreateDirectory(directory); - } - foreach (SaveShortcutsForOutputResolutions s in collection) - { - hiddenFile = $"{s.FileName}.lvs"; - if (File.Exists(hiddenFile)) - continue; - if (s.Description is not null) - { - File.WriteAllLines(hiddenFile, [s.FullName, s.Description]); - File.SetAttributes(hiddenFile, FileAttributes.Hidden); - File.SetLastWriteTime(hiddenFile, s.DateTime); - } - if (File.Exists(s.FileName)) - continue; - try - { - windowsShortcut = new() { Path = s.FullName, Description = s.Description }; - windowsShortcut.Save(s.FileName); - windowsShortcut.Dispose(); - File.SetLastWriteTime(s.FileName, s.DateTime); - } - catch (Exception) - { } - } - } - - private (List<(string, DateTime[])>, List) GetCollectionForSaveShortcutsForOutputResolutionsDuringMapLogic(ReadOnlyDictionary> personKeyToIds, string dFacesContentDirectory, ReadOnlyCollection validImageItems, ReadOnlyCollection distinctValidImageMappingCollection) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - List? ids; - string fileName; - string fullName; - string directory; - DateTime dateTime; - string facesDirectory; - string? directoryName; - string personDirectory; - string personKeyFormatted; - List distinct = []; - List collection = []; - List<(string, DateTime[])> directoriesAndDateTimes = []; - foreach (Item item in validImageItems) - { - if (item.ResizedFileHolder is null) - continue; - if (item.Faces.Any(l => l.FaceEncoding is not null)) - continue; - foreach (Face face in item.Faces) - { - if (face.Mapping is null) - continue; - directoryName = Path.GetDirectoryName(face.Mapping.MappingFromItem.RelativePath); - if (directoryName is null) - throw new NotSupportedException(); - if (item.ResizedFileHolder?.DirectoryFullPath is null || !item.ResizedFileHolder.Exists) - continue; - directory = Path.Combine(item.ResizedFileHolder.DirectoryFullPath, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", _PropertyConfiguration.ResultAllInOne); - personDirectory = Path.Combine(directory, "No Faces"); - fileName = Path.Combine(personDirectory, $"{item.ResizedFileHolder.Name}.lnk"); - collection.Add(new(item.ResizedFileHolder.FullName, personDirectory, new(item.FilePath.LastWriteTicks), fileName, face.Mapping.MappingFromItem.Id.ToString(), MakeAllHidden: false)); - if (face.Mapping.MappingFromItem.ContainerDateTimes.Length > 0 && !distinct.Contains(item.ResizedFileHolder.DirectoryFullPath)) - { - distinct.Add(item.ResizedFileHolder.DirectoryFullPath); - directoriesAndDateTimes.Add(new(item.ResizedFileHolder.DirectoryFullPath, face.Mapping.MappingFromItem.ContainerDateTimes)); - } - } - } - foreach (Mapping mapping in distinctValidImageMappingCollection) - { - directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); - if (directoryName is null) - throw new NotSupportedException(); - if (mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath is null || !mapping.MappingFromItem.ResizedFileHolder.Exists) - continue; - dateTime = mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(); - if (mapping.By is null or Shared.Models.Stateless.IMapLogic.Sorting || mapping.MappingFromPerson?.ApproximateYears is null) - { - if (mapping.MappingFromItem.ContainerDateTimes.Length > 0 && !distinct.Contains(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath)) - { - distinct.Add(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath); - directoriesAndDateTimes.Add(new(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath, mapping.MappingFromItem.ContainerDateTimes)); - } - directory = Path.Combine(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", _PropertyConfiguration.ResultAllInOne); - personDirectory = Path.Combine(directory, "Unknown"); - fileName = Path.Combine(personDirectory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk"); - collection.Add(new(mapping.MappingFromItem.ResizedFileHolder.FullName, personDirectory, dateTime, fileName, mapping.MappingFromLocation?.DeterministicHashCodeKey, MakeAllHidden: false)); - facesDirectory = Stateless.MapLogic.GetFacesDirectory(_PropertyConfiguration, dFacesContentDirectory, mapping.FilePath, mapping.MappingFromItem); - if (mapping.MappingFromLocation is null) - continue; - fullName = Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{_Configuration.FacesFileNameExtension}"); - fileName = Path.Combine(personDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ResizedFileHolder.ExtensionLowered}{_Configuration.FacesFileNameExtension}.lnk"); - collection.Add(new(fullName, personDirectory, dateTime, fileName, mapping.MappingFromLocation.DeterministicHashCodeKey, MakeAllHidden: true)); - } - else - { - if (string.IsNullOrEmpty(mapping.MappingFromPerson.SegmentB)) - throw new NotSupportedException(); - if (string.IsNullOrEmpty(mapping.MappingFromPerson.DisplayDirectoryName)) - throw new NotSupportedException(); - personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mapping.MappingFromPerson.PersonKey); - if (mapping.MappingFromItem.ContainerDateTimes.Length > 0 && !distinct.Contains(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath)) - { - distinct.Add(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath); - directoriesAndDateTimes.Add(new(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath, mapping.MappingFromItem.ContainerDateTimes)); - } - directory = Path.Combine(mapping.MappingFromItem.ResizedFileHolder.DirectoryFullPath, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", personKeyFormatted); - if (!personKeyToIds.TryGetValue(mapping.MappingFromPerson.PersonKey, out ids)) - personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); - else - personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName, $"{ids.Count} Face(s)"); - fileName = Path.Combine(directory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk"); - collection.Add(new(mapping.MappingFromItem.ResizedFileHolder.FullName, personDirectory, dateTime, fileName, mapping.MappingFromLocation?.DeterministicHashCodeKey, MakeAllHidden: false)); - } - } - return new(directoriesAndDateTimes, collection); - } - - public void SaveShortcutsForOutputResolutionsDuringMapLogic(ReadOnlyCollection containers, ReadOnlyDictionary> personKeyToIds, string dFacesContentDirectory, ReadOnlyCollection distinctValidImageMappingCollection) - { - if (_Configuration is null) - throw new NullReferenceException(nameof(_Configuration)); - WindowsShortcut windowsShortcut; - List<(string, DateTime[])> directoriesAndDateTimes; - List collection; - ReadOnlyCollection validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_PropertyConfiguration, containers, distinctItems: true, filterItems: true); - (directoriesAndDateTimes, collection) = GetCollectionForSaveShortcutsForOutputResolutionsDuringMapLogic(personKeyToIds, dFacesContentDirectory, validImageItems, distinctValidImageMappingCollection); - string[] directories = (from l in collection select l.Directory).Distinct().ToArray(); - foreach (string directory in directories) - { - if (string.IsNullOrEmpty(directory)) - continue; - if (!Directory.Exists(directory)) - _ = Directory.CreateDirectory(directory); - } - foreach (SaveShortcutsForOutputResolutions s in collection) - { - if (File.Exists(s.FileName)) - continue; - try - { - windowsShortcut = new() { Path = s.FullName, Description = s.Description }; - windowsShortcut.Save(s.FileName); - windowsShortcut.Dispose(); - if (s.MakeAllHidden) - File.SetAttributes(s.FileName, FileAttributes.Hidden); - File.SetLastWriteTime(s.FileName, s.DateTime); - } - catch (Exception) - { } - } - foreach ((string directory, DateTime[] dateTimes) in directoriesAndDateTimes) - { - if (dateTimes.Length == 0) - continue; - Directory.SetCreationTime(directory, dateTimes[0]); - Directory.SetLastWriteTime(directory, dateTimes[1]); - } - } - - public ReadOnlyDictionary>? GetWholePercentagesToPersonContainers(int? id) - { - ReadOnlyDictionary>? result; - if (id is null) - result = null; - else - _ = _IdThenWholePercentagesToPersonContainers.TryGetValue(id.Value, out result); - return result; - } - - public bool InSkipCollection(int id, int wholePercentages) => - _SkipCollection.TryGetValue(id, out List? wholePercentagesCollection) && wholePercentagesCollection.Contains(wholePercentages); - - public bool InSkipCollection(int id, MappingFromLocation mappingFromLocation) => - InSkipCollection(id, mappingFromLocation.WholePercentages); - - public bool? IsFocusPerson(int? skipPersonWithMoreThen, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, int wholePercentages) - { - bool? result; - ReadOnlyCollection? personContainers; - if (skipPersonWithMoreThen is null && jLinkResolvedPersonKeys.Length == 0) - result = null; - else if (wholePercentagesToPersonContainers is null) - result = null; - else if (!wholePercentagesToPersonContainers.TryGetValue(wholePercentages, out personContainers)) - result = null; - else - { - result = false; - foreach (PersonContainer personContainer in personContainers) - { - if (personContainer.Key is null) - continue; - if (skipPersonWithMoreThen is not null && _PersonKeyToCount.TryGetValue(personContainer.Key.Value, out int count) && count > 2 && count < skipPersonWithMoreThen.Value) - { - result = true; - break; - } - if (jLinkResolvedPersonKeys.Contains(personContainer.Key.Value)) - { - result = true; - break; - } - } - } - return result; - } - - public bool? IsFocusPerson(int? skipPersonWithMoreThen, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => - IsFocusPerson(skipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages); - - public void LookForAbandoned(IDlibDotNet dlibDotNet, Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) - { - string[] directories; - string? directoryName; - List distinctFilteredIds = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); - LookForAbandoned(propertyConfiguration, distinctFilteredIds); - dlibDotNet.Tick(); - List distinctFilteredFileNameFirstSegments = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); - Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, bResultsFullGroupDirectory, distinctFilteredFileNameFirstSegments); - dlibDotNet.Tick(); - directories = Directory.GetDirectories(cResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string directory in directories) - { - directoryName = Path.GetFileName(directory); - if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) - continue; - Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); - } - dlibDotNet.Tick(); - directories = Directory.GetDirectories(dResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string directory in directories) - { - directoryName = Path.GetFileName(directory); - if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) - continue; - Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); - } - dlibDotNet.Tick(); - directories = Directory.GetDirectories(d2ResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string directory in directories) - { - directoryName = Path.GetFileName(directory); - if (string.IsNullOrEmpty(directoryName) || directoryName.Length != 2 && directoryName.Length != 4) - continue; - Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); - } - dlibDotNet.Tick(); - } - } \ No newline at end of file diff --git a/Map/Models/Stateless/DistanceLogic.cs b/Map/Models/Stateless/DistanceLogic.cs index 93aa685..46605de 100644 --- a/Map/Models/Stateless/DistanceLogic.cs +++ b/Map/Models/Stateless/DistanceLogic.cs @@ -25,327 +25,6 @@ internal abstract class DistanceLogic bool? IsLocationContainerDebugDirectory, float? TotalDays); - private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames) - { - string checkFile; - string actionDirectoryName = Path.GetFileName(actionDirectory); - string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName); - if (!Directory.Exists(checkDirectory)) - _ = Directory.CreateDirectory(checkDirectory); - foreach (string file in files) - { - if (facesFileNames.Contains(file)) - { - checkFile = Path.Combine(checkDirectory, Path.GetFileName(file)); - if (File.Exists(checkFile)) - continue; - File.Move(file, checkFile); - continue; - } - File.Delete(file); - } - } - - private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory) - { - string[] files; - string checkFile; - string? checkDirectory; - string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string directory in directories) - { - checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory)); - if (!Directory.Exists(checkDirectory)) - Directory.Move(directory, checkDirectory); - else - { - files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories); - foreach (string file in files) - { - if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted)) - continue; - checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted); - checkDirectory = Path.GetDirectoryName(checkFile); - if (checkDirectory is null) - continue; - if (File.Exists(checkFile)) - continue; - if (!Directory.Exists(checkDirectory)) - _ = Directory.CreateDirectory(checkDirectory); - File.Move(file, checkFile); - } - } - } - _ = IPath.DeleteEmptyDirectories(personKeyDirectory); - } - - private static List UpdateDateVerifyAndGetTicksDirectories(Configuration configuration, string eDistanceContentDirectory) - { - List results = []; - float? totalDays; - long? next = null; - string? checkDirectory; - DateTime directoryDateTime; - DirectoryInfo directoryInfo; - TicksDirectory ticksDirectory; - long? lastDirectoryTicks = null; - DateTime dateTime = DateTime.Now; - DateTime alternateDirectoryDateTime; - string ticksDirectoryNameFirstSegment; - bool? isLocationContainerDebugDirectory; - long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks; - for (int i = 1; i < 5; i++) - _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); - if (!Directory.Exists(eDistanceContentDirectory)) - _ = Directory.CreateDirectory(eDistanceContentDirectory); - string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string ticksFullPath in ticksFullPaths) - { - ticksDirectoryNameFirstSegment = Path.GetFileName(ticksFullPath).Split('.')[0]; - if (ticksDirectoryNameFirstSegment.Length < 3) - continue; - if (!long.TryParse(ticksDirectoryNameFirstSegment, out long directoryTicks)) - throw new NotSupportedException(); - if (next is null) - next = new DateTime(directoryTicks).Ticks; - else - { - next += month; - checkDirectory = Path.GetDirectoryName(ticksFullPath); - if (string.IsNullOrEmpty(checkDirectory)) - { - if (string.IsNullOrEmpty(checkDirectory)) - continue; - checkDirectory = Path.Combine(checkDirectory, next.Value.ToString()); - if (ticksFullPath == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern)) - continue; - Directory.Move(ticksFullPath, checkDirectory); - continue; - } - } - directoryInfo = new(ticksFullPath); - directoryDateTime = new DateTime(directoryTicks); - if (directoryInfo.CreationTime.Ticks != directoryTicks) - Directory.SetCreationTime(ticksFullPath, new DateTime(directoryTicks)); - if (directoryInfo.LastWriteTime.Ticks != directoryTicks) - Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks)); - alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1); - isLocationContainerDebugDirectory = configuration.LocationContainerDebugDirectory is null ? null : ticksDirectoryNameFirstSegment.EndsWith(configuration.LocationContainerDebugDirectory); - totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays; - ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), isLocationContainerDebugDirectory, totalDays); - results.Add(ticksDirectory); - if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0) - continue; - lastDirectoryTicks = directoryTicks; - } - string[] compare = (from l in results where l.TotalDays is not null and < 9.95f select l.Directory).ToArray(); - if (compare.Length > 0 && configuration.ReMap) - throw new Exception($"Please Consolidate <{string.Join(Environment.NewLine, compare)}>"); - return results; - } - - private static void Individually(Configuration configuration, TicksDirectory ticksDirectory, string directory) - { - bool isDefault; - string[] files; - FileInfo[] collection; - string[] facesFileNames; - string yearDirectoryName; - string[] yearDirectories; - string alphaDirectoryName; - string matchDirectoryName; - string personKeyFormatted; - string[] alphaDirectories; - string[] matchDirectories; - string[] actionDirectories; - string personDisplayDirectory; - string[] personKeyDirectories; - string[] segmentCDirectories = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly); - foreach (string segmentCDirectory in segmentCDirectories) - { - personKeyDirectories = Directory.GetDirectories(segmentCDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string personKeyDirectory in personKeyDirectories) - { - personKeyFormatted = Path.GetFileName(personKeyDirectory); - yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string yearDirectory in yearDirectories) - { - yearDirectoryName = Path.GetFileName(yearDirectory); - if (yearDirectoryName.StartsWith('=')) - Directory.Move(yearDirectory, yearDirectory.Replace('=', '~')); - } - yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string yearDirectory in yearDirectories) - { - yearDirectoryName = Path.GetFileName(yearDirectory); - matchDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); - alphaDirectories = matchDirectories.Where(l => !long.TryParse(Path.GetFileName(l), out long a)).ToArray(); - if (alphaDirectories.Length == 0) - continue; - alphaDirectoryName = Path.GetFileName(alphaDirectories[0]); - foreach (string matchDirectory in matchDirectories) - { - matchDirectoryName = Path.GetFileName(matchDirectory); - files = Directory.GetFiles(matchDirectory, "*", SearchOption.TopDirectoryOnly); - if (files.Length != 4) - continue; - collection = files.Select(l => new FileInfo(l)).ToArray(); - isDefault = IPerson.IsDefaultName(alphaDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]); - if (isDefault) - facesFileNames = (from l in collection where l.Extension == configuration.FacesFileNameExtension select l.FullName).ToArray(); - else - facesFileNames = (from l in collection where l.Extension == configuration.FacesFileNameExtension && l.Name.Contains(matchDirectoryName) select l.FullName).ToArray(); - if (facesFileNames.Length == 0) - continue; - personDisplayDirectory = Path.Combine(matchDirectory, alphaDirectoryName); - if (!Directory.Exists(personDisplayDirectory) || !Directory.Exists(matchDirectory)) - continue; - _ = Process.Start("explorer", matchDirectory); - for (int i = 0; i < int.MaxValue; i++) - { - Thread.Sleep(500); - actionDirectories = Directory.GetDirectories(matchDirectory, "*", SearchOption.TopDirectoryOnly).Where(l => l != personDisplayDirectory && !l.EndsWith("Maybe")).ToArray(); - if (actionDirectories.Length > 0) - { - MoveTo(actionDirectories[0], ticksDirectory, directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName, files, facesFileNames); - break; - } - } - } - } - } - } - } - - private static string GetCheckFile(TicksDirectory ticksDirectory, string @enum, string fileName, string file) - { - string result; - string checkDirectory; - string directory = file; - List collection = []; - for (int i = 0; i < file.Length; i++) - { - directory = Path.GetDirectoryName(directory) ?? throw new Exception(); - if (directory == ticksDirectory.Directory) - break; - collection.Add(Path.GetFileName(directory)); - } - collection.Reverse(); - checkDirectory = $"{ticksDirectory.Directory}.{@enum}"; - foreach (string directoryName in collection) - checkDirectory = Path.Combine(checkDirectory, directoryName); - if (!Directory.Exists(checkDirectory)) - _ = Directory.CreateDirectory(checkDirectory); - result = Path.Combine(checkDirectory, fileName); - if (File.Exists(result)) - throw new Exception($"File <{fileName}> already exists!"); - File.Move(file, result); - return result; - } - - private static List GetRecords(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, TicksDirectory ticksDirectory, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List distinct, string? personDisplayDirectoryName) - { - List results = []; - string @enum; - Record record; - string fileName; - string checkFile; - FilePath filePath; - FileHolder fileHolder; - int? wholePercentages; - foreach (string file in files) - { - if (file.EndsWith(".lnk")) - continue; - fileHolder = IFileHolder.Get(file); - filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); - if (filePath.Id is null) - continue; - wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath); - if (wholePercentages is null) - continue; - fileName = Path.GetFileName(file); - if (distinct.Contains(fileName)) - { - checkFile = $"{file}.dup"; - if (File.Exists(checkFile)) - continue; - File.Move(file, checkFile); - continue; - } - if (file.StartsWith(ticksDirectory.Directory)) - { - @enum = IPath.GetEnum(filePath).ToString(); - if (!ticksDirectory.Directory.EndsWith(@enum)) - { - checkFile = GetCheckFile(ticksDirectory, @enum, fileName, file); - fileHolder = IFileHolder.Get(checkFile); - filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); - if (filePath.Id is null) - continue; - } - } - distinct.Add(fileName); - record = new(DirectoryNumber: directoryNumber, - IsDefault: isDefault, - LinksCount: linksCount, - MappedFaceFilePath: filePath, - PersonDisplayDirectoryName: personDisplayDirectoryName, - PersonKeyFormatted: personKeyFormatted); - results.Add(record); - } - return results; - } - - private static string[] RenameBirth(string[] files) - { - List results = []; - string checkFile; - foreach (string file in files) - { - if (file.EndsWith(".brt")) - { - results.Add(file); - continue; - } - checkFile = $"{file}.brt"; - if (File.Exists(checkFile)) - { - results.Add(file); - continue; - } - File.Move(file, checkFile); - results.Add(checkFile); - } - return results.ToArray(); - } - - private static void MovedToNewestPersonKeyFormatted(string personKeyFormatted, string newestPersonKeyFormatted, TicksDirectory ticksDirectory, string personKeyDirectory) - { - string newestPersonKeyDirectory = Path.Combine(ticksDirectory.Directory, newestPersonKeyFormatted); - if (Directory.Exists(newestPersonKeyDirectory)) - MoveFiles(personKeyFormatted, personKeyDirectory, newestPersonKeyFormatted, newestPersonKeyDirectory); - else - Directory.Move(personKeyDirectory, newestPersonKeyDirectory); - } - - private static int? GetLinksCount(string yearDirectory) - { - int? result; - string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-'); - if (yearDirectoryNameSegments.Length != 3) - result = null; - else - { - string lastSegment = yearDirectoryNameSegments[^1]; - if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0])) - result = null; - else - result = lastSegment[0] - 65; - } - return result; - } - internal static List DeleteEmptyDirectoriesAndGetCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, string eDistanceContentDirectory, ReadOnlyDictionary personKeyFormattedToNewestPersonKeyFormatted, ReadOnlyCollection personKeyFormattedCollection) { List results = []; @@ -529,4 +208,325 @@ internal abstract class DistanceLogic return results; } + private static List UpdateDateVerifyAndGetTicksDirectories(Configuration configuration, string eDistanceContentDirectory) + { + List results = []; + float? totalDays; + long? next = null; + string? checkDirectory; + DateTime directoryDateTime; + DirectoryInfo directoryInfo; + TicksDirectory ticksDirectory; + long? lastDirectoryTicks = null; + DateTime dateTime = DateTime.Now; + DateTime alternateDirectoryDateTime; + string ticksDirectoryNameFirstSegment; + bool? isLocationContainerDebugDirectory; + long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks; + for (int i = 1; i < 5; i++) + _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); + if (!Directory.Exists(eDistanceContentDirectory)) + _ = Directory.CreateDirectory(eDistanceContentDirectory); + string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string ticksFullPath in ticksFullPaths) + { + ticksDirectoryNameFirstSegment = Path.GetFileName(ticksFullPath).Split('.')[0]; + if (ticksDirectoryNameFirstSegment.Length < 3) + continue; + if (!long.TryParse(ticksDirectoryNameFirstSegment, out long directoryTicks)) + throw new NotSupportedException(); + if (next is null) + next = new DateTime(directoryTicks).Ticks; + else + { + next += month; + checkDirectory = Path.GetDirectoryName(ticksFullPath); + if (string.IsNullOrEmpty(checkDirectory)) + { + if (string.IsNullOrEmpty(checkDirectory)) + continue; + checkDirectory = Path.Combine(checkDirectory, next.Value.ToString()); + if (ticksFullPath == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern)) + continue; + Directory.Move(ticksFullPath, checkDirectory); + continue; + } + } + directoryInfo = new(ticksFullPath); + directoryDateTime = new DateTime(directoryTicks); + if (directoryInfo.CreationTime.Ticks != directoryTicks) + Directory.SetCreationTime(ticksFullPath, new DateTime(directoryTicks)); + if (directoryInfo.LastWriteTime.Ticks != directoryTicks) + Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks)); + alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1); + isLocationContainerDebugDirectory = configuration.LocationContainerDebugDirectory is null ? null : ticksDirectoryNameFirstSegment.EndsWith(configuration.LocationContainerDebugDirectory); + totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays; + ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), isLocationContainerDebugDirectory, totalDays); + results.Add(ticksDirectory); + if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0) + continue; + lastDirectoryTicks = directoryTicks; + } + string[] compare = (from l in results where l.TotalDays is not null and < 9.95f select l.Directory).ToArray(); + if (compare.Length > 0 && configuration.ReMap) + throw new Exception($"Please Consolidate <{string.Join(Environment.NewLine, compare)}>"); + return results; + } + + private static void Individually(Configuration configuration, TicksDirectory ticksDirectory, string directory) + { + bool isDefault; + string[] files; + FileInfo[] collection; + string[] facesFileNames; + string yearDirectoryName; + string[] yearDirectories; + string alphaDirectoryName; + string matchDirectoryName; + string personKeyFormatted; + string[] alphaDirectories; + string[] matchDirectories; + string[] actionDirectories; + string personDisplayDirectory; + string[] personKeyDirectories; + string[] segmentCDirectories = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly); + foreach (string segmentCDirectory in segmentCDirectories) + { + personKeyDirectories = Directory.GetDirectories(segmentCDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string personKeyDirectory in personKeyDirectories) + { + personKeyFormatted = Path.GetFileName(personKeyDirectory); + yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string yearDirectory in yearDirectories) + { + yearDirectoryName = Path.GetFileName(yearDirectory); + if (yearDirectoryName.StartsWith('=')) + Directory.Move(yearDirectory, yearDirectory.Replace('=', '~')); + } + yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string yearDirectory in yearDirectories) + { + yearDirectoryName = Path.GetFileName(yearDirectory); + matchDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); + alphaDirectories = matchDirectories.Where(l => !long.TryParse(Path.GetFileName(l), out long a)).ToArray(); + if (alphaDirectories.Length == 0) + continue; + alphaDirectoryName = Path.GetFileName(alphaDirectories[0]); + foreach (string matchDirectory in matchDirectories) + { + matchDirectoryName = Path.GetFileName(matchDirectory); + files = Directory.GetFiles(matchDirectory, "*", SearchOption.TopDirectoryOnly); + if (files.Length != 4) + continue; + collection = files.Select(l => new FileInfo(l)).ToArray(); + isDefault = IPerson.IsDefaultName(alphaDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]); + if (isDefault) + facesFileNames = (from l in collection where l.Extension == configuration.FacesFileNameExtension select l.FullName).ToArray(); + else + facesFileNames = (from l in collection where l.Extension == configuration.FacesFileNameExtension && l.Name.Contains(matchDirectoryName) select l.FullName).ToArray(); + if (facesFileNames.Length == 0) + continue; + personDisplayDirectory = Path.Combine(matchDirectory, alphaDirectoryName); + if (!Directory.Exists(personDisplayDirectory) || !Directory.Exists(matchDirectory)) + continue; + _ = Process.Start("explorer", matchDirectory); + for (int i = 0; i < int.MaxValue; i++) + { + Thread.Sleep(500); + actionDirectories = Directory.GetDirectories(matchDirectory, "*", SearchOption.TopDirectoryOnly).Where(l => l != personDisplayDirectory && !l.EndsWith("Maybe")).ToArray(); + if (actionDirectories.Length > 0) + { + MoveTo(actionDirectories[0], ticksDirectory, directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName, files, facesFileNames); + break; + } + } + } + } + } + } + } + + private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames) + { + string checkFile; + string actionDirectoryName = Path.GetFileName(actionDirectory); + string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + foreach (string file in files) + { + if (facesFileNames.Contains(file)) + { + checkFile = Path.Combine(checkDirectory, Path.GetFileName(file)); + if (File.Exists(checkFile)) + continue; + File.Move(file, checkFile); + continue; + } + File.Delete(file); + } + } + + private static int? GetLinksCount(string yearDirectory) + { + int? result; + string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-'); + if (yearDirectoryNameSegments.Length != 3) + result = null; + else + { + string lastSegment = yearDirectoryNameSegments[^1]; + if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0])) + result = null; + else + result = lastSegment[0] - 65; + } + return result; + } + + private static string[] RenameBirth(string[] files) + { + List results = []; + string checkFile; + foreach (string file in files) + { + if (file.EndsWith(".brt")) + { + results.Add(file); + continue; + } + checkFile = $"{file}.brt"; + if (File.Exists(checkFile)) + { + results.Add(file); + continue; + } + File.Move(file, checkFile); + results.Add(checkFile); + } + return results.ToArray(); + } + + private static void MovedToNewestPersonKeyFormatted(string personKeyFormatted, string newestPersonKeyFormatted, TicksDirectory ticksDirectory, string personKeyDirectory) + { + string newestPersonKeyDirectory = Path.Combine(ticksDirectory.Directory, newestPersonKeyFormatted); + if (Directory.Exists(newestPersonKeyDirectory)) + MoveFiles(personKeyFormatted, personKeyDirectory, newestPersonKeyFormatted, newestPersonKeyDirectory); + else + Directory.Move(personKeyDirectory, newestPersonKeyDirectory); + } + + private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory) + { + string[] files; + string checkFile; + string? checkDirectory; + string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) + { + checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory)); + if (!Directory.Exists(checkDirectory)) + Directory.Move(directory, checkDirectory); + else + { + files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories); + foreach (string file in files) + { + if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted)) + continue; + checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted); + checkDirectory = Path.GetDirectoryName(checkFile); + if (checkDirectory is null) + continue; + if (File.Exists(checkFile)) + continue; + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + File.Move(file, checkFile); + } + } + } + _ = IPath.DeleteEmptyDirectories(personKeyDirectory); + } + + private static List GetRecords(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, TicksDirectory ticksDirectory, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List distinct, string? personDisplayDirectoryName) + { + List results = []; + string @enum; + Record record; + string fileName; + string checkFile; + FilePath filePath; + FileHolder fileHolder; + int? wholePercentages; + foreach (string file in files) + { + if (file.EndsWith(".lnk")) + continue; + fileHolder = IFileHolder.Get(file); + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + if (filePath.Id is null) + continue; + wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath); + if (wholePercentages is null) + continue; + fileName = Path.GetFileName(file); + if (distinct.Contains(fileName)) + { + checkFile = $"{file}.dup"; + if (File.Exists(checkFile)) + continue; + File.Move(file, checkFile); + continue; + } + if (file.StartsWith(ticksDirectory.Directory)) + { + @enum = IPath.GetEnum(filePath).ToString(); + if (!ticksDirectory.Directory.EndsWith(@enum)) + { + checkFile = GetCheckFile(ticksDirectory, @enum, fileName, file); + fileHolder = IFileHolder.Get(checkFile); + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + if (filePath.Id is null) + continue; + } + } + distinct.Add(fileName); + record = new(DirectoryNumber: directoryNumber, + IsDefault: isDefault, + LinksCount: linksCount, + MappedFaceFilePath: filePath, + PersonDisplayDirectoryName: personDisplayDirectoryName, + PersonKeyFormatted: personKeyFormatted); + results.Add(record); + } + return results; + } + + private static string GetCheckFile(TicksDirectory ticksDirectory, string @enum, string fileName, string file) + { + string result; + string checkDirectory; + string directory = file; + List collection = []; + for (int i = 0; i < file.Length; i++) + { + directory = Path.GetDirectoryName(directory) ?? throw new Exception(); + if (directory == ticksDirectory.Directory) + break; + collection.Add(Path.GetFileName(directory)); + } + collection.Reverse(); + checkDirectory = $"{ticksDirectory.Directory}.{@enum}"; + foreach (string directoryName in collection) + checkDirectory = Path.Combine(checkDirectory, directoryName); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + result = Path.Combine(checkDirectory, fileName); + if (File.Exists(result)) + throw new Exception($"File <{fileName}> already exists!"); + File.Move(file, result); + return result; + } + } \ No newline at end of file diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index 72cb0cc..6846fd8 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -32,75 +32,214 @@ internal abstract class MapLogic int Id, int WholePercentages); - internal static void SetSkipCollections(Configuration configuration, ReadOnlyCollection personContainers, string? a2PeopleSingletonDirectory, Dictionary> skipCollection, Dictionary> skipNotSkipCollection) + private static ReadOnlyDictionary>> GetReadOnly(Dictionary>> idThenWholePercentagesToPersonContainerCollection) { - string checkFile; - int? wholePercentages; - List distinct = []; - List distinctFiles = []; - List distinctFileName = []; - bool skipNotSkipDirectoriesAny = configuration.SkipNotSkipDirectories.Length > 0; - string[] checkDirectories = (from l in configuration.SkipNotSkipDirectories select Path.GetFullPath($"{a2PeopleSingletonDirectory}{l}")).ToArray(); - foreach (PersonContainer personContainer in personContainers) + Dictionary>> results = []; + List distinct = []; + List personContainers; + Dictionary> keyValuePairs; + foreach (KeyValuePair>> idTo in idThenWholePercentagesToPersonContainerCollection) { - foreach (FilePath personDisplayDirectoryAllFilePath in personContainer.DisplayDirectoryAllFilePaths) + keyValuePairs = []; + foreach (KeyValuePair> wholePercentagesTo in idThenWholePercentagesToPersonContainerCollection[idTo.Key]) { - if (personDisplayDirectoryAllFilePath.ExtensionLowered != configuration.FacesFileNameExtension) + distinct.Clear(); + personContainers = []; + foreach (PersonContainer personContainer in wholePercentagesTo.Value) + { + if (personContainer.Key is null) + throw new Exception(); + if (distinct.Contains(personContainer.Key.Value)) + continue; + personContainers.Add(personContainer); + } + keyValuePairs.Add(wholePercentagesTo.Key, new(personContainers)); + } + results.Add(idTo.Key, new(keyValuePairs)); + } + return results.AsReadOnly(); + } + + internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName) + { + string byValue; + bool isByMapping; + bool isBySorting; + if (by is null) + { + isByMapping = false; + isBySorting = !sortingContainersAny; + byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Mapping)}Null"; + } + else + { + isByMapping = by == Shared.Models.Stateless.IMapLogic.Mapping; + isBySorting = by == Shared.Models.Stateless.IMapLogic.Sorting; + bool isDefaultName = displayDirectoryName is not null && IPerson.IsDefaultName(displayDirectoryName); + if (isBySorting && displayDirectoryName is null) + byValue = saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)} Without Person{(distancePermyriad < 2000 ? "-A" : "-Z")}"; + else if (isBySorting && useFiltersCounter.HasValue) + byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}"; + else + byValue = Methods.IMapLogic.Get(saveIndividually, forceSingleImageHumanized, by.Value, isDefaultName); + } + return new(byValue, isByMapping, isBySorting); + } + + internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, long? personKey, string? displayDirectoryName) => + Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKey is null ? null : Shared.Models.Stateless.IMapLogic.Mapping, displayDirectoryName); + + internal static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection items) + { + Mapping[] results; + ReadOnlyCollection faces = GetFaces(items); + results = GetSelectedMappingCollection(faces); + return results; + } + + internal static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection faces) + { + Mapping[] results; + IEnumerable collection = from l in faces orderby l.Mapping?.MappingFromItem.Id select l.Mapping; + results = (from l in collection where l is not null select l).ToArray(); + return results; + } + + internal static ReadOnlyCollection GetFaces(ReadOnlyCollection items) + { + List results = []; + foreach (Item item in items) + { + if (item.Property?.Id is null || item.ResizedFileHolder is null) + continue; + foreach (Face face in item.Faces) + { + if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) continue; - if (distinctFiles.Contains(personDisplayDirectoryAllFilePath.FullName)) - continue; - distinctFiles.Add(personDisplayDirectoryAllFilePath.FullName); - distinct.Add(personDisplayDirectoryAllFilePath); + results.Add(face); } } - foreach (FilePath filePath in distinct) + return results.AsReadOnly(); + } + + internal static void SaveMappingShortcuts(string mappingDirectory) + { + string? shortcutFileName; + string[] yearDirectories; + string personKeyFormatted; + string[] personNameDirectories; + WindowsShortcut windowsShortcut; + string personDisplayDirectoryName; + (string, string)[] yearDirectoryNameCheck; + List<(string, string)> yearDirectoryNames = []; + string[] personKeyDirectories = Directory.GetDirectories(mappingDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string personKeyDirectory in personKeyDirectories) { - if (distinctFileName.Contains(filePath.Name)) - { - checkFile = $"{filePath.FullName}.dup"; - if (File.Exists(checkFile)) - continue; - File.Move(filePath.FullName, checkFile); + windowsShortcut = new(); + shortcutFileName = null; + yearDirectoryNames.Clear(); + personKeyFormatted = Path.GetFileName(personKeyDirectory); + yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); + yearDirectoryNames.AddRange(GetCollection(yearDirectories)); + yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('^') select l).OrderByDescending(l => l.Item2).ToArray(); + if (yearDirectoryNameCheck.Length == 0) + yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('~') select l).OrderByDescending(l => l.Item2).ToArray(); + if (yearDirectoryNameCheck.Length == 0) + yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('=') select l).OrderByDescending(l => l.Item2).ToArray(); + if (yearDirectoryNameCheck.Length == 0) + yearDirectoryNameCheck = (from l in yearDirectoryNames select l).OrderByDescending(l => l).ToArray(); + if (yearDirectoryNameCheck.Length == 0) continue; - } - if (filePath.Id is null) - continue; - wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath); - if (wholePercentages is null) - continue; - if (!skipNotSkipDirectoriesAny || !checkDirectories.Any(filePath.FullName.StartsWith)) + foreach ((string yearDirectory, string yearDirectoryName) in yearDirectoryNameCheck) { - if (!skipCollection.ContainsKey(filePath.Id.Value)) - skipCollection.Add(filePath.Id.Value, []); - skipCollection[filePath.Id.Value].Add((filePath.FullName, wholePercentages.Value)); + personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string personNameDirectory in personNameDirectories) + { + personDisplayDirectoryName = Path.GetFileName(personNameDirectory).Split('-')[0]; + if (personDisplayDirectoryName is null) + continue; + windowsShortcut.Path = yearDirectory; + windowsShortcut.Description = yearDirectoryName; + shortcutFileName = Path.Combine(mappingDirectory, $"{personDisplayDirectoryName} [{windowsShortcut.Description}].lnk"); + break; + } + if (shortcutFileName is not null) + { + if (!File.Exists(shortcutFileName)) + break; + } } - else + if (shortcutFileName is null || windowsShortcut.Path is null || windowsShortcut.Description is null) + continue; + try { - if (!skipNotSkipCollection.ContainsKey(filePath.Id.Value)) - skipNotSkipCollection.Add(filePath.Id.Value, []); - skipNotSkipCollection[filePath.Id.Value].Add((filePath.FullName, wholePercentages.Value)); + windowsShortcut.Save(shortcutFileName); + windowsShortcut.Dispose(); } + catch (Exception) + { } } } - internal static void SetPersonCollectionsAfterSetSkipCollections(Configuration configuration, ReadOnlyCollection personContainers, Dictionary personKeyFormattedToNewestPersonKeyFormatted, List personKeyFormattedCollection) + private static IEnumerable<(string, string)> GetCollection(string[] yearDirectories) { - string personKeyFormatted; - string newestPersonKeyFormatted; - foreach (PersonContainer personContainer in personContainers) + foreach (string l in yearDirectories) + yield return new(l, Path.GetFileName(l)); + } + + internal static ReadOnlyDictionary> ConvertSkip(Dictionary> skipCollection) + { + Dictionary> results = []; + List? wholePercentagesCollection; + foreach (KeyValuePair> keyValuePair in skipCollection) { - if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) - continue; - foreach (PersonBirthday personBirthday in personContainer.Birthdays) + if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection)) { - personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday); - personKeyFormattedCollection.Add(personKeyFormatted); - if (personContainer.Birthdays.Length < 1) + results.Add(keyValuePair.Key, []); + if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection)) + throw new Exception(); + } + foreach ((string _, int wholePercentage) in keyValuePair.Value) + wholePercentagesCollection.Add(wholePercentage); + } + return results.AsReadOnly(); + } + + internal static ReadOnlyDictionary> GetIdToPersonKeys(ReadOnlyDictionary> personKeyToIds) + { + Dictionary> results = []; + List? collection; + foreach (KeyValuePair> keyValuePair in personKeyToIds) + { + foreach (int id in keyValuePair.Value) + { + if (!results.TryGetValue(id, out collection)) + { + results.Add(id, []); + if (!results.TryGetValue(id, out collection)) + throw new Exception(); + } + if (collection.Contains(keyValuePair.Key)) continue; - newestPersonKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personContainer.Key.Value); - _ = personKeyFormattedToNewestPersonKeyFormatted.TryAdd(personKeyFormatted, newestPersonKeyFormatted); + collection.Add(keyValuePair.Key); } } + return results.AsReadOnly(); + } + + internal static SaveContainer GetDebugSaveContainer(SortingContainer sortingContainer, string directory, Mapping keyMapping) + { + SaveContainer result; + string shortcutFile; + if (sortingContainer?.Source.MappingFromLocation is null) + throw new NullReferenceException(nameof(sortingContainer.Source.MappingFromLocation)); + FileHolder faceFileHolder = IFileHolder.Get($"C:/{sortingContainer.Sorting.Id}.{sortingContainer.Sorting.WholePercentages}"); + if (keyMapping.MappingFromPerson is not null && keyMapping.MappingFromLocation is not null) + shortcutFile = Path.Combine(directory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}.{sortingContainer.Sorting.DistancePermyriad}.lnk"); + else + shortcutFile = Path.Combine(directory, $"{sortingContainer.Source.MappingFromLocation.DeterministicHashCodeKey}{sortingContainer.Source.MappingFromItem.FilePath.ExtensionLowered}.{sortingContainer.Sorting.DistancePermyriad}.lnk"); + result = new(directory, faceFileHolder, sortingContainer.Source.MappingFromItem.ResizedFileHolder, shortcutFile); + return result; } internal static string GetMappingSegmentB(long ticks, long personKey, int? approximateYears, MappingFromItem mappingFromItem) @@ -112,6 +251,14 @@ internal abstract class MapLogic return result; } + internal static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, MappingFromItem mappingFromItem) + { + string result; + DateTime dateTime = mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(); + result = GetMappingSegmentB(ticks, personBirthday, approximateYears, dateTime, mappingFromItem.IsWrongYear); + return result; + } + private static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, DateTime dateTimeOriginalThenMinimumDateTime, bool? isWrongYear) { string result = GetMappingSegmentB(ticks, personBirthday, approximateYears, dateTimeOriginalThenMinimumDateTime.Ticks, isWrongYear); @@ -144,100 +291,154 @@ internal abstract class MapLogic return result; } - internal static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection items) + internal static ReadOnlyDictionary> ConvertSkipNotSkip(Dictionary> skipNotSkipCollection) { - Mapping[] results; - ReadOnlyCollection faces = GetFaces(items); - results = GetSelectedMappingCollection(faces); - return results; - } - - internal static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection faces) - { - Mapping[] results; - IEnumerable collection = from l in faces orderby l.Mapping?.MappingFromItem.Id select l.Mapping; - results = (from l in collection where l is not null select l).ToArray(); - return results; - } - - internal static ReadOnlyCollection GetFaces(ReadOnlyCollection items) - { - List results = []; - foreach (Item item in items) + Dictionary> results = []; + List? wholePercentagesCollection; + foreach (KeyValuePair> keyValuePair in skipNotSkipCollection) { - if (item.Property?.Id is null || item.ResizedFileHolder is null) - continue; - foreach (Face face in item.Faces) + if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection)) { - if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) - continue; - results.Add(face); + results.Add(keyValuePair.Key, []); + if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection)) + throw new Exception(); } + foreach ((string _, int wholePercentage) in keyValuePair.Value) + wholePercentagesCollection.Add(wholePercentage); } return results.AsReadOnly(); } - internal static string GetMappingSegmentB(long ticks, PersonBirthday personBirthday, int? approximateYears, MappingFromItem mappingFromItem) + internal static ReadOnlyDictionary> ConvertLocationContainers(List locationContainers) { - string result; - DateTime dateTime = mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(); - result = GetMappingSegmentB(ticks, personBirthday, approximateYears, dateTime, mappingFromItem.IsWrongYear); + Dictionary> results = []; + foreach (LocationContainer locationContainer in locationContainers) + { + if (!results.ContainsKey(locationContainer.Id)) + results.Add(locationContainer.Id, []); + results[locationContainer.Id].Add(locationContainer); + } + return results.AsReadOnly(); + } + + internal static void CheckCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string? rootDirectoryParent) + { + string json; + string fullPath; + List>? collection; + foreach (string propertyContentCollectionFile in propertyConfiguration.PropertyContentCollectionFiles) + { + fullPath = Path.GetFullPath(string.Concat(rootDirectoryParent, propertyContentCollectionFile)); + if (fullPath.Contains(propertyConfiguration.RootDirectory)) + continue; + if (!File.Exists(fullPath)) + continue; + json = File.ReadAllText(fullPath); + collection = JsonSerializer.Deserialize>>(json); + if (collection is null) + throw new NullReferenceException(nameof(collection)); + } + } + + internal static string? GetFacePartsContentCollectionFile(string extension, string d2FacePartsContentCollectionDirectory, MappingFromItem mappingFromItem) + { + string? result; + string? directoryName = Path.GetDirectoryName(mappingFromItem.RelativePath); + if (directoryName is null) + result = null; + else + result = Path.Combine($"{d2FacePartsContentCollectionDirectory}{directoryName}", $"{mappingFromItem.FilePath.Name}{extension}"); return result; } - private static List GetDisplayDirectoryAllFiles(string fileNameExtension, string personBirthdayFormat, ReadOnlyCollection personContainers) + internal static string GetResizeContentDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string cContentDirectory, FilePath filePath) { - List results = []; - MappedFile mappedFile; + string result; + CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); + result = Path.Combine(cContentDirectory, cei.Combined); + return result; + } + + internal static ReadOnlyDictionary> GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) + { + Dictionary> results = []; + Dictionary? keyValuePairs; + foreach (Mapping mapping in distinctValidImageMappingCollection) + { + if (mapping.MappingFromLocation is null) + continue; + if (!results.TryGetValue(mapping.MappingFromItem.Id, out keyValuePairs)) + { + results.Add(mapping.MappingFromItem.Id, []); + if (!results.TryGetValue(mapping.MappingFromItem.Id, out keyValuePairs)) + throw new Exception(); + } + if (keyValuePairs.ContainsKey(mapping.MappingFromLocation.WholePercentages)) + continue; + keyValuePairs.Add(mapping.MappingFromLocation.WholePercentages, mapping); + } + return GetReadOnly(results); + } + + private static ReadOnlyDictionary> GetReadOnly(Dictionary> keyValuePairs) + { + Dictionary> results = []; + foreach (KeyValuePair> keyValuePair in keyValuePairs) + results.Add(keyValuePair.Key, new(keyValuePair.Value)); + return results.AsReadOnly(); + } + + internal static int CopyManualFiles(Configuration configuration, long ticks, ReadOnlyCollection personContainers, string eDistanceContentTicksDirectory) + { + int result = 0; + string checkFile; + string dateDirectory; + string directoryName; + string checkDirectory; string personKeyFormatted; List distinct = []; + PersonBirthday personBirthday; + DateTime dateTime = new(ticks); + string by = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy); foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null) continue; for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--) { - if (personContainer.DisplayDirectoryAllFilePaths[i].ExtensionLowered != fileNameExtension) + if (personContainer.DisplayDirectoryAllFilePaths[i].ExtensionLowered != configuration.FacesFileNameExtension) continue; if (distinct.Contains(personContainer.DisplayDirectoryAllFilePaths[i].Name)) continue; distinct.Add(personContainer.DisplayDirectoryAllFilePaths[i].Name); - personKeyFormatted = IPersonBirthday.GetFormatted(personBirthdayFormat, personContainer.Key.Value); - mappedFile = new(PersonKey: personContainer.Key.Value, - PersonKeyFormatted: personKeyFormatted, - PersonDisplayDirectoryName: personContainer.DisplayDirectoryName, - DirectoryNumber: null, - FilePath: personContainer.DisplayDirectoryAllFilePaths[i]); - results.Add(mappedFile); + directoryName = Path.GetFileName(personContainer.DisplayDirectoryAllFilePaths[i].DirectoryFullPath); + if (directoryName != personContainer.DisplayDirectoryName) + continue; + personBirthday = IPersonBirthday.GetPersonBirthday(personContainer.Key.Value); + personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday); + dateDirectory = Path.Combine(eDistanceContentTicksDirectory, by, personKeyFormatted, dateTime.ToString("yyyy")); + checkDirectory = Path.Combine(dateDirectory, personContainer.DisplayDirectoryName); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + checkFile = Path.Combine(dateDirectory, personContainer.DisplayDirectoryAllFilePaths[i].Name); + if (File.Exists(checkFile)) + continue; + File.Move(personContainer.DisplayDirectoryAllFilePaths[i].FullName, checkFile); + throw new NotImplementedException("readonly null?"); + // personContainer.DisplayDirectoryAllFilePaths[i] = null; + // result++; } } - return results; + return result; } - private static void OpenPossibleDuplicates(Configuration configuration, List duplicates) + internal static string GetFacePartsDirectoryX(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string d2FacePartsContentDirectory, FilePath filePath) { - string personKeyFormatted; - foreach (Duplicate duplicate in duplicates) - { - if (duplicate.Percent is null) - continue; - _ = Process.Start("explorer.exe", string.Concat("\"", duplicate.FilePath.DirectoryFullPath, "\"")); - personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, duplicate.PersonKey); - } - foreach ((long personKey, int id, FilePath filePath, float? percent) in duplicates) - { - if (percent is not null && percent.Value == 0) - continue; - _ = Process.Start("explorer.exe", string.Concat("\"", filePath.DirectoryFullPath, "\"")); - personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey); - } - foreach ((long personKey, int id, FilePath filePath, float? percent) in duplicates) - { - if (percent is not null && percent.Value > 0) - continue; - _ = Process.Start("explorer.exe", string.Concat("\"", filePath.DirectoryFullPath, "\"")); - personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey); - } + string result; + CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); + result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, filePath.NameWithoutExtension); + MoveIf(filePath.NameWithoutExtension, cei, d2FacePartsContentDirectory, result); + return result; } private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, string fullFileName) @@ -256,6 +457,70 @@ internal abstract class MapLogic } } + internal static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, int wholePercentages) + { + bool? result; + ReadOnlyCollection? personContainers; + if (wholePercentagesToPersonContainers is null) + result = null; + else + { + if (!wholePercentagesToPersonContainers.TryGetValue(wholePercentages, out personContainers)) + result = null; + else + { + result = false; + foreach (PersonContainer personContainer in personContainers) + { + if (!IPerson.IsDefaultName(personContainer) || personContainer.Key is null || !IPersonBirthday.IsCounterPersonYear(new DateTime(personContainer.Key.Value).Year) || jLinkResolvedPersonKeys.Contains(personContainer.Key.Value)) + continue; + result = true; + break; + } + } + } + return result; + } + + internal static ReadOnlyCollection GetPersonKeyFormattedIdThenWholePercentages(Configuration configuration, long ticks, List records) + { + List results = []; + int? wholePercentages; + List wholePercentagesCollection; + Dictionary> idToWholePercentagesCollection = []; + PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages; + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + string message = $") {records.Count:000} join from ticks Director(ies) - C - {totalSeconds} total second(s)"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using ProgressBar progressBar = new(records.Count, message, options); + foreach (Record record in records) + { + progressBar.Tick(); + if (record.MappedFaceFilePath.Id is null) + continue; + wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, record.MappedFaceFilePath); + if (wholePercentages is null) + continue; + if (!idToWholePercentagesCollection.ContainsKey(record.MappedFaceFilePath.Id.Value)) + idToWholePercentagesCollection.Add(record.MappedFaceFilePath.Id.Value, []); + wholePercentagesCollection = idToWholePercentagesCollection[record.MappedFaceFilePath.Id.Value]; + wholePercentagesCollection.Add(wholePercentages.Value); + idToWholePercentagesCollection[record.MappedFaceFilePath.Id.Value].Add(wholePercentages.Value); + personKeyFormattedIdThenWholePercentages = new(PersonKeyFormatted: record.PersonKeyFormatted, + PersonDisplayDirectoryName: record.PersonDisplayDirectoryName, + IsDefault: record.IsDefault, + LinksCount: record.LinksCount, + MappedFaceFilePath: record.MappedFaceFilePath, + Id: record.MappedFaceFilePath.Id.Value, + WholePercentages: wholePercentages.Value); + results.Add(personKeyFormattedIdThenWholePercentages); + } + return results.AsReadOnly(); + } + + internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, Mapping mapping) => + Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, mapping.By, mapping.MappingFromPerson?.DisplayDirectoryName); + internal static string GetFacesDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string dFacesContentDirectory, FilePath filePath, MappingFromItem mappingFromItem) { string result; @@ -265,144 +530,40 @@ internal abstract class MapLogic return result; } - private static List<(string, long)> GetDirectoryAndTicksCollection(string[] jLinks, string personBirthdayFormat, string? rootDirectory) + internal static string GetFacePartsDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string d2FacePartsContentDirectory, FilePath filePath, MappingFromItem mappingFromItem) { - List<(string, long)> results = []; - string directory; - DateTime dateTime; - string[] personKeyDirectories; - string[] personDisplayDirectoryNames; - string personKeyFormattedDirectoryName; - foreach (string jLink in jLinks) - { - if (rootDirectory is null) - continue; - directory = Path.Combine(rootDirectory, jLink); - if (!Directory.Exists(directory)) - continue; - personDisplayDirectoryNames = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly); - foreach (string personDisplayDirectoryName in personDisplayDirectoryNames) - { - personKeyDirectories = Directory.GetDirectories(personDisplayDirectoryName, "*", SearchOption.TopDirectoryOnly); - foreach (string personKeyFormattedDirectory in personKeyDirectories) - { - personKeyFormattedDirectoryName = Path.GetFileName(personKeyFormattedDirectory); - if (personKeyFormattedDirectoryName.Length != personBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormattedDirectoryName, personBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) - continue; - results.Add((directory, dateTime.Ticks)); - } - } - } - return results; + string result; + CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); + result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, mappingFromItem.FilePath.NameWithoutExtension); + MoveIf(mappingFromItem.FilePath.NameWithoutExtension, cei, d2FacePartsContentDirectory, result); + return result; } - private static (long, PersonContainer)[] GetDistinctCollection(Configuration configuration, IEnumerable personContainers, Dictionary> personKeyToPersonContainerCollection, Dictionary personKeyFormattedToPersonContainer) + internal static List GetNotMappedPersonContainers(Configuration configuration, long ticks, ReadOnlyCollection personContainers, ReadOnlyDictionary personKeyToCount) { - (long, PersonContainer)[] results; - const int zero = 0; - List errors = []; - string newestPersonKeyFormatted; - List<(long PersonKey, PersonContainer PersonContainer)> collection = []; + List results = []; + List notMappedAndNotNamedPersonKeys = []; + List notMappedAndWithNamedPersonKeys = []; + List personKeys = IPersonContainer.GetPersonKeys(personContainers); foreach (PersonContainer personContainer in personContainers) { - if (personContainer.Key is null) + if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) continue; - if (!personKeyToPersonContainerCollection.ContainsKey(personContainer.Key.Value)) - personKeyToPersonContainerCollection.Add(personContainer.Key.Value, []); - personKeyToPersonContainerCollection[personContainer.Key.Value].Add(personContainer); - newestPersonKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personContainer.Key.Value); - _ = personKeyFormattedToPersonContainer.TryAdd(newestPersonKeyFormatted, personContainer); + if (personKeys.Contains(personContainer.Key.Value)) + continue; + if (personKeyToCount.ContainsKey(personContainer.Key.Value)) + continue; + if (string.IsNullOrEmpty(personContainer.DisplayDirectoryName) || personContainer.DisplayDirectoryName == configuration.MappingDefaultName) + notMappedAndNotNamedPersonKeys.Add(personContainer); + else + notMappedAndWithNamedPersonKeys.Add(personContainer); } - foreach (KeyValuePair> keyValuePair in personKeyToPersonContainerCollection) - { - if (keyValuePair.Value.Count != 1 && (from l in keyValuePair.Value select l.DisplayDirectoryName).Distinct().Count() != 1) - errors.Add(keyValuePair.Value[zero].DisplayDirectoryName); - collection.Add(new(keyValuePair.Key, keyValuePair.Value[zero])); - } - if (errors.Count > 0) - throw new Exception(string.Join(Environment.NewLine, errors)); - results = (from l in collection orderby l.PersonKey descending select (l.PersonKey, l.PersonContainer)).ToArray(); + results.AddRange(notMappedAndNotNamedPersonKeys); + if (results.Count == 0) + results.AddRange(GetNonSpecificPeopleCollection(configuration, ticks, personKeys, personKeyToCount)); return results; } - private static Dictionary>> GetAll(Configuration configuration, Dictionary> skipCollection, ReadOnlyDictionary personKeyFormattedToPersonContainer, ReadOnlyCollection personKeyFormattedIdThenWholePercentagesCollection) - { - Dictionary>> results = []; - PersonBirthday? personBirthday; - PersonContainer? personContainer; - List? personContainers; - Dictionary>? idTo; - int? linkedAlphaCheck = string.IsNullOrEmpty(configuration.LinkedAlpha) ? null : configuration.LinkedAlpha[0] - 65; - if (personKeyFormattedIdThenWholePercentagesCollection.Count > 0) - { - foreach (PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages in personKeyFormattedIdThenWholePercentagesCollection) - { - personBirthday = IPersonBirthday.GetPersonBirthday(configuration.PersonBirthdayFormat, personKeyFormattedIdThenWholePercentages.PersonKeyFormatted); - if (personBirthday is null) - throw new Exception(); - if (linkedAlphaCheck is not null && personKeyFormattedIdThenWholePercentages.LinksCount is not null && personKeyFormattedIdThenWholePercentages.LinksCount.Value < linkedAlphaCheck.Value) - { - if (!skipCollection.ContainsKey(personKeyFormattedIdThenWholePercentages.Id)) - skipCollection.Add(personKeyFormattedIdThenWholePercentages.Id, []); - skipCollection[personKeyFormattedIdThenWholePercentages.Id].Add((personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName, personKeyFormattedIdThenWholePercentages.WholePercentages)); - continue; - } - if (!personKeyFormattedToPersonContainer.TryGetValue(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted, out personContainer)) - continue; - if (!results.TryGetValue(personKeyFormattedIdThenWholePercentages.Id, out idTo)) - { - results.Add(personKeyFormattedIdThenWholePercentages.Id, []); - if (!results.TryGetValue(personKeyFormattedIdThenWholePercentages.Id, out idTo)) - throw new Exception(); - } - if (!idTo.TryGetValue(personKeyFormattedIdThenWholePercentages.WholePercentages, out personContainers)) - { - idTo.Add(personKeyFormattedIdThenWholePercentages.WholePercentages, []); - if (!idTo.TryGetValue(personKeyFormattedIdThenWholePercentages.WholePercentages, out personContainers)) - throw new Exception(); - } - personContainers.Add(personContainer); - } - } - return results; - } - - private static ReadOnlyDictionary>> GetReadOnly(Dictionary>> idThenWholePercentagesToPersonContainerCollection) - { - Dictionary>> results = []; - List distinct = []; - List personContainers; - Dictionary> keyValuePairs; - foreach (KeyValuePair>> idTo in idThenWholePercentagesToPersonContainerCollection) - { - keyValuePairs = []; - foreach (KeyValuePair> wholePercentagesTo in idThenWholePercentagesToPersonContainerCollection[idTo.Key]) - { - distinct.Clear(); - personContainers = []; - foreach (PersonContainer personContainer in wholePercentagesTo.Value) - { - if (personContainer.Key is null) - throw new Exception(); - if (distinct.Contains(personContainer.Key.Value)) - continue; - personContainers.Add(personContainer); - } - keyValuePairs.Add(wholePercentagesTo.Key, new(personContainers)); - } - results.Add(idTo.Key, new(keyValuePairs)); - } - return results.AsReadOnly(); - } - - private static ReadOnlyDictionary> GetReadOnly(Dictionary> keyValuePairs) - { - Dictionary> results = []; - foreach (KeyValuePair> keyValuePair in keyValuePairs) - results.Add(keyValuePair.Key, new(keyValuePair.Value)); - return results.AsReadOnly(); - } - private static List GetNonSpecificPeopleCollection(Configuration configuration, long ticks, List personKeys, ReadOnlyDictionary personKeyToCount) { List results = []; @@ -449,16 +610,16 @@ internal abstract class MapLogic return results; } - private static string? GetDisplayDirectoryName(Dictionary personKeyToPersonContainer, long key) + internal static (SaveContainer, SaveContainer?) GetContainers(string facesFileNameExtension, string facePartsFileNameExtension, string directory, FileHolder faceFileHolder, FileHolder facePartsFileHolder, Mapping mapping) { - string? result = null; - if (personKeyToPersonContainer.TryGetValue(key, out PersonContainer? personContainer)) - { - result = personContainer.DisplayDirectoryName; - if (string.IsNullOrEmpty(result)) - throw new NotSupportedException(); - } - return result; + string checkFile; + SaveContainer? saveContainer; + if (mapping.MappingFromLocation is null) + throw new NullReferenceException(nameof(mapping.MappingFromLocation)); + checkFile = Path.Combine(directory, $"{mapping.MappingFromItem.FilePath.Name}{facePartsFileNameExtension}"); + saveContainer = !facePartsFileHolder.Exists ? null : new(checkFile, directory, facePartsFileHolder); + checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}"); + return (new(checkFile, directory, faceFileHolder), saveContainer); } internal static List GetMappedFiles(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyCollection personContainers, List records) @@ -522,6 +683,424 @@ internal abstract class MapLogic return results; } + private static List GetDisplayDirectoryAllFiles(string fileNameExtension, string personBirthdayFormat, ReadOnlyCollection personContainers) + { + List results = []; + MappedFile mappedFile; + string personKeyFormatted; + List distinct = []; + foreach (PersonContainer personContainer in personContainers) + { + if (personContainer.Key is null) + continue; + for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--) + { + if (personContainer.DisplayDirectoryAllFilePaths[i].ExtensionLowered != fileNameExtension) + continue; + if (distinct.Contains(personContainer.DisplayDirectoryAllFilePaths[i].Name)) + continue; + distinct.Add(personContainer.DisplayDirectoryAllFilePaths[i].Name); + personKeyFormatted = IPersonBirthday.GetFormatted(personBirthdayFormat, personContainer.Key.Value); + mappedFile = new(PersonKey: personContainer.Key.Value, + PersonKeyFormatted: personKeyFormatted, + PersonDisplayDirectoryName: personContainer.DisplayDirectoryName, + DirectoryNumber: null, + FilePath: personContainer.DisplayDirectoryAllFilePaths[i]); + results.Add(mappedFile); + } + } + return results; + } + + internal static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) + { + List<(string, long)> results; + string[] files; + string? foundPath; + int totalFiles = 0; + string checkDirectory; + WindowsShortcut windowsShortcut; + List resolvedDirectories = []; + if (string.IsNullOrEmpty(genealogicalDataCommunicationFile)) + results = GetDirectoryAndTicksCollection(jLinks, personBirthdayFormat, a2PeopleContentDirectory); + else + results = GetGenealogicalDataCommunicationDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat); + if (results.Count == 0 || results.Count < jLinks.Length) + { + List<(string, string, string)> a2PeopleSingletonDirectories = []; + foreach (string directory in Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.AllDirectories)) + a2PeopleSingletonDirectories.Add((directory, Path.GetFileName(directory), Path.GetFileName(directory).Split(personCharacters).First())); + foreach (string directoryName in jLinks) + { + checkDirectory = Path.Combine(a2PeopleContentDirectory, directoryName); + if (!Directory.Exists(checkDirectory)) + continue; + files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly); + totalFiles += files.Length; + foreach (string file in files) + { + windowsShortcut = WindowsShortcut.Load(file); + if (windowsShortcut.Path is null) + continue; + if (Directory.Exists(windowsShortcut.Path)) + resolvedDirectories.Add(windowsShortcut.Path); + else + { + foundPath = TryToFind(personCharacters, a2PeopleSingletonDirectory, a2PeopleSingletonDirectories, file, windowsShortcut.Path); + if (string.IsNullOrEmpty(foundPath)) + continue; + resolvedDirectories.Add(foundPath); + } + } + } + if (totalFiles == resolvedDirectories.Count) + results = GetJLinkResolvedDirectories(personBirthdayFormat, resolvedDirectories); + else + { + resolvedDirectories.Clear(); + results = []; + } + } + return results; + } + + private static List<(string, long)> GetDirectoryAndTicksCollection(string[] jLinks, string personBirthdayFormat, string? rootDirectory) + { + List<(string, long)> results = []; + string directory; + DateTime dateTime; + string[] personKeyDirectories; + string[] personDisplayDirectoryNames; + string personKeyFormattedDirectoryName; + foreach (string jLink in jLinks) + { + if (rootDirectory is null) + continue; + directory = Path.Combine(rootDirectory, jLink); + if (!Directory.Exists(directory)) + continue; + personDisplayDirectoryNames = Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly); + foreach (string personDisplayDirectoryName in personDisplayDirectoryNames) + { + personKeyDirectories = Directory.GetDirectories(personDisplayDirectoryName, "*", SearchOption.TopDirectoryOnly); + foreach (string personKeyFormattedDirectory in personKeyDirectories) + { + personKeyFormattedDirectoryName = Path.GetFileName(personKeyFormattedDirectory); + if (personKeyFormattedDirectoryName.Length != personBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormattedDirectoryName, personBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + continue; + results.Add((directory, dateTime.Ticks)); + } + } + } + return results; + } + + private static List<(string, long)> GetGenealogicalDataCommunicationDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat) + { + List<(string, long)> results; + string? genealogicalDataCommunicationDirectory = Path.GetDirectoryName(genealogicalDataCommunicationFile); + results = GetDirectoryAndTicksCollection(jLinks, personBirthdayFormat, genealogicalDataCommunicationDirectory); + return results; + } + + private static string? TryToFind(char[] personCharacters, string a2PeopleSingletonDirectory, List<(string Directory, string DirectoryName, string DirectoryNameSplitFirst)> a2PeopleSingletonDirectories, string file, string path) + { + string? result; + string? pathName = Path.GetFileName(path); + string? group = Path.GetDirectoryName(path); + string? groupName = Path.GetFileName(group); + if (pathName is null || group is null || groupName is null) + result = null; + else + { + string[] matches; + matches = (from l in a2PeopleSingletonDirectories where l.DirectoryName == pathName select l.Directory).ToArray(); + if (matches.Length == 1) + result = matches.First(); + else + { + string pathNameSplitFirst = pathName.Split(personCharacters).First(); + matches = (from l in a2PeopleSingletonDirectories where l.DirectoryNameSplitFirst == pathNameSplitFirst select l.Directory).ToArray(); + if (matches.Length == 1) + result = matches.First(); + else + { + string checkDirectory = Path.Combine(a2PeopleSingletonDirectory, groupName, pathName); + if (!Directory.Exists(checkDirectory)) + result = null; + else + result = checkDirectory; + } + } + if (!string.IsNullOrEmpty(result)) + { + try + { + WindowsShortcut windowsShortcut; + windowsShortcut = new() { Path = result }; + windowsShortcut.Save(file); + windowsShortcut.Dispose(); + } + catch (Exception) + { } + } + } + return result; + } + + private static List<(string, long)> GetJLinkResolvedDirectories(string personBirthdayFormat, List resolvedDirectories) + { + List<(string, long)> results = []; + DateTime dateTime; + string directoryName; + string[] directories; + foreach (string resolvedDirectory in resolvedDirectories) + { + directories = Directory.GetDirectories(resolvedDirectory, "*", SearchOption.TopDirectoryOnly); + foreach (string directory in directories) + { + directoryName = Path.GetFileName(directory); + if (directoryName.Length != personBirthdayFormat.Length || !DateTime.TryParseExact(directoryName, personBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + continue; + results.Add((resolvedDirectory, dateTime.Ticks)); + } + } + if (results.Count != resolvedDirectories.Count) + results.Clear(); + return results; + } + + internal static void SetPersonCollectionsAfterSetSkipCollections(Configuration configuration, ReadOnlyCollection personContainers, Dictionary personKeyFormattedToNewestPersonKeyFormatted, List personKeyFormattedCollection) + { + string personKeyFormatted; + string newestPersonKeyFormatted; + foreach (PersonContainer personContainer in personContainers) + { + if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) + continue; + foreach (PersonBirthday personBirthday in personContainer.Birthdays) + { + personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday); + personKeyFormattedCollection.Add(personKeyFormatted); + if (personContainer.Birthdays.Length < 1) + continue; + newestPersonKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personContainer.Key.Value); + _ = personKeyFormattedToNewestPersonKeyFormatted.TryAdd(personKeyFormatted, newestPersonKeyFormatted); + } + } + } + + internal static void SetSkipCollections(Configuration configuration, ReadOnlyCollection personContainers, string? a2PeopleSingletonDirectory, Dictionary> skipCollection, Dictionary> skipNotSkipCollection) + { + string checkFile; + int? wholePercentages; + List distinct = []; + List distinctFiles = []; + List distinctFileName = []; + bool skipNotSkipDirectoriesAny = configuration.SkipNotSkipDirectories.Length > 0; + string[] checkDirectories = (from l in configuration.SkipNotSkipDirectories select Path.GetFullPath($"{a2PeopleSingletonDirectory}{l}")).ToArray(); + foreach (PersonContainer personContainer in personContainers) + { + foreach (FilePath personDisplayDirectoryAllFilePath in personContainer.DisplayDirectoryAllFilePaths) + { + if (personDisplayDirectoryAllFilePath.ExtensionLowered != configuration.FacesFileNameExtension) + continue; + if (distinctFiles.Contains(personDisplayDirectoryAllFilePath.FullName)) + continue; + distinctFiles.Add(personDisplayDirectoryAllFilePath.FullName); + distinct.Add(personDisplayDirectoryAllFilePath); + } + } + foreach (FilePath filePath in distinct) + { + if (distinctFileName.Contains(filePath.Name)) + { + checkFile = $"{filePath.FullName}.dup"; + if (File.Exists(checkFile)) + continue; + File.Move(filePath.FullName, checkFile); + continue; + } + if (filePath.Id is null) + continue; + wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath); + if (wholePercentages is null) + continue; + if (!skipNotSkipDirectoriesAny || !checkDirectories.Any(filePath.FullName.StartsWith)) + { + if (!skipCollection.ContainsKey(filePath.Id.Value)) + skipCollection.Add(filePath.Id.Value, []); + skipCollection[filePath.Id.Value].Add((filePath.FullName, wholePercentages.Value)); + } + else + { + if (!skipNotSkipCollection.ContainsKey(filePath.Id.Value)) + skipNotSkipCollection.Add(filePath.Id.Value, []); + skipNotSkipCollection[filePath.Id.Value].Add((filePath.FullName, wholePercentages.Value)); + } + } + } + + internal static (SaveContainer?, SaveContainer?) GetContainers(string facesFileNameExtension, string facePartsFileNameExtension, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string dFacesContentDirectory, string d2FacePartsContentCollectionDirectory, string directory, Mapping keyMapping) + { + SaveContainer? result; + SaveContainer? saveContainer; + if (keyMapping.MappingFromLocation is null) + (result, saveContainer) = (null, null); + else + { + string? facePartsContentCollectionFile = GetFacePartsContentCollectionFile(facePartsFileNameExtension, d2FacePartsContentCollectionDirectory, keyMapping.MappingFromItem); + if (facePartsContentCollectionFile is null || !File.Exists(facePartsContentCollectionFile)) + result = null; + else + { + string checkFile = Path.Combine(directory, $"{keyMapping.MappingFromItem.FilePath.Name}{facePartsFileNameExtension}"); + result = new(checkFile, directory, IFileHolder.Get(facePartsContentCollectionFile)); + } + string facesDirectory = GetFacesDirectory(propertyConfiguration, dFacesContentDirectory, keyMapping.FilePath, keyMapping.MappingFromItem); + FileHolder faceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}")); + if (!faceFileHolder.Exists) + saveContainer = null; + else + { + string checkFile = Path.Combine(directory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}"); + saveContainer = new(checkFile, directory, faceFileHolder); + } + } + return (result, saveContainer); + } + + internal static void PossiblyRebuildPersonContainers(Configuration configuration, long ticks, string? a2PeopleSingletonDirectory, ReadOnlyDictionary readOnlyPersonKeyToCount, ReadOnlyCollection<(PersonKeyFormattedIdThenWholePercentages, PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer) + { + int count; + long personKey; + bool[] matches; + string fileName; + string checkFile; + const int zero = 0; + string personKeyFormatted; + List distinct = []; + FilePath[] deleteCollection; + PersonBirthday personBirthday; + string personDisplayDirectory; + DateTime dateTime = new(ticks); + string personKeyFormattedDirectory; + foreach ((PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages, PersonContainer personContainer) in possiblyNewPersonDisplayDirectoryNamesAndPersonContainer) + { + if (distinct.Contains(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted)) + continue; + if (a2PeopleSingletonDirectory is null || personContainer.Key is null || personContainer.Birthdays is null || personContainer.PersonDirectory is null || personContainer.Birthdays.Length == 0) + continue; + fileName = $"{Path.GetFileName(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName)}{configuration.FacesHiddenFileNameExtension}"; + personBirthday = personContainer.Birthdays[zero]; + personKey = personBirthday.Value.Ticks; + personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday); + personDisplayDirectory = Path.Combine(a2PeopleSingletonDirectory, personContainer.PersonDirectory.Char.ToString(), personContainer.PersonDirectory.Group, personContainer.DisplayDirectoryName); + personKeyFormattedDirectory = Path.GetFullPath(Path.Combine(personDisplayDirectory, personKeyFormatted)); + deleteCollection = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.StartsWith(personKeyFormattedDirectory) select l).ToArray(); + if (personContainer.DisplayDirectoryAllFilePaths.Count != 0 && deleteCollection.Length == 0) + throw new NotSupportedException(); + if (!Directory.Exists(personKeyFormattedDirectory)) + _ = Directory.CreateDirectory(personKeyFormattedDirectory); + _ = readOnlyPersonKeyToCount.TryGetValue(personKey, out count); + _ = Directory.CreateDirectory(Path.Combine(personDisplayDirectory, count.ToString("0000"))); + Directory.SetLastWriteTime(personDisplayDirectory, dateTime.AddMinutes(count)); + matches = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.EndsWith(fileName) select true).ToArray(); + if (matches.Length > 0) + continue; + matches = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.EndsWith(configuration.FacesHiddenFileNameExtension) select true).ToArray(); + if (matches.Length > 0) + continue; + if (!File.Exists(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName)) + continue; + checkFile = Path.Combine(personKeyFormattedDirectory, $"{Path.GetFileName(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName)}{configuration.FacesHiddenFileNameExtension}"); + if (File.Exists(checkFile)) + continue; + File.Copy(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName, checkFile); + foreach (FilePath delete in deleteCollection) + { + if (delete.ExtensionLowered == ".lnk") + continue; + if (!File.Exists(delete.FullName)) + continue; + File.Delete(delete.FullName); + } + Directory.SetLastWriteTime(personDisplayDirectory, DateTime.Now); + distinct.Add(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted); + } + } + + internal static void SetPersonKeyToPersonContainer(Configuration configuration, ReadOnlyCollection personContainers, ReadOnlyDictionary personKeyToCount, Dictionary personKeyToPersonContainer, ReadOnlyDictionary> personKeyToPersonContainerCollection) + { + string? displayDirectoryName; + foreach (PersonContainer personContainer in personContainers) + { + if (personContainer.Key is null || !personKeyToCount.ContainsKey(personContainer.Key.Value)) + continue; + displayDirectoryName = GetDisplayDirectoryName(personKeyToPersonContainer, personContainer.Key.Value); + if (displayDirectoryName is not null && (displayDirectoryName == personContainer.DisplayDirectoryName || (displayDirectoryName[0] == personContainer.DisplayDirectoryName[0] && (displayDirectoryName.Length == 1 || personContainer.DisplayDirectoryName.Length == 1)))) + continue; + personKeyToPersonContainer.Add(personContainer.Key.Value, personContainer); + } + if (personKeyToCount.Count > 0) + { + const int zero = 0; + int? approximateYears = null; + PersonBirthday? personBirthday; + PersonContainer personContainer; + List? collection; + displayDirectoryName = configuration.MappingDefaultName; + foreach (KeyValuePair keyValuePair in personKeyToCount) + { + if (personKeyToPersonContainer.ContainsKey(keyValuePair.Key)) + continue; + personBirthday = IPersonBirthday.GetPersonBirthday(keyValuePair.Key); + if (!personKeyToPersonContainerCollection.TryGetValue(keyValuePair.Key, out collection)) + personContainer = PersonContainer.Get(approximateYears, personBirthday, displayDirectoryName, keyValuePair.Key); + else + personContainer = PersonContainer.Get(approximateYears, personBirthday, collection[zero].PersonDirectory, displayDirectoryName, keyValuePair.Key); + personKeyToPersonContainer.Add(keyValuePair.Key, personContainer); + } + } + } + + private static string? GetDisplayDirectoryName(Dictionary personKeyToPersonContainer, long key) + { + string? result = null; + if (personKeyToPersonContainer.TryGetValue(key, out PersonContainer? personContainer)) + { + result = personContainer.DisplayDirectoryName; + if (string.IsNullOrEmpty(result)) + throw new NotSupportedException(); + } + return result; + } + + internal static List GetLocationContainers(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, ReadOnlyDictionary> skipCollection, List records) + { + List results = []; + List mappedFiles = GetMappedFiles(propertyConfiguration, configuration, personContainers, records); + if (mappedFiles.Count > 0 && (configuration.DistanceMoveUnableToMatch || configuration.DistanceRenameToMatch)) + { + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)"; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using ProgressBar progressBar = new(mappedFiles.Count, message, options); + _ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) => + { + progressBar.Tick(); + ParallelFor(propertyConfiguration, configuration, skipCollection, results, mappedFiles[i]); + }); + } + if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) + { + ReadOnlyCollection locationContainers = new(results.OrderBy(l => l.DirectoryNumber).ToArray()); + LookForPossibleDuplicates(configuration, locationContainers); + } + return results; + } + private static void ParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyDictionary> skipCollection, List locationContainers, MappedFile mappedFile) { int? id; @@ -641,113 +1220,80 @@ internal abstract class MapLogic } } - internal static (SaveContainer?, SaveContainer?) GetContainers(string facesFileNameExtension, string facePartsFileNameExtension, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string dFacesContentDirectory, string d2FacePartsContentCollectionDirectory, string directory, Mapping keyMapping) + private static void OpenPossibleDuplicates(Configuration configuration, List duplicates) { - SaveContainer? result; - SaveContainer? saveContainer; - if (keyMapping.MappingFromLocation is null) - (result, saveContainer) = (null, null); - else + string personKeyFormatted; + foreach (Duplicate duplicate in duplicates) { - string? facePartsContentCollectionFile = GetFacePartsContentCollectionFile(facePartsFileNameExtension, d2FacePartsContentCollectionDirectory, keyMapping.MappingFromItem); - if (facePartsContentCollectionFile is null || !File.Exists(facePartsContentCollectionFile)) - result = null; - else - { - string checkFile = Path.Combine(directory, $"{keyMapping.MappingFromItem.FilePath.Name}{facePartsFileNameExtension}"); - result = new(checkFile, directory, IFileHolder.Get(facePartsContentCollectionFile)); - } - string facesDirectory = GetFacesDirectory(propertyConfiguration, dFacesContentDirectory, keyMapping.FilePath, keyMapping.MappingFromItem); - FileHolder faceFileHolder = IFileHolder.Get(Path.Combine(facesDirectory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}")); - if (!faceFileHolder.Exists) - saveContainer = null; - else - { - string checkFile = Path.Combine(directory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}"); - saveContainer = new(checkFile, directory, faceFileHolder); - } + if (duplicate.Percent is null) + continue; + _ = Process.Start("explorer.exe", string.Concat("\"", duplicate.FilePath.DirectoryFullPath, "\"")); + personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, duplicate.PersonKey); + } + foreach ((long personKey, int id, FilePath filePath, float? percent) in duplicates) + { + if (percent is not null && percent.Value == 0) + continue; + _ = Process.Start("explorer.exe", string.Concat("\"", filePath.DirectoryFullPath, "\"")); + personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey); + } + foreach ((long personKey, int id, FilePath filePath, float? percent) in duplicates) + { + if (percent is not null && percent.Value > 0) + continue; + _ = Process.Start("explorer.exe", string.Concat("\"", filePath.DirectoryFullPath, "\"")); + personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personKey); } - return (result, saveContainer); } - private static IEnumerable<(string, string)> GetCollection(string[] yearDirectories) + internal static ReadOnlyDictionary>> GetIdThenWholePercentagesToPersonContainers(Configuration configuration, Dictionary> skipCollection, ReadOnlyDictionary personKeyFormattedToPersonContainer, ReadOnlyCollection personKeyFormattedIdThenWholePercentagesCollection) { - foreach (string l in yearDirectories) - yield return new(l, Path.GetFileName(l)); - } - - private static List<(string, long)> GetGenealogicalDataCommunicationDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat) - { - List<(string, long)> results; - string? genealogicalDataCommunicationDirectory = Path.GetDirectoryName(genealogicalDataCommunicationFile); - results = GetDirectoryAndTicksCollection(jLinks, personBirthdayFormat, genealogicalDataCommunicationDirectory); + ReadOnlyDictionary>> results; + Dictionary>> idThenWholePercentagesToPersonContainerCollection; + idThenWholePercentagesToPersonContainerCollection = GetAll(configuration, skipCollection, personKeyFormattedToPersonContainer, personKeyFormattedIdThenWholePercentagesCollection); + results = GetReadOnly(idThenWholePercentagesToPersonContainerCollection); return results; } - private static string? TryToFind(char[] personCharacters, string a2PeopleSingletonDirectory, List<(string Directory, string DirectoryName, string DirectoryNameSplitFirst)> a2PeopleSingletonDirectories, string file, string path) + private static Dictionary>> GetAll(Configuration configuration, Dictionary> skipCollection, ReadOnlyDictionary personKeyFormattedToPersonContainer, ReadOnlyCollection personKeyFormattedIdThenWholePercentagesCollection) { - string? result; - string? pathName = Path.GetFileName(path); - string? group = Path.GetDirectoryName(path); - string? groupName = Path.GetFileName(group); - if (pathName is null || group is null || groupName is null) - result = null; - else + Dictionary>> results = []; + PersonBirthday? personBirthday; + PersonContainer? personContainer; + List? personContainers; + Dictionary>? idTo; + int? linkedAlphaCheck = string.IsNullOrEmpty(configuration.LinkedAlpha) ? null : configuration.LinkedAlpha[0] - 65; + if (personKeyFormattedIdThenWholePercentagesCollection.Count > 0) { - string[] matches; - matches = (from l in a2PeopleSingletonDirectories where l.DirectoryName == pathName select l.Directory).ToArray(); - if (matches.Length == 1) - result = matches.First(); - else + foreach (PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages in personKeyFormattedIdThenWholePercentagesCollection) { - string pathNameSplitFirst = pathName.Split(personCharacters).First(); - matches = (from l in a2PeopleSingletonDirectories where l.DirectoryNameSplitFirst == pathNameSplitFirst select l.Directory).ToArray(); - if (matches.Length == 1) - result = matches.First(); - else + personBirthday = IPersonBirthday.GetPersonBirthday(configuration.PersonBirthdayFormat, personKeyFormattedIdThenWholePercentages.PersonKeyFormatted); + if (personBirthday is null) + throw new Exception(); + if (linkedAlphaCheck is not null && personKeyFormattedIdThenWholePercentages.LinksCount is not null && personKeyFormattedIdThenWholePercentages.LinksCount.Value < linkedAlphaCheck.Value) { - string checkDirectory = Path.Combine(a2PeopleSingletonDirectory, groupName, pathName); - if (!Directory.Exists(checkDirectory)) - result = null; - else - result = checkDirectory; - } - } - if (!string.IsNullOrEmpty(result)) - { - try - { - WindowsShortcut windowsShortcut; - windowsShortcut = new() { Path = result }; - windowsShortcut.Save(file); - windowsShortcut.Dispose(); - } - catch (Exception) - { } - } - } - return result; - } - - private static List<(string, long)> GetJLinkResolvedDirectories(string personBirthdayFormat, List resolvedDirectories) - { - List<(string, long)> results = []; - DateTime dateTime; - string directoryName; - string[] directories; - foreach (string resolvedDirectory in resolvedDirectories) - { - directories = Directory.GetDirectories(resolvedDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string directory in directories) - { - directoryName = Path.GetFileName(directory); - if (directoryName.Length != personBirthdayFormat.Length || !DateTime.TryParseExact(directoryName, personBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + if (!skipCollection.ContainsKey(personKeyFormattedIdThenWholePercentages.Id)) + skipCollection.Add(personKeyFormattedIdThenWholePercentages.Id, []); + skipCollection[personKeyFormattedIdThenWholePercentages.Id].Add((personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName, personKeyFormattedIdThenWholePercentages.WholePercentages)); continue; - results.Add((resolvedDirectory, dateTime.Ticks)); + } + if (!personKeyFormattedToPersonContainer.TryGetValue(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted, out personContainer)) + continue; + if (!results.TryGetValue(personKeyFormattedIdThenWholePercentages.Id, out idTo)) + { + results.Add(personKeyFormattedIdThenWholePercentages.Id, []); + if (!results.TryGetValue(personKeyFormattedIdThenWholePercentages.Id, out idTo)) + throw new Exception(); + } + if (!idTo.TryGetValue(personKeyFormattedIdThenWholePercentages.WholePercentages, out personContainers)) + { + idTo.Add(personKeyFormattedIdThenWholePercentages.WholePercentages, []); + if (!idTo.TryGetValue(personKeyFormattedIdThenWholePercentages.WholePercentages, out personContainers)) + throw new Exception(); + } + personContainers.Add(personContainer); } } - if (results.Count != resolvedDirectories.Count) - results.Clear(); return results; } @@ -792,587 +1338,33 @@ internal abstract class MapLogic } } - internal static ReadOnlyDictionary>> GetIdThenWholePercentagesToPersonContainers(Configuration configuration, Dictionary> skipCollection, ReadOnlyDictionary personKeyFormattedToPersonContainer, ReadOnlyCollection personKeyFormattedIdThenWholePercentagesCollection) + private static (long, PersonContainer)[] GetDistinctCollection(Configuration configuration, IEnumerable personContainers, Dictionary> personKeyToPersonContainerCollection, Dictionary personKeyFormattedToPersonContainer) { - ReadOnlyDictionary>> results; - Dictionary>> idThenWholePercentagesToPersonContainerCollection; - idThenWholePercentagesToPersonContainerCollection = GetAll(configuration, skipCollection, personKeyFormattedToPersonContainer, personKeyFormattedIdThenWholePercentagesCollection); - results = GetReadOnly(idThenWholePercentagesToPersonContainerCollection); - return results; - } - - internal static ReadOnlyCollection GetPersonKeyFormattedIdThenWholePercentages(Configuration configuration, long ticks, List records) - { - List results = []; - int? wholePercentages; - List wholePercentagesCollection; - Dictionary> idToWholePercentagesCollection = []; - PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages; - int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - string message = $") {records.Count:000} join from ticks Director(ies) - C - {totalSeconds} total second(s)"; - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(records.Count, message, options); - foreach (Record record in records) - { - progressBar.Tick(); - if (record.MappedFaceFilePath.Id is null) - continue; - wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, record.MappedFaceFilePath); - if (wholePercentages is null) - continue; - if (!idToWholePercentagesCollection.ContainsKey(record.MappedFaceFilePath.Id.Value)) - idToWholePercentagesCollection.Add(record.MappedFaceFilePath.Id.Value, []); - wholePercentagesCollection = idToWholePercentagesCollection[record.MappedFaceFilePath.Id.Value]; - wholePercentagesCollection.Add(wholePercentages.Value); - idToWholePercentagesCollection[record.MappedFaceFilePath.Id.Value].Add(wholePercentages.Value); - personKeyFormattedIdThenWholePercentages = new(PersonKeyFormatted: record.PersonKeyFormatted, - PersonDisplayDirectoryName: record.PersonDisplayDirectoryName, - IsDefault: record.IsDefault, - LinksCount: record.LinksCount, - MappedFaceFilePath: record.MappedFaceFilePath, - Id: record.MappedFaceFilePath.Id.Value, - WholePercentages: wholePercentages.Value); - results.Add(personKeyFormattedIdThenWholePercentages); - } - return results.AsReadOnly(); - } - - internal static List GetNotMappedPersonContainers(Configuration configuration, long ticks, ReadOnlyCollection personContainers, ReadOnlyDictionary personKeyToCount) - { - List results = []; - List notMappedAndNotNamedPersonKeys = []; - List notMappedAndWithNamedPersonKeys = []; - List personKeys = IPersonContainer.GetPersonKeys(personContainers); - foreach (PersonContainer personContainer in personContainers) - { - if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0) - continue; - if (personKeys.Contains(personContainer.Key.Value)) - continue; - if (personKeyToCount.ContainsKey(personContainer.Key.Value)) - continue; - if (string.IsNullOrEmpty(personContainer.DisplayDirectoryName) || personContainer.DisplayDirectoryName == configuration.MappingDefaultName) - notMappedAndNotNamedPersonKeys.Add(personContainer); - else - notMappedAndWithNamedPersonKeys.Add(personContainer); - } - results.AddRange(notMappedAndNotNamedPersonKeys); - if (results.Count == 0) - results.AddRange(GetNonSpecificPeopleCollection(configuration, ticks, personKeys, personKeyToCount)); - return results; - } - - internal static void SetPersonKeyToPersonContainer(Configuration configuration, ReadOnlyCollection personContainers, ReadOnlyDictionary personKeyToCount, Dictionary personKeyToPersonContainer, ReadOnlyDictionary> personKeyToPersonContainerCollection) - { - string? displayDirectoryName; - foreach (PersonContainer personContainer in personContainers) - { - if (personContainer.Key is null || !personKeyToCount.ContainsKey(personContainer.Key.Value)) - continue; - displayDirectoryName = GetDisplayDirectoryName(personKeyToPersonContainer, personContainer.Key.Value); - if (displayDirectoryName is not null && (displayDirectoryName == personContainer.DisplayDirectoryName || (displayDirectoryName[0] == personContainer.DisplayDirectoryName[0] && (displayDirectoryName.Length == 1 || personContainer.DisplayDirectoryName.Length == 1)))) - continue; - personKeyToPersonContainer.Add(personContainer.Key.Value, personContainer); - } - if (personKeyToCount.Count > 0) - { - const int zero = 0; - int? approximateYears = null; - PersonBirthday? personBirthday; - PersonContainer personContainer; - List? collection; - displayDirectoryName = configuration.MappingDefaultName; - foreach (KeyValuePair keyValuePair in personKeyToCount) - { - if (personKeyToPersonContainer.ContainsKey(keyValuePair.Key)) - continue; - personBirthday = IPersonBirthday.GetPersonBirthday(keyValuePair.Key); - if (!personKeyToPersonContainerCollection.TryGetValue(keyValuePair.Key, out collection)) - personContainer = PersonContainer.Get(approximateYears, personBirthday, displayDirectoryName, keyValuePair.Key); - else - personContainer = PersonContainer.Get(approximateYears, personBirthday, collection[zero].PersonDirectory, displayDirectoryName, keyValuePair.Key); - personKeyToPersonContainer.Add(keyValuePair.Key, personContainer); - } - } - } - - internal static void PossiblyRebuildPersonContainers(Configuration configuration, long ticks, string? a2PeopleSingletonDirectory, ReadOnlyDictionary readOnlyPersonKeyToCount, ReadOnlyCollection<(PersonKeyFormattedIdThenWholePercentages, PersonContainer)> possiblyNewPersonDisplayDirectoryNamesAndPersonContainer) - { - int count; - long personKey; - bool[] matches; - string fileName; - string checkFile; + (long, PersonContainer)[] results; const int zero = 0; - string personKeyFormatted; - List distinct = []; - FilePath[] deleteCollection; - PersonBirthday personBirthday; - string personDisplayDirectory; - DateTime dateTime = new(ticks); - string personKeyFormattedDirectory; - foreach ((PersonKeyFormattedIdThenWholePercentages personKeyFormattedIdThenWholePercentages, PersonContainer personContainer) in possiblyNewPersonDisplayDirectoryNamesAndPersonContainer) - { - if (distinct.Contains(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted)) - continue; - if (a2PeopleSingletonDirectory is null || personContainer.Key is null || personContainer.Birthdays is null || personContainer.PersonDirectory is null || personContainer.Birthdays.Length == 0) - continue; - fileName = $"{Path.GetFileName(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName)}{configuration.FacesHiddenFileNameExtension}"; - personBirthday = personContainer.Birthdays[zero]; - personKey = personBirthday.Value.Ticks; - personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday); - personDisplayDirectory = Path.Combine(a2PeopleSingletonDirectory, personContainer.PersonDirectory.Char.ToString(), personContainer.PersonDirectory.Group, personContainer.DisplayDirectoryName); - personKeyFormattedDirectory = Path.GetFullPath(Path.Combine(personDisplayDirectory, personKeyFormatted)); - deleteCollection = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.StartsWith(personKeyFormattedDirectory) select l).ToArray(); - if (personContainer.DisplayDirectoryAllFilePaths.Count != 0 && deleteCollection.Length == 0) - throw new NotSupportedException(); - if (!Directory.Exists(personKeyFormattedDirectory)) - _ = Directory.CreateDirectory(personKeyFormattedDirectory); - _ = readOnlyPersonKeyToCount.TryGetValue(personKey, out count); - _ = Directory.CreateDirectory(Path.Combine(personDisplayDirectory, count.ToString("0000"))); - Directory.SetLastWriteTime(personDisplayDirectory, dateTime.AddMinutes(count)); - matches = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.EndsWith(fileName) select true).ToArray(); - if (matches.Length > 0) - continue; - matches = (from l in personContainer.DisplayDirectoryAllFilePaths where l.FullName.EndsWith(configuration.FacesHiddenFileNameExtension) select true).ToArray(); - if (matches.Length > 0) - continue; - if (!File.Exists(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName)) - continue; - checkFile = Path.Combine(personKeyFormattedDirectory, $"{Path.GetFileName(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName)}{configuration.FacesHiddenFileNameExtension}"); - if (File.Exists(checkFile)) - continue; - File.Copy(personKeyFormattedIdThenWholePercentages.MappedFaceFilePath.FullName, checkFile); - foreach (FilePath delete in deleteCollection) - { - if (delete.ExtensionLowered == ".lnk") - continue; - if (!File.Exists(delete.FullName)) - continue; - File.Delete(delete.FullName); - } - Directory.SetLastWriteTime(personDisplayDirectory, DateTime.Now); - distinct.Add(personKeyFormattedIdThenWholePercentages.PersonKeyFormatted); - } - } - - internal static List GetLocationContainers(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, ReadOnlyDictionary> skipCollection, List records) - { - List results = []; - List mappedFiles = GetMappedFiles(propertyConfiguration, configuration, personContainers, records); - if (mappedFiles.Count > 0 && (configuration.DistanceMoveUnableToMatch || configuration.DistanceRenameToMatch)) - { - int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)"; - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(mappedFiles.Count, message, options); - _ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) => - { - progressBar.Tick(); - ParallelFor(propertyConfiguration, configuration, skipCollection, results, mappedFiles[i]); - }); - } - if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) - { - ReadOnlyCollection locationContainers = new(results.OrderBy(l => l.DirectoryNumber).ToArray()); - LookForPossibleDuplicates(configuration, locationContainers); - } - return results; - } - - internal static int CopyManualFiles(Configuration configuration, long ticks, ReadOnlyCollection personContainers, string eDistanceContentTicksDirectory) - { - int result = 0; - string checkFile; - string dateDirectory; - string directoryName; - string checkDirectory; - string personKeyFormatted; - List distinct = []; - PersonBirthday personBirthday; - DateTime dateTime = new(ticks); - string by = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy); + List errors = []; + string newestPersonKeyFormatted; + List<(long PersonKey, PersonContainer PersonContainer)> collection = []; foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null) continue; - for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--) - { - if (personContainer.DisplayDirectoryAllFilePaths[i].ExtensionLowered != configuration.FacesFileNameExtension) - continue; - if (distinct.Contains(personContainer.DisplayDirectoryAllFilePaths[i].Name)) - continue; - distinct.Add(personContainer.DisplayDirectoryAllFilePaths[i].Name); - directoryName = Path.GetFileName(personContainer.DisplayDirectoryAllFilePaths[i].DirectoryFullPath); - if (directoryName != personContainer.DisplayDirectoryName) - continue; - personBirthday = IPersonBirthday.GetPersonBirthday(personContainer.Key.Value); - personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personBirthday); - dateDirectory = Path.Combine(eDistanceContentTicksDirectory, by, personKeyFormatted, dateTime.ToString("yyyy")); - checkDirectory = Path.Combine(dateDirectory, personContainer.DisplayDirectoryName); - if (!Directory.Exists(checkDirectory)) - _ = Directory.CreateDirectory(checkDirectory); - checkFile = Path.Combine(dateDirectory, personContainer.DisplayDirectoryAllFilePaths[i].Name); - if (File.Exists(checkFile)) - continue; - File.Move(personContainer.DisplayDirectoryAllFilePaths[i].FullName, checkFile); - throw new NotImplementedException("readonly null?"); - // personContainer.DisplayDirectoryAllFilePaths[i] = null; - // result++; - } + if (!personKeyToPersonContainerCollection.ContainsKey(personContainer.Key.Value)) + personKeyToPersonContainerCollection.Add(personContainer.Key.Value, []); + personKeyToPersonContainerCollection[personContainer.Key.Value].Add(personContainer); + newestPersonKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, personContainer.Key.Value); + _ = personKeyFormattedToPersonContainer.TryAdd(newestPersonKeyFormatted, personContainer); } - return result; - } - - internal static string? GetFacePartsContentCollectionFile(string extension, string d2FacePartsContentCollectionDirectory, MappingFromItem mappingFromItem) - { - string? result; - string? directoryName = Path.GetDirectoryName(mappingFromItem.RelativePath); - if (directoryName is null) - result = null; - else - result = Path.Combine($"{d2FacePartsContentCollectionDirectory}{directoryName}", $"{mappingFromItem.FilePath.Name}{extension}"); - return result; - } - - internal static string GetFacePartsDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string d2FacePartsContentDirectory, FilePath filePath, MappingFromItem mappingFromItem) - { - string result; - CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); - result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, mappingFromItem.FilePath.NameWithoutExtension); - MoveIf(mappingFromItem.FilePath.NameWithoutExtension, cei, d2FacePartsContentDirectory, result); - return result; - } - - internal static string GetFacePartsDirectoryX(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string d2FacePartsContentDirectory, FilePath filePath) - { - string result; - CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); - result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, filePath.NameWithoutExtension); - MoveIf(filePath.NameWithoutExtension, cei, d2FacePartsContentDirectory, result); - return result; - } - - internal static string GetResizeContentDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string cContentDirectory, FilePath filePath) - { - string result; - CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); - result = Path.Combine(cContentDirectory, cei.Combined); - return result; - } - - internal static SaveContainer GetDebugSaveContainer(SortingContainer sortingContainer, string directory, Mapping keyMapping) - { - SaveContainer result; - string shortcutFile; - if (sortingContainer?.Source.MappingFromLocation is null) - throw new NullReferenceException(nameof(sortingContainer.Source.MappingFromLocation)); - FileHolder faceFileHolder = IFileHolder.Get($"C:/{sortingContainer.Sorting.Id}.{sortingContainer.Sorting.WholePercentages}"); - if (keyMapping.MappingFromPerson is not null && keyMapping.MappingFromLocation is not null) - shortcutFile = Path.Combine(directory, $"{keyMapping.MappingFromLocation.DeterministicHashCodeKey}{keyMapping.MappingFromItem.FilePath.ExtensionLowered}.{sortingContainer.Sorting.DistancePermyriad}.lnk"); - else - shortcutFile = Path.Combine(directory, $"{sortingContainer.Source.MappingFromLocation.DeterministicHashCodeKey}{sortingContainer.Source.MappingFromItem.FilePath.ExtensionLowered}.{sortingContainer.Sorting.DistancePermyriad}.lnk"); - result = new(directory, faceFileHolder, sortingContainer.Source.MappingFromItem.ResizedFileHolder, shortcutFile); - return result; - } - - internal static (SaveContainer, SaveContainer?) GetContainers(string facesFileNameExtension, string facePartsFileNameExtension, string directory, FileHolder faceFileHolder, FileHolder facePartsFileHolder, Mapping mapping) - { - string checkFile; - SaveContainer? saveContainer; - if (mapping.MappingFromLocation is null) - throw new NullReferenceException(nameof(mapping.MappingFromLocation)); - checkFile = Path.Combine(directory, $"{mapping.MappingFromItem.FilePath.Name}{facePartsFileNameExtension}"); - saveContainer = !facePartsFileHolder.Exists ? null : new(checkFile, directory, facePartsFileHolder); - checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}"); - return (new(checkFile, directory, faceFileHolder), saveContainer); - } - - internal static void SaveMappingShortcuts(string mappingDirectory) - { - string? shortcutFileName; - string[] yearDirectories; - string personKeyFormatted; - string[] personNameDirectories; - WindowsShortcut windowsShortcut; - string personDisplayDirectoryName; - (string, string)[] yearDirectoryNameCheck; - List<(string, string)> yearDirectoryNames = []; - string[] personKeyDirectories = Directory.GetDirectories(mappingDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string personKeyDirectory in personKeyDirectories) + foreach (KeyValuePair> keyValuePair in personKeyToPersonContainerCollection) { - windowsShortcut = new(); - shortcutFileName = null; - yearDirectoryNames.Clear(); - personKeyFormatted = Path.GetFileName(personKeyDirectory); - yearDirectories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); - yearDirectoryNames.AddRange(GetCollection(yearDirectories)); - yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('^') select l).OrderByDescending(l => l.Item2).ToArray(); - if (yearDirectoryNameCheck.Length == 0) - yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('~') select l).OrderByDescending(l => l.Item2).ToArray(); - if (yearDirectoryNameCheck.Length == 0) - yearDirectoryNameCheck = (from l in yearDirectoryNames where l.Item2.Contains('=') select l).OrderByDescending(l => l.Item2).ToArray(); - if (yearDirectoryNameCheck.Length == 0) - yearDirectoryNameCheck = (from l in yearDirectoryNames select l).OrderByDescending(l => l).ToArray(); - if (yearDirectoryNameCheck.Length == 0) - continue; - foreach ((string yearDirectory, string yearDirectoryName) in yearDirectoryNameCheck) - { - personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly); - foreach (string personNameDirectory in personNameDirectories) - { - personDisplayDirectoryName = Path.GetFileName(personNameDirectory).Split('-')[0]; - if (personDisplayDirectoryName is null) - continue; - windowsShortcut.Path = yearDirectory; - windowsShortcut.Description = yearDirectoryName; - shortcutFileName = Path.Combine(mappingDirectory, $"{personDisplayDirectoryName} [{windowsShortcut.Description}].lnk"); - break; - } - if (shortcutFileName is not null) - { - if (!File.Exists(shortcutFileName)) - break; - } - } - if (shortcutFileName is null || windowsShortcut.Path is null || windowsShortcut.Description is null) - continue; - try - { - windowsShortcut.Save(shortcutFileName); - windowsShortcut.Dispose(); - } - catch (Exception) - { } - } - } - - internal static ReadOnlyDictionary> GetIdToPersonKeys(ReadOnlyDictionary> personKeyToIds) - { - Dictionary> results = []; - List? collection; - foreach (KeyValuePair> keyValuePair in personKeyToIds) - { - foreach (int id in keyValuePair.Value) - { - if (!results.TryGetValue(id, out collection)) - { - results.Add(id, []); - if (!results.TryGetValue(id, out collection)) - throw new Exception(); - } - if (collection.Contains(keyValuePair.Key)) - continue; - collection.Add(keyValuePair.Key); - } - } - return results.AsReadOnly(); - } - - internal static ReadOnlyDictionary> GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) - { - Dictionary> results = []; - Dictionary? keyValuePairs; - foreach (Mapping mapping in distinctValidImageMappingCollection) - { - if (mapping.MappingFromLocation is null) - continue; - if (!results.TryGetValue(mapping.MappingFromItem.Id, out keyValuePairs)) - { - results.Add(mapping.MappingFromItem.Id, []); - if (!results.TryGetValue(mapping.MappingFromItem.Id, out keyValuePairs)) - throw new Exception(); - } - if (keyValuePairs.ContainsKey(mapping.MappingFromLocation.WholePercentages)) - continue; - keyValuePairs.Add(mapping.MappingFromLocation.WholePercentages, mapping); - } - return GetReadOnly(results); - } - - internal static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) - { - List<(string, long)> results; - string[] files; - string? foundPath; - int totalFiles = 0; - string checkDirectory; - WindowsShortcut windowsShortcut; - List resolvedDirectories = []; - if (string.IsNullOrEmpty(genealogicalDataCommunicationFile)) - results = GetDirectoryAndTicksCollection(jLinks, personBirthdayFormat, a2PeopleContentDirectory); - else - results = GetGenealogicalDataCommunicationDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat); - if (results.Count == 0 || results.Count < jLinks.Length) - { - List<(string, string, string)> a2PeopleSingletonDirectories = []; - foreach (string directory in Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.AllDirectories)) - a2PeopleSingletonDirectories.Add((directory, Path.GetFileName(directory), Path.GetFileName(directory).Split(personCharacters).First())); - foreach (string directoryName in jLinks) - { - checkDirectory = Path.Combine(a2PeopleContentDirectory, directoryName); - if (!Directory.Exists(checkDirectory)) - continue; - files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly); - totalFiles += files.Length; - foreach (string file in files) - { - windowsShortcut = WindowsShortcut.Load(file); - if (windowsShortcut.Path is null) - continue; - if (Directory.Exists(windowsShortcut.Path)) - resolvedDirectories.Add(windowsShortcut.Path); - else - { - foundPath = TryToFind(personCharacters, a2PeopleSingletonDirectory, a2PeopleSingletonDirectories, file, windowsShortcut.Path); - if (string.IsNullOrEmpty(foundPath)) - continue; - resolvedDirectories.Add(foundPath); - } - } - } - if (totalFiles == resolvedDirectories.Count) - results = GetJLinkResolvedDirectories(personBirthdayFormat, resolvedDirectories); - else - { - resolvedDirectories.Clear(); - results = []; - } + if (keyValuePair.Value.Count != 1 && (from l in keyValuePair.Value select l.DisplayDirectoryName).Distinct().Count() != 1) + errors.Add(keyValuePair.Value[zero].DisplayDirectoryName); + collection.Add(new(keyValuePair.Key, keyValuePair.Value[zero])); } + if (errors.Count > 0) + throw new Exception(string.Join(Environment.NewLine, errors)); + results = (from l in collection orderby l.PersonKey descending select (l.PersonKey, l.PersonContainer)).ToArray(); return results; } - internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName) - { - string byValue; - bool isByMapping; - bool isBySorting; - if (by is null) - { - isByMapping = false; - isBySorting = !sortingContainersAny; - byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Mapping)}Null"; - } - else - { - isByMapping = by == Shared.Models.Stateless.IMapLogic.Mapping; - isBySorting = by == Shared.Models.Stateless.IMapLogic.Sorting; - bool isDefaultName = displayDirectoryName is not null && IPerson.IsDefaultName(displayDirectoryName); - if (isBySorting && displayDirectoryName is null) - byValue = saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)} Without Person{(distancePermyriad < 2000 ? "-A" : "-Z")}"; - else if (isBySorting && useFiltersCounter.HasValue) - byValue = $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}"; - else - { - byValue = $"{by.Value switch - { - Shared.Models.Stateless.IMapLogic.Mapping => nameof(Shared.Models.Stateless.IMapLogic.Mapping), - Shared.Models.Stateless.IMapLogic.Sorting => saveIndividually ? nameof(Shared.Models.Stateless.IMapLogic.Individually) : nameof(Shared.Models.Stateless.IMapLogic.Sorting), - Shared.Models.Stateless.IMapLogic.ForceSingleImage => forceSingleImageHumanized, - _ => throw new NotImplementedException() - }}{(!isDefaultName ? "-A" : "-Z")}"; - } - } - return new(byValue, isByMapping, isBySorting); - } - - internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, long? personKey, string? displayDirectoryName) => - Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKey is null ? null : Shared.Models.Stateless.IMapLogic.Mapping, displayDirectoryName); - - internal static (string, bool, bool) Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, Mapping mapping) => - Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, mapping.By, mapping.MappingFromPerson?.DisplayDirectoryName); - - internal static void CheckCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string? rootDirectoryParent) - { - string json; - string fullPath; - List>? collection; - foreach (string propertyContentCollectionFile in propertyConfiguration.PropertyContentCollectionFiles) - { - fullPath = Path.GetFullPath(string.Concat(rootDirectoryParent, propertyContentCollectionFile)); - if (fullPath.Contains(propertyConfiguration.RootDirectory)) - continue; - if (!File.Exists(fullPath)) - continue; - json = File.ReadAllText(fullPath); - collection = JsonSerializer.Deserialize>>(json); - if (collection is null) - throw new NullReferenceException(nameof(collection)); - } - } - - internal static ReadOnlyDictionary> ConvertSkip(Dictionary> skipCollection) - { - Dictionary> results = []; - List? wholePercentagesCollection; - foreach (KeyValuePair> keyValuePair in skipCollection) - { - if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection)) - { - results.Add(keyValuePair.Key, []); - if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection)) - throw new Exception(); - } - foreach ((string _, int wholePercentage) in keyValuePair.Value) - wholePercentagesCollection.Add(wholePercentage); - } - return results.AsReadOnly(); - } - - internal static ReadOnlyDictionary> ConvertSkipNotSkip(Dictionary> skipNotSkipCollection) - { - Dictionary> results = []; - List? wholePercentagesCollection; - foreach (KeyValuePair> keyValuePair in skipNotSkipCollection) - { - if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection)) - { - results.Add(keyValuePair.Key, []); - if (!results.TryGetValue(keyValuePair.Key, out wholePercentagesCollection)) - throw new Exception(); - } - foreach ((string _, int wholePercentage) in keyValuePair.Value) - wholePercentagesCollection.Add(wholePercentage); - } - return results.AsReadOnly(); - } - - internal static ReadOnlyDictionary> ConvertLocationContainers(List locationContainers) - { - Dictionary> results = []; - foreach (LocationContainer locationContainer in locationContainers) - { - if (!results.ContainsKey(locationContainer.Id)) - results.Add(locationContainer.Id, []); - results[locationContainer.Id].Add(locationContainer); - } - return results.AsReadOnly(); - } - - internal static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, int wholePercentages) - { - bool? result; - ReadOnlyCollection? personContainers; - if (wholePercentagesToPersonContainers is null) - result = null; - else - { - if (!wholePercentagesToPersonContainers.TryGetValue(wholePercentages, out personContainers)) - result = null; - else - { - result = false; - foreach (PersonContainer personContainer in personContainers) - { - if (!IPerson.IsDefaultName(personContainer) || personContainer.Key is null || !IPersonBirthday.IsCounterPersonYear(new DateTime(personContainer.Key.Value).Year) || jLinkResolvedPersonKeys.Contains(personContainer.Key.Value)) - continue; - result = true; - break; - } - } - } - return result; - } - } \ No newline at end of file diff --git a/Map/Models/Stateless/Methods/IMapLogic.cs b/Map/Models/Stateless/Methods/IMapLogic.cs index 6c8cc2e..cb442c4 100644 --- a/Map/Models/Stateless/Methods/IMapLogic.cs +++ b/Map/Models/Stateless/Methods/IMapLogic.cs @@ -7,69 +7,93 @@ namespace View_by_Distance.Map.Models.Stateless.Methods; public interface IMapLogic { - ReadOnlyDictionary> TestStatic_GetIdToPersonKeys(ReadOnlyDictionary> personKeyToIds) => - GetIdToPersonKeys(personKeyToIds); - static ReadOnlyDictionary> GetIdToPersonKeys(ReadOnlyDictionary> personKeyToIds) => - MapLogic.GetIdToPersonKeys(personKeyToIds); + static string Get(bool saveIndividually, string forceSingleImageHumanized, int by, bool isDefaultName) => + $"{by switch + { + Shared.Models.Stateless.IMapLogic.Mapping => nameof(Shared.Models.Stateless.IMapLogic.Mapping), + Shared.Models.Stateless.IMapLogic.Sorting => saveIndividually ? + nameof(Shared.Models.Stateless.IMapLogic.Individually) : + nameof(Shared.Models.Stateless.IMapLogic.Sorting), + Shared.Models.Stateless.IMapLogic.ForceSingleImage => forceSingleImageHumanized, + _ => throw new NotImplementedException() + }}{(!isDefaultName ? "-A" : "-Z")}"; - ReadOnlyCollection TestStatic_GetFaces(ReadOnlyCollection items) => - GetFaces(items); - static ReadOnlyCollection GetFaces(ReadOnlyCollection items) => - MapLogic.GetFaces(items); - - Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection items) => - GetSelectedMappingCollection(items); - static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection items) => + public static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection items) => MapLogic.GetSelectedMappingCollection(items); - Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection faces) => - GetSelectedMappingCollection(faces); - static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection faces) => + public static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection faces) => MapLogic.GetSelectedMappingCollection(faces); - ReadOnlyDictionary> TestStatic_GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) => - GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); - static ReadOnlyDictionary> GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) => - MapLogic.GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); - - List<(string, long)> TestStatic_GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) => - GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory); - static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) => - MapLogic.GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory); - - void TestStatic_SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => - SetCreationTime(mappingFromItem, locationContainers); - static void SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => - DecadeLogic.SetCreationTime(mappingFromItem, locationContainers); - - void TestStatic_MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => - MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers); - static void MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => - DecadeLogic.MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers); - - bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => - CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); - static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => - MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages); - - bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, int wholePercentages) => - CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages); - static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, int wholePercentages) => + public static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, int wholePercentages) => MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages); - string TestStatic_GetDecade(MappingFromItem mappingFromItem) => - GetDecade(mappingFromItem); - static string GetDecade(MappingFromItem mappingFromItem) => + public static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => + MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages); + + public static string GetDecade(MappingFromItem mappingFromItem) => DecadeLogic.GetDecade(mappingFromItem, null); - ReadOnlyDictionary> TestStatic_GetMappedFiles(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) => - GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); - static ReadOnlyDictionary> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) => - FaceFileLogic.GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); + public static ReadOnlyCollection GetFaces(ReadOnlyCollection items) => + MapLogic.GetFaces(items); - List TestStatic_GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection filePaths) => - GetAvailable(maxDegreeOfParallelism, configuration, dFace, ticks, filePaths); - static List GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection filePaths) => + public static ReadOnlyDictionary> GetIdToPersonKeys(ReadOnlyDictionary> personKeyToIds) => + MapLogic.GetIdToPersonKeys(personKeyToIds); + + public static void SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => + DecadeLogic.SetCreationTime(mappingFromItem, locationContainers); + + public static ReadOnlyDictionary> GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) => + MapLogic.GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); + + public static List GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection filePaths) => FaceFileLogic.GetAvailable(maxDegreeOfParallelism, configuration, dFace, ticks, filePaths); + public static void MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => + DecadeLogic.MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers); + + public static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) => + MapLogic.GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory); + + public static ReadOnlyDictionary> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) => + FaceFileLogic.GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); + + internal string TestStatic_GetDecade(MappingFromItem mappingFromItem) => + GetDecade(mappingFromItem); + + internal ReadOnlyCollection TestStatic_GetFaces(ReadOnlyCollection items) => + GetFaces(items); + + internal Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection items) => + GetSelectedMappingCollection(items); + + internal Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection faces) => + GetSelectedMappingCollection(faces); + + internal ReadOnlyDictionary> TestStatic_GetIdToPersonKeys(ReadOnlyDictionary> personKeyToIds) => + GetIdToPersonKeys(personKeyToIds); + + internal void TestStatic_SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => + SetCreationTime(mappingFromItem, locationContainers); + + internal ReadOnlyDictionary> TestStatic_GetIdToWholePercentagesToFace(ReadOnlyCollection distinctValidImageMappingCollection) => + GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); + + internal List TestStatic_GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection filePaths) => + GetAvailable(maxDegreeOfParallelism, configuration, dFace, ticks, filePaths); + + internal void TestStatic_MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => + MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers); + + internal bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, int wholePercentages) => + CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages); + + internal bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) => + CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); + + internal List<(string, long)> TestStatic_GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) => + GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory); + + internal ReadOnlyDictionary> TestStatic_GetMappedFiles(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) => + GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); + } \ No newline at end of file diff --git a/Map/Models/Stateless/RelationLogic.cs b/Map/Models/Stateless/RelationLogic.cs index 01fe0d2..14c9f3b 100644 --- a/Map/Models/Stateless/RelationLogic.cs +++ b/Map/Models/Stateless/RelationLogic.cs @@ -8,36 +8,48 @@ namespace View_by_Distance.Map.Models.Stateless; internal abstract class RelationLogic { - internal record Group(string Key, long PersonKey, ReadOnlyCollection RelationContainersCollection); + internal record Group(string Key, + long PersonKey, + ReadOnlyCollection RelationContainersCollection); - private static Dictionary>> GetPersonKeyTo(Configuration configuration, List locationContainers) + internal static void SaveMappedRelations(Configuration configuration, Shared.Models.Methods.IDistance distance, string a2PeopleContentDirectory, string eDistanceContentDirectory, long ticks, List locationContainers, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection) { - List? collection; - Dictionary>? yearTo; - Dictionary>> personKeyTo = []; - foreach (LocationContainer locationContainer in locationContainers) + int take; + string directory; + bool isCounterPersonYear; + string personKeyFormatted; + string? displayDirectoryName; + Uri uri = new(eDistanceContentDirectory); + ReadOnlyDictionary movedFiles; + ReadOnlyCollection relationContainers; + ReadOnlyCollection groups = GetGroups(configuration, locationContainers); + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + string message = $") Save Mapped Relations - {totalSeconds} total second(s)"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using ProgressBar progressBar = new(groups.Count, message, options); + foreach (Group group in groups) { - if (locationContainer.PersonKey is null) + if (configuration.LocationContainerDistanceTolerance is null) + break; + progressBar.Tick(); + if (group.RelationContainersCollection.Count == 0) continue; - if (!locationContainer.FromDistanceContent) - continue; - if (!locationContainer.FilePath.FullName.Contains(configuration.LocationContainerDirectoryPattern)) - continue; - if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo)) - { - personKeyTo.Add(locationContainer.PersonKey.Value, []); - if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo)) - throw new Exception(); - } - if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection)) - { - yearTo.Add(locationContainer.CreationDateOnly.Year, []); - if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection)) - throw new Exception(); - } - collection.Add(locationContainer); + take = GetTake(configuration.LocationContainerDistanceTake, group.RelationContainersCollection.Count); + isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(new DateTime(group.PersonKey).Year); + personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, group.PersonKey); + displayDirectoryName = GetDisplayDirectoryName(readOnlyPersonKeyToPersonContainerCollection, readOnlyPersonKeyFormattedToPersonContainer, group.PersonKey, personKeyFormatted); + directory = Path.Combine(a2PeopleContentDirectory, $"{ticks}-{configuration.LocationContainerDistanceTolerance.Value}", personKeyFormatted, group.Key); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + WriteVsCodeFiles(eDistanceContentDirectory, displayDirectoryName, directory); + relationContainers = distance.GetRelationContainers(configuration.DistanceLimits, configuration.FaceDistancePermyriad, configuration.LocationContainerDistanceTake, configuration.LocationContainerDistanceTolerance.Value, group.RelationContainersCollection); + movedFiles = GetMoveFiles(configuration, group.Key, take, isCounterPersonYear, displayDirectoryName, relationContainers); + WriteFile(take, group.PersonKey, isCounterPersonYear, personKeyFormatted, displayDirectoryName, directory, ticks, uri, relationContainers, movedFiles); } - return personKeyTo; + if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) + _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); + else + AddDisplayDirectoryNames(configuration, eDistanceContentDirectory, readOnlyPersonKeyFormattedToPersonContainer, readOnlyPersonKeyToPersonContainerCollection, groups); } private static ReadOnlyCollection GetGroups(Configuration configuration, List locationContainers) @@ -90,6 +102,121 @@ internal abstract class RelationLogic return results.AsReadOnly(); } + private static Dictionary>> GetPersonKeyTo(Configuration configuration, List locationContainers) + { + List? collection; + Dictionary>? yearTo; + Dictionary>> personKeyTo = []; + foreach (LocationContainer locationContainer in locationContainers) + { + if (locationContainer.PersonKey is null) + continue; + if (!locationContainer.FromDistanceContent) + continue; + if (!locationContainer.FilePath.FullName.Contains(configuration.LocationContainerDirectoryPattern)) + continue; + if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo)) + { + personKeyTo.Add(locationContainer.PersonKey.Value, []); + if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo)) + throw new Exception(); + } + if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection)) + { + yearTo.Add(locationContainer.CreationDateOnly.Year, []); + if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection)) + throw new Exception(); + } + collection.Add(locationContainer); + } + return personKeyTo; + } + + private static int GetTake(int locationContainerDistanceTake, int count) + { + int result = locationContainerDistanceTake; + int subtract = (int)(locationContainerDistanceTake * .05); + if (subtract < 1) + subtract = 1; + if (count > 9000) + result -= subtract; + if (count > 8000) + result -= subtract; + if (count > 7000) + result -= subtract; + if (count > 6000) + result -= subtract; + if (count > 5000) + result -= subtract; + if (count > 4000) + result -= subtract; + if (count > 3000) + result -= subtract; + if (count > 2000) + result -= subtract; + if (count > 1000) + result -= subtract; + if (result < 3) + result = 3; + return result; + } + + private static string? GetDisplayDirectoryName(ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, long personKey, string personKeyFormatted) + { + string? result; + PersonContainer? personContainer; + List? collection; + _ = readOnlyPersonKeyToPersonContainerCollection.TryGetValue(personKey, out collection); + if (collection is not null) + result = collection[0].DisplayDirectoryName; + else + { + if (!readOnlyPersonKeyFormattedToPersonContainer.TryGetValue(personKeyFormatted, out personContainer)) + result = null; + else + result = personContainer.DisplayDirectoryName; + } + return result; + } + + private static void WriteVsCodeFiles(string eDistanceContentDirectory, string? displayDirectoryName, string directory) + { + string json; + string vsCodeDirectory = Path.Combine(directory, ".vscode"); + if (!Directory.Exists(vsCodeDirectory)) + _ = Directory.CreateDirectory(vsCodeDirectory); + if (displayDirectoryName is not null) + File.WriteAllText(Path.Combine(directory, $"_ {displayDirectoryName}.txt"), string.Empty); + json = /*lang=json*/ """{ "[markdown]": { "editor.wordWrap": "off" }, "foam.links.hover.enable": false, "foam.graph.style": { "background": "#202020", "node": { "note": "#f2cb1d", "distance": "green", "image": "orange", "placeholder": "white", } } }"""; + _ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "settings.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + json = string.Concat("{ \"version\": \"2.0.0\", \"tasks\": [ { \"label\": \"MKLink\", \"type\": \"shell\", \"command\": \"New-Item\", \"args\": [ \"-ItemType\", \"Junction\", \"-Path\", \"'", directory.Replace('\\', '/'), "/()'\", \"-Target\", \"'", eDistanceContentDirectory.Replace('\\', '/'), "'\" ], \"problemMatcher\": [] } ] }"); + _ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "tasks.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + } + + private static ReadOnlyDictionary GetMoveFiles(Configuration configuration, string key, int take, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection relationContainers) + { + ReadOnlyDictionary results; + List> linked = []; + for (int i = 0; i < 25; i++) + linked.Add([]); + foreach ((FileHolder fileHolder, ReadOnlyCollection relations) in relationContainers) + { + foreach (Relation relation in relations.Take(take)) + { + for (int i = 0; i < 25; i++) + { + if (!linked[i].Contains(relation.File)) + { + linked[i].Add(relation.File); + break; + } + } + } + } + results = MoveFiles(configuration, key, isCounterPersonYear, displayDirectoryName, relationContainers, linked); + return results; + } + private static ReadOnlyDictionary MoveFiles(Configuration configuration, string key, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection relationContainers, List> linked) { Dictionary results = []; @@ -205,91 +332,6 @@ internal abstract class RelationLogic return results.AsReadOnly(); } - private static string? GetDisplayDirectoryName(ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, long personKey, string personKeyFormatted) - { - string? result; - PersonContainer? personContainer; - List? collection; - _ = readOnlyPersonKeyToPersonContainerCollection.TryGetValue(personKey, out collection); - if (collection is not null) - result = collection[0].DisplayDirectoryName; - else - { - if (!readOnlyPersonKeyFormattedToPersonContainer.TryGetValue(personKeyFormatted, out personContainer)) - result = null; - else - result = personContainer.DisplayDirectoryName; - } - return result; - } - - private static int GetTake(int locationContainerDistanceTake, int count) - { - int result = locationContainerDistanceTake; - int subtract = (int)(locationContainerDistanceTake * .05); - if (subtract < 1) - subtract = 1; - if (count > 9000) - result -= subtract; - if (count > 8000) - result -= subtract; - if (count > 7000) - result -= subtract; - if (count > 6000) - result -= subtract; - if (count > 5000) - result -= subtract; - if (count > 4000) - result -= subtract; - if (count > 3000) - result -= subtract; - if (count > 2000) - result -= subtract; - if (count > 1000) - result -= subtract; - if (result < 3) - result = 3; - return result; - } - - private static void WriteVsCodeFiles(string eDistanceContentDirectory, string? displayDirectoryName, string directory) - { - string json; - string vsCodeDirectory = Path.Combine(directory, ".vscode"); - if (!Directory.Exists(vsCodeDirectory)) - _ = Directory.CreateDirectory(vsCodeDirectory); - if (displayDirectoryName is not null) - File.WriteAllText(Path.Combine(directory, $"_ {displayDirectoryName}.txt"), string.Empty); - json = /*lang=json*/ """{ "[markdown]": { "editor.wordWrap": "off" }, "foam.links.hover.enable": false, "foam.graph.style": { "background": "#202020", "node": { "note": "#f2cb1d", "distance": "green", "image": "orange", "placeholder": "white", } } }"""; - _ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "settings.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - json = string.Concat("{ \"version\": \"2.0.0\", \"tasks\": [ { \"label\": \"MKLink\", \"type\": \"shell\", \"command\": \"New-Item\", \"args\": [ \"-ItemType\", \"Junction\", \"-Path\", \"'", directory.Replace('\\', '/'), "/()'\", \"-Target\", \"'", eDistanceContentDirectory.Replace('\\', '/'), "'\" ], \"problemMatcher\": [] } ] }"); - _ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "tasks.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - } - - private static ReadOnlyDictionary GetMoveFiles(Configuration configuration, string key, int take, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection relationContainers) - { - ReadOnlyDictionary results; - List> linked = []; - for (int i = 0; i < 25; i++) - linked.Add([]); - foreach ((FileHolder fileHolder, ReadOnlyCollection relations) in relationContainers) - { - foreach (Relation relation in relations.Take(take)) - { - for (int i = 0; i < 25; i++) - { - if (!linked[i].Contains(relation.File)) - { - linked[i].Add(relation.File); - break; - } - } - } - } - results = MoveFiles(configuration, key, isCounterPersonYear, displayDirectoryName, relationContainers, linked); - return results; - } - private static void WriteFile(int take, long personKey, bool isCounterPersonYear, string personKeyFormatted, string? displayDirectoryName, string directory, long ticks, Uri uri, ReadOnlyCollection relationContainers, ReadOnlyDictionary movedFiles) { string a; @@ -398,44 +440,4 @@ internal abstract class RelationLogic } } - internal static void SaveMappedRelations(Configuration configuration, Shared.Models.Methods.IDistance distance, string a2PeopleContentDirectory, string eDistanceContentDirectory, long ticks, List locationContainers, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection) - { - int take; - string directory; - bool isCounterPersonYear; - string personKeyFormatted; - string? displayDirectoryName; - Uri uri = new(eDistanceContentDirectory); - ReadOnlyDictionary movedFiles; - ReadOnlyCollection relationContainers; - ReadOnlyCollection groups = GetGroups(configuration, locationContainers); - int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - string message = $") Save Mapped Relations - {totalSeconds} total second(s)"; - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(groups.Count, message, options); - foreach (Group group in groups) - { - if (configuration.LocationContainerDistanceTolerance is null) - break; - progressBar.Tick(); - if (group.RelationContainersCollection.Count == 0) - continue; - take = GetTake(configuration.LocationContainerDistanceTake, group.RelationContainersCollection.Count); - isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(new DateTime(group.PersonKey).Year); - personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, group.PersonKey); - displayDirectoryName = GetDisplayDirectoryName(readOnlyPersonKeyToPersonContainerCollection, readOnlyPersonKeyFormattedToPersonContainer, group.PersonKey, personKeyFormatted); - directory = Path.Combine(a2PeopleContentDirectory, $"{ticks}-{configuration.LocationContainerDistanceTolerance.Value}", personKeyFormatted, group.Key); - if (!Directory.Exists(directory)) - _ = Directory.CreateDirectory(directory); - WriteVsCodeFiles(eDistanceContentDirectory, displayDirectoryName, directory); - relationContainers = distance.GetRelationContainers(configuration.DistanceLimits, configuration.FaceDistancePermyriad, configuration.LocationContainerDistanceTake, configuration.LocationContainerDistanceTolerance.Value, group.RelationContainersCollection); - movedFiles = GetMoveFiles(configuration, group.Key, take, isCounterPersonYear, displayDirectoryName, relationContainers); - WriteFile(take, group.PersonKey, isCounterPersonYear, personKeyFormatted, displayDirectoryName, directory, ticks, uri, relationContainers, movedFiles); - } - if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) - _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); - else - AddDisplayDirectoryNames(configuration, eDistanceContentDirectory, readOnlyPersonKeyFormattedToPersonContainer, readOnlyPersonKeyToPersonContainerCollection, groups); - } - } \ No newline at end of file diff --git a/Property/Models/A_Property.cs b/Property/Models/A_Property.cs index 62e3057..64fb431 100644 --- a/Property/Models/A_Property.cs +++ b/Property/Models/A_Property.cs @@ -51,22 +51,110 @@ public class A_Property return result; } - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo) + public void SetAngleBracketCollection(string aResultsFullGroupDirectory, string sourceDirectory, bool anyNullOrNoIsUniqueFileName = true) { - string[] segments = directory.Split(cei.Combined); - string? checkDirectory = segments.Length == 1 ? - Path.Combine(segments[0], $"{cei.Combined[2..]}") : - segments.Length == 2 ? - $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : - null; - if (checkDirectory is not null && Directory.Exists(checkDirectory)) + _AngleBracketCollection.Clear(); + if (!anyNullOrNoIsUniqueFileName) + _AngleBracketCollection.AddRange([Path.Combine(aResultsFullGroupDirectory, "<>")]); + else + _AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(_PropertyConfiguration, + sourceDirectory, + aResultsFullGroupDirectory, + contentDescription: string.Empty, + singletonDescription: "Properties for each image", + collectionDescription: string.Empty, + converted: false)); + } + + public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata metadata, int t, Container.Models.Container[] containers) + { + int total = 0; + string message; + int totalSeconds; + bool anyNullOrNoIsUniqueFileName; + List exceptions = []; + Container.Models.Container container; + int containersLength = containers.Length; + const string outputResolution = "Original"; + List> sourceDirectoryChanges = []; + string propertyRoot = IResult.GetResultsGroupDirectory(_PropertyConfiguration, nameof(A_Property)); + for (int i = 0; i < containers.Length; i++) { - string checkFile = Path.Combine(checkDirectory, fileName); - if (File.Exists(checkFile)) - { - File.Move(checkFile, fileInfo.FullName); - fileInfo.Refresh(); - } + container = containers[i]; + if (container.Items.Count == 0) + continue; + sourceDirectoryChanges.Clear(); + if (container.Items.Count == 0) + continue; + anyNullOrNoIsUniqueFileName = container.Items.Any(l => !l.IsUniqueFileName); + SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName); + totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; + SavePropertyParallelWork(_MaxDegreeOfParallelism, metadata, exceptions, sourceDirectoryChanges, container, container.Items, message); + if (exceptions.Count == container.Items.Count) + throw new Exception(string.Concat("All in [", container.SourceDirectory, "]failed!")); + if (exceptions.Count != 0) + _ExceptionsDirectories.Add(container.SourceDirectory); + total += container.Items.Count; + } + } + + private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName) + { + _AngleBracketCollection.Clear(); + string aResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(_PropertyConfiguration, + nameof(A_Property), + string.Empty, + includeResizeGroup: false, + includeModel: false, + includePredictorModel: false); + SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); + } + + private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata metadata, List exceptions, List> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection items, string message) + { + List> sourceDirectoryFileTuples = []; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using ProgressBar progressBar = new(items.Count, message, options); + _ = Parallel.For(0, items.Count, parallelOptions, (i, state) => + { + try + { + long ticks = DateTime.Now.Ticks; + DateTime dateTime = DateTime.Now; + List> collection; + SavePropertyParallelForWork(metadata, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]); + if (i == 0 || sourceDirectoryChanges.Count != 0) + progressBar.Tick(); + lock (sourceDirectoryFileTuples) + collection = (from l in sourceDirectoryFileTuples where l.Item2 > dateTime select l).ToList(); + lock (sourceDirectoryChanges) + sourceDirectoryChanges.AddRange(collection); + } + catch (Exception ex) + { + lock (exceptions) + exceptions.Add(ex); + } + }); + } + + private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata metadata, string sourceDirectory, List> sourceDirectoryFileTuples, List> sourceDirectoryChanges, Item item) + { + Shared.Models.Property property; + List parseExceptions = []; + bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered); + string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}"); + if (item.IsValidImageFormatExtension && item.FilePath.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.FilePath.FullName != filteredSourceDirectoryFileExtensionLowered) + File.Move(item.FilePath.FullName, filteredSourceDirectoryFileExtensionLowered); + if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.Property is null) + { + property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension); + lock (sourceDirectoryChanges) + sourceDirectoryChanges.Add(new Tuple(nameof(A_Property), DateTime.Now)); + lock (item) + item.Update(property); } } @@ -194,110 +282,22 @@ public class A_Property return result; } - public void SetAngleBracketCollection(string aResultsFullGroupDirectory, string sourceDirectory, bool anyNullOrNoIsUniqueFileName = true) + private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo) { - _AngleBracketCollection.Clear(); - if (!anyNullOrNoIsUniqueFileName) - _AngleBracketCollection.AddRange([Path.Combine(aResultsFullGroupDirectory, "<>")]); - else - _AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(_PropertyConfiguration, - sourceDirectory, - aResultsFullGroupDirectory, - contentDescription: string.Empty, - singletonDescription: "Properties for each image", - collectionDescription: string.Empty, - converted: false)); - } - - private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata metadata, string sourceDirectory, List> sourceDirectoryFileTuples, List> sourceDirectoryChanges, Item item) - { - Shared.Models.Property property; - List parseExceptions = []; - bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered); - string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}"); - if (item.IsValidImageFormatExtension && item.FilePath.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.FilePath.FullName != filteredSourceDirectoryFileExtensionLowered) - File.Move(item.FilePath.FullName, filteredSourceDirectoryFileExtensionLowered); - if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.Property is null) + string[] segments = directory.Split(cei.Combined); + string? checkDirectory = segments.Length == 1 ? + Path.Combine(segments[0], $"{cei.Combined[2..]}") : + segments.Length == 2 ? + $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : + null; + if (checkDirectory is not null && Directory.Exists(checkDirectory)) { - property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension); - lock (sourceDirectoryChanges) - sourceDirectoryChanges.Add(new Tuple(nameof(A_Property), DateTime.Now)); - lock (item) - item.Update(property); - } - } - - private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName) - { - _AngleBracketCollection.Clear(); - string aResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(_PropertyConfiguration, - nameof(A_Property), - string.Empty, - includeResizeGroup: false, - includeModel: false, - includePredictorModel: false); - SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); - } - - private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata metadata, List exceptions, List> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection items, string message) - { - List> sourceDirectoryFileTuples = []; - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(items.Count, message, options); - _ = Parallel.For(0, items.Count, parallelOptions, (i, state) => - { - try - { - long ticks = DateTime.Now.Ticks; - DateTime dateTime = DateTime.Now; - List> collection; - SavePropertyParallelForWork(metadata, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]); - if (i == 0 || sourceDirectoryChanges.Count != 0) - progressBar.Tick(); - lock (sourceDirectoryFileTuples) - collection = (from l in sourceDirectoryFileTuples where l.Item2 > dateTime select l).ToList(); - lock (sourceDirectoryChanges) - sourceDirectoryChanges.AddRange(collection); - } - catch (Exception ex) - { - lock (exceptions) - exceptions.Add(ex); - } - }); - } - - public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata metadata, int t, Container.Models.Container[] containers) - { - int total = 0; - string message; - int totalSeconds; - bool anyNullOrNoIsUniqueFileName; - List exceptions = []; - Container.Models.Container container; - int containersLength = containers.Length; - const string outputResolution = "Original"; - List> sourceDirectoryChanges = []; - string propertyRoot = IResult.GetResultsGroupDirectory(_PropertyConfiguration, nameof(A_Property)); - for (int i = 0; i < containers.Length; i++) - { - container = containers[i]; - if (container.Items.Count == 0) - continue; - sourceDirectoryChanges.Clear(); - if (container.Items.Count == 0) - continue; - anyNullOrNoIsUniqueFileName = container.Items.Any(l => !l.IsUniqueFileName); - SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName); - totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; - SavePropertyParallelWork(_MaxDegreeOfParallelism, metadata, exceptions, sourceDirectoryChanges, container, container.Items, message); - if (exceptions.Count == container.Items.Count) - throw new Exception(string.Concat("All in [", container.SourceDirectory, "]failed!")); - if (exceptions.Count != 0) - _ExceptionsDirectories.Add(container.SourceDirectory); - total += container.Items.Count; + string checkFile = Path.Combine(checkDirectory, fileName); + if (File.Exists(checkFile)) + { + File.Move(checkFile, fileInfo.FullName); + fileInfo.Refresh(); + } } } diff --git a/Property/Models/Stateless/IProperty.cs b/Property/Models/Stateless/IProperty.cs index 5d0ecff..a2d9f78 100644 --- a/Property/Models/Stateless/IProperty.cs +++ b/Property/Models/Stateless/IProperty.cs @@ -10,69 +10,80 @@ namespace View_by_Distance.Property.Models.Stateless; public interface IProperty { - string TestStatic_DateTimeFormat() => - DateTimeFormat(); - static string DateTimeFormat() => - "yyyy:MM:dd HH:mm:ss"; + const string DateTimeFormat = "yyyy:MM:dd HH:mm:ss"; - byte[] TestStatic_GetBytes(string value) => - GetBytes(value); - static byte[] GetBytes(string value) => - Property.GetBytes(value); - - DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) => - GetDateTime(dateTimeFormat, value); - static DateTime? GetDateTime(string dateTimeFormat, string? value) => - Property.GetDateTime(dateTimeFormat, value); - - PropertyItem TestStatic_GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) => - GetPropertyItem(constructorInfo, id, type, value); - static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) => - Property.GetPropertyItem(constructorInfo, id, type, value); - - (string?, DateTime[], Shared.Models.Property) TestStatic_GetProperty(bool populateId, IMetadata? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) => - GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding); - static (string?, DateTime[], Shared.Models.Property) GetProperty(bool populateId, IMetadata? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) => - Property.GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding); - - (DateTime?, DateTime[], int?, string?) TestStatic_Get(IPropertyConfiguration propertyConfiguration, bool populateId, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => - Get(propertyConfiguration, populateId, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); - static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => - Property.Get(populateId, null, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); - - (DateTime?, DateTime[], int?, string?) TestStatic_Get(bool populateId, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => - Get(populateId, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); - static (DateTime?, DateTime[], int?, string?) Get(bool populateId, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => - Property.Get(populateId, null, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); - - (DateTime?, DateTime[], int?, string?) TestStatic_Get(bool populateId, IMetadata? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => - Get(populateId, metadata, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + // public static (DateTime?, DateTime[], int?, string?) Get(bool populateId, IMetadata? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => Property.Get(populateId, metadata, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); - (DateTime?, DateTime[], int?, string?) TestStatic_Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => - Get(propertyConfiguration, populateId, metadata, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); - static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => - Property.Get(populateId, metadata, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + // public + static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => + Property.Get(populateId, null, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); - TimeSpan TestStatic_GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) => - GetThreeStandardDeviationHigh(minimum, container); - static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) => - Property.GetThreeStandardDeviationHigh(minimum, container); + public static byte[] GetBytes(string value) => + Property.GetBytes(value); - (int, List, List) TestStatic_Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) => - Get(container, threeStandardDeviationHigh, i); - static (int, List, List) Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) => - Property.Get(container, threeStandardDeviationHigh, i); + public static DateTime? GetDateTime(string dateTimeFormat, string? value) => + Property.GetDateTime(dateTimeFormat, value); - bool TestStatic_Any(Container.Models.Container[] propertyHolderCollections) => - Any(propertyHolderCollections); - static bool Any(Container.Models.Container[] propertyHolderCollections) => - Property.Any(propertyHolderCollections); - - double TestStatic_GetStandardDeviation(List values, double average) => - GetStandardDeviation(values, average); - static double GetStandardDeviation(List values, double average) => + public static double GetStandardDeviation(List values, double average) => Property.GetStandardDeviation(values, average); + public static bool Any(Container.Models.Container[] propertyHolderCollections) => + Property.Any(propertyHolderCollections); + + public static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) => + Property.GetThreeStandardDeviationHigh(minimum, container); + + public static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) => + Property.GetPropertyItem(constructorInfo, id, type, value); + + public static (int, List, List) Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) => + Property.Get(container, threeStandardDeviationHigh, i); + + public static (DateTime?, DateTime[], int?, string?) Get(bool populateId, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => + Property.Get(populateId, null, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + + public static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => + Property.Get(populateId, metadata, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + + public static (string?, DateTime[], Shared.Models.Property) GetProperty(bool populateId, IMetadata? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) => + Property.GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding); + + internal byte[] TestStatic_GetBytes(string value) => + GetBytes(value); + + internal DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) => + GetDateTime(dateTimeFormat, value); + + internal double TestStatic_GetStandardDeviation(List values, double average) => + GetStandardDeviation(values, average); + + internal bool TestStatic_Any(Container.Models.Container[] propertyHolderCollections) => + Any(propertyHolderCollections); + + internal TimeSpan TestStatic_GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) => + GetThreeStandardDeviationHigh(minimum, container); + + internal PropertyItem TestStatic_GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) => + GetPropertyItem(constructorInfo, id, type, value); + + internal (int, List, List) TestStatic_Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) => + Get(container, threeStandardDeviationHigh, i); + + internal (DateTime?, DateTime[], int?, string?) TestStatic_Get(bool populateId, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => + Get(populateId, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + + internal (DateTime?, DateTime[], int?, string?) TestStatic_Get(IPropertyConfiguration propertyConfiguration, bool populateId, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => + Get(propertyConfiguration, populateId, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + + internal (DateTime?, DateTime[], int?, string?) TestStatic_Get(bool populateId, IMetadata? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => + Get(populateId, metadata, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + + internal (DateTime?, DateTime[], int?, string?) TestStatic_Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => + Get(propertyConfiguration, populateId, metadata, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); + + internal (string?, DateTime[], Shared.Models.Property) TestStatic_GetProperty(bool populateId, IMetadata? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) => + GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding); + } \ No newline at end of file diff --git a/Property/Models/Stateless/IResult.cs b/Property/Models/Stateless/IResult.cs index 23fb238..a4a911b 100644 --- a/Property/Models/Stateless/IResult.cs +++ b/Property/Models/Stateless/IResult.cs @@ -3,44 +3,52 @@ namespace View_by_Distance.Property.Models.Stateless; public interface IResult { - string TestStatic_GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) => - GetRelativePath(propertyConfiguration, path); - static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) => + public static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) => Result.GetRelativePath(propertyConfiguration, path); - string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) => - GetResultsGroupDirectory(propertyConfiguration, description, create); - static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) => - Result.GetResultsGroupDirectory(propertyConfiguration, description, create); - - string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => - GetResultsGroupDirectory(propertyConfiguration, description); - static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => + public static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => Result.GetResultsGroupDirectory(propertyConfiguration, description, create: true); - string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => - GetResultsDateGroupDirectory(propertyConfiguration, description); - static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => + public static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => Result.GetResultsDateGroupDirectory(propertyConfiguration, description); - string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) => - GetResultsDateGroupDirectory(propertyConfiguration, description, jsonGroup); - static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) => + public static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) => + Result.GetResultsGroupDirectory(propertyConfiguration, description, create); + + public static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) => Result.GetResultsDateGroupDirectory(propertyConfiguration, description, jsonGroup); - List TestStatic_GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) => - GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted); - static List GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) => - Result.GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted); - - string TestStatic_GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) => - GetResultsFullGroupDirectory(propertyConfiguration, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel); - static string GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) => + public static string GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) => Result.GetResultsFullGroupDirectory(propertyConfiguration, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel); - List TestStatic_GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) => - GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel, contentDescription, singletonDescription, collectionDescription); - static List GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) => + public static List GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) => + Result.GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted); + + public static List GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) => Result.GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel, contentDescription, singletonDescription, collectionDescription); + internal string TestStatic_GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) => + GetRelativePath(propertyConfiguration, path); + + internal string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => + GetResultsGroupDirectory(propertyConfiguration, description); + + internal string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => + GetResultsDateGroupDirectory(propertyConfiguration, description); + + internal string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) => + GetResultsGroupDirectory(propertyConfiguration, description, create); + + internal string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) => + GetResultsDateGroupDirectory(propertyConfiguration, description, jsonGroup); + + internal string TestStatic_GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) => + GetResultsFullGroupDirectory(propertyConfiguration, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel); + + internal List TestStatic_GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) => + GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted); + + internal List TestStatic_GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) => + GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel, contentDescription, singletonDescription, collectionDescription); + } \ No newline at end of file diff --git a/Property/Models/Stateless/Property.cs b/Property/Models/Stateless/Property.cs index ee90854..c7ca681 100644 --- a/Property/Models/Stateless/Property.cs +++ b/Property/Models/Stateless/Property.cs @@ -14,56 +14,53 @@ namespace View_by_Distance.Property.Models.Stateless; internal partial class Property { - [GeneratedRegex(@"\D+")] - private static partial Regex Digit(); - - private static List GetDateTimes(DateTime dateTimeFromName, DateTime?[] dateTimes) + internal static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) { - List results = [dateTimeFromName]; - foreach (DateTime? dateTime in dateTimes) + TimeSpan result; + DateTime? minimumDateTime; + List ticksCollection = []; + foreach (Item item in container.Items) { - if (dateTime is null) + if (item.Property is null) continue; - results.Add(dateTime.Value); + minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); + if (minimumDateTime is null) + continue; + ticksCollection.Add(minimumDateTime.Value.Ticks); } - return results; + long threeStandardDeviationHigh; + long min; + if (ticksCollection.Count == 0) + min = 0; + else + min = ticksCollection.Min(); + if (ticksCollection.Count < minimum) + threeStandardDeviationHigh = long.MaxValue; + else + threeStandardDeviationHigh = GetThreeStandardDeviationHigh(ref ticksCollection, min); + result = new TimeSpan(threeStandardDeviationHigh - min); + return result; } - private static List GetDateTimes(DateTime?[] dateTimes, DateTime?[] metadataDateTimes) + private static long GetThreeStandardDeviationHigh(ref List ticksCollection, long min) { - List results = []; - foreach (DateTime? dateTime in metadataDateTimes) - { - if (dateTime is null || results.Contains(dateTime.Value)) - continue; - results.Add(dateTime.Value); - } - foreach (DateTime? dateTime in dateTimes) - { - if (dateTime is null || results.Contains(dateTime.Value)) - continue; - results.Add(dateTime.Value); - } - return results; + long result; + ticksCollection = (from l in ticksCollection select l - min).ToList(); + double sum = ticksCollection.Sum(); + double average = sum / ticksCollection.Count; + double standardDeviation = GetStandardDeviation(ticksCollection, average); + result = (long)Math.Ceiling(average + min + (standardDeviation * 3)); + return result; } - private static List GetDateTimes(FilePath filePath, DateTime?[] dateTimes) + internal static double GetStandardDeviation(List values, double average) { - List results = []; - string[] digits = Digit().Split(filePath.FullName); - foreach (string digit in digits) - { - if (digit.Length != 4 || digit[..2] is not "19" and not "20" || !int.TryParse(digit, out int year)) - continue; - results.Add(new(year, 1, 1)); - } - foreach (DateTime? dateTime in dateTimes) - { - if (dateTime is null) - continue; - results.Add(dateTime.Value); - } - return results; + double result = 0; + if (values.Count == 0) + throw new Exception("Collection must have at least one value!"); + double sum = values.Sum(l => (l - average) * (l - average)); + result = Math.Sqrt(sum / values.Count); + return result; } internal static byte[] GetBytes(string value) @@ -126,22 +123,40 @@ internal partial class Property return result; } - private static List GetDateTimes(DateTime?[] metadataDateTimes) + internal static bool Any(Container.Models.Container[] containers) { - List results = []; - foreach (DateTime? dateTime in metadataDateTimes) + bool result = false; + foreach (Container.Models.Container container in containers) { - if (dateTime is null || results.Contains(dateTime.Value)) + if (container.Items.Count == 0) continue; - results.Add(dateTime.Value); + if ((from l in container.Items where l.Any() select true).Any()) + { + result = true; + break; + } } - return results; + return result; } -#pragma warning disable 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; + } internal static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) { +#pragma warning disable CA1416 PropertyItem result = (PropertyItem)constructorInfo.Invoke(null); int length; byte[] bytes; @@ -161,27 +176,65 @@ internal partial class Property result.Len = length; result.Type = type; result.Value = bytes; - return result; - } - #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; } -#pragma warning disable CA1416 + internal static (int, List, List) Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) + { + List results = []; + int j = i; + long? ticks; + TimeSpan timeSpan; + Item item; + DateTime? minimumDateTime; + Item nextItem; + DateTime? nextMinimumDateTime; + List dateTimes = []; + for (; j < container.Items.Count; j++) + { + ticks = null; + item = container.Items[j]; + if (item.Property is null) + continue; + minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); + if (minimumDateTime is null) + continue; + for (int k = j + 1; k < container.Items.Count; k++) + { + nextItem = container.Items[k]; + if (nextItem.Property is null) + continue; + nextMinimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(nextItem.Property); + if (nextMinimumDateTime is null) + continue; + ticks = nextMinimumDateTime.Value.Ticks; + break; + } + results.Add(item); + dateTimes.Add(minimumDateTime.Value); + if (ticks.HasValue) + { + timeSpan = new(ticks.Value - minimumDateTime.Value.Ticks); + if (timeSpan > threeStandardDeviationHigh) + break; + } + } + return new(j, dateTimes, results); + } + + internal static (DateTime?, DateTime[], int?, string?) Get(bool populateId, IMetadata? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) + { + int? id = null; + string? message; + DateTime[] dateTimes; + Shared.Models.Property? property = null; + if (isIgnoreExtension || !isValidImageFormatExtension) + (message, dateTimes, property) = (null, [], null); + else + (message, dateTimes, property) = GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding); + return new(property?.DateTimeOriginal, dateTimes, property?.Id, message); + } internal static (string?, DateTime[], Shared.Models.Property) GetProperty(bool populateId, IMetadata? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) { @@ -225,6 +278,7 @@ internal partial class Property { try { +#pragma warning disable CA1416 using Image image = Image.FromFile(filePath.FullName); width = image.Width; height = image.Height; @@ -240,7 +294,7 @@ internal partial class Property bitmap.UnlockBits(bitmapData); id ??= Shared.Models.Stateless.Methods.IId.GetDeterministicHashCode(bytes); } - dateTimeFormat = IProperty.DateTimeFormat(); + dateTimeFormat = IProperty.DateTimeFormat; if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime)) { propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime); @@ -318,6 +372,7 @@ internal partial class Property keywords = Encoding.Unicode.GetString(propertyItem.Value).Trim('\0', ' ').Split(';'); } } +#pragma warning restore CA1416 message = null; dateTimes = [new(filePath.LastWriteTicks), new(filePath.CreationTicks), dateTime, dateTimeDigitized, dateTimeOriginal, gpsDateStamp]; } @@ -351,127 +406,68 @@ internal partial class Property return (message, dateTimesByLogic.ToArray(), result); } -#pragma warning restore CA1416 - - internal static (DateTime?, DateTime[], int?, string?) Get(bool populateId, IMetadata? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) + private static List GetDateTimes(DateTime?[] metadataDateTimes) { - int? id = null; - string? message; - DateTime[] dateTimes; - Shared.Models.Property? property = null; - if (isIgnoreExtension || !isValidImageFormatExtension) - (message, dateTimes, property) = (null, [], null); - else - (message, dateTimes, property) = GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding); - return new(property?.DateTimeOriginal, dateTimes, property?.Id, message); - } - - internal static double GetStandardDeviation(List values, double average) - { - double result = 0; - if (values.Count == 0) - throw new Exception("Collection must have at least one value!"); - double sum = values.Sum(l => (l - average) * (l - average)); - result = Math.Sqrt(sum / values.Count); - return result; - } - - private static long GetThreeStandardDeviationHigh(ref List ticksCollection, long min) - { - long result; - ticksCollection = (from l in ticksCollection select l - min).ToList(); - double sum = ticksCollection.Sum(); - double average = sum / ticksCollection.Count; - double standardDeviation = GetStandardDeviation(ticksCollection, average); - result = (long)Math.Ceiling(average + min + (standardDeviation * 3)); - return result; - } - - internal static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) - { - TimeSpan result; - DateTime? minimumDateTime; - List ticksCollection = []; - foreach (Shared.Models.Item item in container.Items) + List results = []; + foreach (DateTime? dateTime in metadataDateTimes) { - if (item.Property is null) + if (dateTime is null || results.Contains(dateTime.Value)) continue; - minimumDateTime = Shared.Models.Methods.IProperty.GetMinimumDateTime(item.Property); - if (minimumDateTime is null) - continue; - ticksCollection.Add(minimumDateTime.Value.Ticks); + results.Add(dateTime.Value); } - long threeStandardDeviationHigh; - long min; - if (ticksCollection.Count == 0) - min = 0; - else - min = ticksCollection.Min(); - if (ticksCollection.Count < minimum) - threeStandardDeviationHigh = long.MaxValue; - else - threeStandardDeviationHigh = GetThreeStandardDeviationHigh(ref ticksCollection, min); - result = new TimeSpan(threeStandardDeviationHigh - min); - return result; + return results; } - internal static (int, List, List) Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) + private static List GetDateTimes(FilePath filePath, DateTime?[] dateTimes) { - List results = []; - int j = i; - long? ticks; - TimeSpan timeSpan; - Shared.Models.Item item; - DateTime? minimumDateTime; - Shared.Models.Item nextItem; - DateTime? nextMinimumDateTime; - List dateTimes = []; - for (; j < container.Items.Count; j++) + List results = []; + string[] digits = Digit().Split(filePath.FullName); + foreach (string digit in digits) { - ticks = null; - item = container.Items[j]; - if (item.Property is null) + if (digit.Length != 4 || digit[..2] is not "19" and not "20" || !int.TryParse(digit, out int year)) continue; - minimumDateTime = Shared.Models.Methods.IProperty.GetMinimumDateTime(item.Property); - if (minimumDateTime is null) - continue; - for (int k = j + 1; k < container.Items.Count; k++) - { - nextItem = container.Items[k]; - if (nextItem.Property is null) - continue; - nextMinimumDateTime = Shared.Models.Methods.IProperty.GetMinimumDateTime(nextItem.Property); - if (nextMinimumDateTime is null) - continue; - ticks = nextMinimumDateTime.Value.Ticks; - break; - } - results.Add(item); - dateTimes.Add(minimumDateTime.Value); - if (ticks.HasValue) - { - timeSpan = new(ticks.Value - minimumDateTime.Value.Ticks); - if (timeSpan > threeStandardDeviationHigh) - break; - } + results.Add(new(year, 1, 1)); } - return new(j, dateTimes, results); + foreach (DateTime? dateTime in dateTimes) + { + if (dateTime is null) + continue; + results.Add(dateTime.Value); + } + return results; } - internal static bool Any(Container.Models.Container[] containers) + private static List GetDateTimes(DateTime dateTimeFromName, DateTime?[] dateTimes) { - bool result = false; - foreach (Container.Models.Container container in containers) + List results = [dateTimeFromName]; + foreach (DateTime? dateTime in dateTimes) { - if (container.Items.Count == 0) + if (dateTime is null) continue; - if ((from l in container.Items where l.Any() select true).Any()) - { - result = true; - break; - } + results.Add(dateTime.Value); } - return result; + return results; } + private static List GetDateTimes(DateTime?[] dateTimes, DateTime?[] metadataDateTimes) + { + List results = []; + foreach (DateTime? dateTime in metadataDateTimes) + { + if (dateTime is null || results.Contains(dateTime.Value)) + continue; + results.Add(dateTime.Value); + } + foreach (DateTime? dateTime in dateTimes) + { + if (dateTime is null || results.Contains(dateTime.Value)) + continue; + results.Add(dateTime.Value); + } + return results; + } + + [GeneratedRegex(@"\D+")] + private static partial Regex Digit(); + } \ No newline at end of file diff --git a/Property/Models/Stateless/Result.cs b/Property/Models/Stateless/Result.cs index 48c57c1..b45eab9 100644 --- a/Property/Models/Stateless/Result.cs +++ b/Property/Models/Stateless/Result.cs @@ -5,13 +5,8 @@ namespace View_by_Distance.Property.Models.Stateless; internal class Result { - internal static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) - { - string result = Path.Combine(GetResultsDateGroupDirectory(propertyConfiguration, description), jsonGroup); - if (!Directory.Exists(result)) - _ = Directory.CreateDirectory(result); - return result; - } + internal static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) => + Shared.Models.Stateless.Methods.IPath.GetRelativePath(path, propertyConfiguration.RootDirectory.Length); internal static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) { @@ -21,12 +16,66 @@ internal class Result return result; } - internal static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) + internal static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) { - string result = Shared.Models.Stateless.Methods.IPath.GetRelativePath(path, propertyConfiguration.RootDirectory.Length); + string result = Path.Combine($"{propertyConfiguration.RootDirectory}-Results", description.Replace('_', ')')); + if (create && !Directory.Exists(result)) + _ = Directory.CreateDirectory(result); return result; } + internal static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) + { + string result = Path.Combine(GetResultsDateGroupDirectory(propertyConfiguration, description), jsonGroup); + if (!Directory.Exists(result)) + _ = Directory.CreateDirectory(result); + return result; + } + + internal static string GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) + { + string result = GetResultsDateGroupDirectory(propertyConfiguration, description); + if (includeResizeGroup) + result = Path.Combine(result, outputResolution); + if (includeModel && includePredictorModel) + { + string modelName; + string predictorModelName; + if (propertyConfiguration.ModelName is null) + modelName = Model.Hog.ToString(); + else + modelName = propertyConfiguration.ModelName; + if (propertyConfiguration.PredictorModelName is null) + predictorModelName = PredictorModel.Large.ToString(); + else + predictorModelName = propertyConfiguration.PredictorModelName; + string dateGroupDirectory = string.Concat(outputResolution.Replace(" ", string.Empty), "-", modelName, "-", predictorModelName, "-", propertyConfiguration.NumberOfJitters, "-", propertyConfiguration.NumberOfTimesToUpsample); + result = Path.Combine(result, dateGroupDirectory); + } + else if (includeModel) + throw new Exception(); + else if (includePredictorModel) + throw new Exception(); + if (!Directory.Exists(result)) + _ = Directory.CreateDirectory(result); + return result; + } + + internal static List GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) + { + List results = []; + string sourceDirectorySegment = GetRelativePath(propertyConfiguration, sourceDirectory); + string result = string.Concat(Path.Combine(dateGroupDirectory, "<>"), sourceDirectorySegment); + if (!string.IsNullOrEmpty(contentDescription)) + CheckContent(propertyConfiguration, dateGroupDirectory, contentDescription, result); + if (!string.IsNullOrEmpty(singletonDescription)) + CheckSingleton(propertyConfiguration, dateGroupDirectory, singletonDescription, converted, result); + if (!string.IsNullOrEmpty(collectionDescription)) + CheckCollection(propertyConfiguration, dateGroupDirectory, collectionDescription, converted, result); + results.Add(result); + return results; + } + private static void CheckContent(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string dateGroupDirectory, string contentDescription, string result) { string checkDirectory; @@ -74,58 +123,6 @@ internal class Result _ = Directory.CreateDirectory(checkDirectory); } - internal static string GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) - { - string result = GetResultsDateGroupDirectory(propertyConfiguration, description); - if (includeResizeGroup) - result = Path.Combine(result, outputResolution); - if (includeModel && includePredictorModel) - { - string modelName; - string predictorModelName; - if (propertyConfiguration.ModelName is null) - modelName = Model.Hog.ToString(); - else - modelName = propertyConfiguration.ModelName; - if (propertyConfiguration.PredictorModelName is null) - predictorModelName = PredictorModel.Large.ToString(); - else - predictorModelName = propertyConfiguration.PredictorModelName; - string dateGroupDirectory = string.Concat(outputResolution.Replace(" ", string.Empty), "-", modelName, "-", predictorModelName, "-", propertyConfiguration.NumberOfJitters, "-", propertyConfiguration.NumberOfTimesToUpsample); - result = Path.Combine(result, dateGroupDirectory); - } - else if (includeModel) - throw new Exception(); - else if (includePredictorModel) - throw new Exception(); - if (!Directory.Exists(result)) - _ = Directory.CreateDirectory(result); - return result; - } - - internal static List GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) - { - List results = []; - string sourceDirectorySegment = GetRelativePath(propertyConfiguration, sourceDirectory); - string result = string.Concat(Path.Combine(dateGroupDirectory, "<>"), sourceDirectorySegment); - if (!string.IsNullOrEmpty(contentDescription)) - CheckContent(propertyConfiguration, dateGroupDirectory, contentDescription, result); - if (!string.IsNullOrEmpty(singletonDescription)) - CheckSingleton(propertyConfiguration, dateGroupDirectory, singletonDescription, converted, result); - if (!string.IsNullOrEmpty(collectionDescription)) - CheckCollection(propertyConfiguration, dateGroupDirectory, collectionDescription, converted, result); - results.Add(result); - return results; - } - - internal static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) - { - string result = Path.Combine($"{propertyConfiguration.RootDirectory}-Results", description.Replace('_', ')')); - if (create && !Directory.Exists(result)) - _ = Directory.CreateDirectory(result); - return result; - } - internal static List GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) { List results; diff --git a/Resize/Models/_C_Resize.cs b/Resize/Models/_C_Resize.cs index 40a1c83..905288b 100644 --- a/Resize/Models/_C_Resize.cs +++ b/Resize/Models/_C_Resize.cs @@ -66,6 +66,98 @@ public class C_Resize _ConstructorInfo = constructorInfo; } + public static byte[] GetBitmapData(Bitmap bitmap) + { + byte[] results; +#pragma warning disable CA1416 + Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height); + BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat); + IntPtr intPtr = bitmapData.Scan0; + int length = bitmapData.Stride * bitmap.Height; + results = new byte[length]; + Marshal.Copy(intPtr, results, 0, length); + bitmap.UnlockBits(bitmapData); +#pragma warning restore CA1416 + return results; + } + + public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetPngLowQuality() + { + (ImageCodecInfo, EncoderParameters, string) result; +#pragma warning disable CA1416 + ImageFormat imageFormat = ImageFormat.Png; + ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders(); + ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First(); + EncoderParameters encoderParameters = new(1); + encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); + if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) + throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); + result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]); +#pragma warning restore CA1416 + return result; + } + + public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetJpegLowQuality() + { + (ImageCodecInfo, EncoderParameters, string) result; +#pragma warning disable CA1416 + ImageFormat imageFormat = ImageFormat.Jpeg; + ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders(); + ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First(); + EncoderParameters encoderParameters = new(1); + encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); + if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) + throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); + result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]); +#pragma warning restore CA1416 + return result; + } + + public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetTuple(string outputExtension, int outputQuality) + { + (ImageCodecInfo, EncoderParameters, string) result; +#pragma warning disable CA1416 + ImageFormat imageFormat = outputExtension switch + { + ".gif" => ImageFormat.Gif, + ".jfif" => ImageFormat.Jpeg, + ".jpe" => ImageFormat.Jpeg, + ".jpeg" => ImageFormat.Jpeg, + ".jpg" => ImageFormat.Jpeg, + ".png" => ImageFormat.Png, + ".tif" => ImageFormat.Tiff, + ".tiff" => ImageFormat.Tiff, + _ => throw new Exception(), + }; + ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders(); + ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First(); + EncoderParameters encoderParameters = new(1); + // encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(75L, Int32)) 'Default + // encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(95L, Int32)) 'Paint + encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, outputQuality); + if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) + throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); + result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]); +#pragma warning restore CA1416 + return result; + } + + public FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, Item item, bool outputResolutionHasNumber, int id) => + GetResizedFileHolder(cResultsFullGroupDirectory, item.FilePath, outputResolutionHasNumber, $"{id}{item.FilePath.ExtensionLowered}"); + + private FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, FilePath filePath, bool outputResolutionHasNumber, string fileName) + { + FileHolder result; + if (outputResolutionHasNumber) + result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(AngleBracketCollection[0].Replace("<>", _PropertyConfiguration.ResultContent), fileName)); + else + { + CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath); + result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(cResultsFullGroupDirectory, _PropertyConfiguration.ResultContent, cei.Combined, fileName)); + } + return result; + } + public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); @@ -96,373 +188,10 @@ public class C_Resize converted: true)); } -#pragma warning disable CA1416 - - public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetJpegLowQuality() - { - (ImageCodecInfo, EncoderParameters, string) result; - ImageFormat imageFormat = ImageFormat.Jpeg; - ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders(); - ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First(); - EncoderParameters encoderParameters = new(1); - encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); - if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) - throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); - result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]); - return result; - } - - public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetPngLowQuality() - { - (ImageCodecInfo, EncoderParameters, string) result; - ImageFormat imageFormat = ImageFormat.Png; - ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders(); - ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First(); - EncoderParameters encoderParameters = new(1); - encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); - if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) - throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); - result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]); - return result; - } - - public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetTuple(string outputExtension, int outputQuality) - { - (ImageCodecInfo, EncoderParameters, string) result; - ImageFormat imageFormat = outputExtension switch - { - ".gif" => ImageFormat.Gif, - ".jfif" => ImageFormat.Jpeg, - ".jpe" => ImageFormat.Jpeg, - ".jpeg" => ImageFormat.Jpeg, - ".jpg" => ImageFormat.Jpeg, - ".png" => ImageFormat.Png, - ".tif" => ImageFormat.Tiff, - ".tiff" => ImageFormat.Tiff, - _ => throw new Exception(), - }; - ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders(); - ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First(); - EncoderParameters encoderParameters = new(1); - // encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(75L, Int32)) 'Default - // encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(95L, Int32)) 'Paint - encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, outputQuality); - if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) - throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); - result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]); - return result; - } - - public static byte[] GetBitmapData(Bitmap bitmap) - { - byte[] results; - Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height); - BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat); - IntPtr intPtr = bitmapData.Scan0; - int length = bitmapData.Stride * bitmap.Height; - results = new byte[length]; - Marshal.Copy(intPtr, results, 0, length); - bitmap.UnlockBits(bitmapData); - return results; - } - - private void CopyPropertyItems(byte[] bytes, PropertyItem[] propertyItems, Bitmap bitmap) - { - bool hasId = false; - int id = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized; - int imageWidth = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageWidth; - int imageHeight = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageHeight; - int orientation = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation; - foreach (PropertyItem propertyItem in propertyItems) - { - if (propertyItem.Id == id) - hasId = true; - else if (propertyItem.Id == imageWidth) - continue; - else if (propertyItem.Id == imageHeight) - continue; - else if (propertyItem.Id == orientation) - continue; - bitmap.SetPropertyItem(propertyItem); - } - if (!hasId) - { - PropertyItem propertyItem = (PropertyItem)_ConstructorInfo.Invoke(null); - propertyItem.Id = id; - propertyItem.Len = bytes.Length; - propertyItem.Type = 2; - propertyItem.Value = bytes; - bitmap.SetPropertyItem(propertyItem); - } - } - - private void SaveResizedSubfile3(MappingFromItem mappingFromItem, int[] resize, byte[] bytes) - { - Bitmap bitmap; - int outputResolutionWidth = resize[_OutputResolutionWidthIndex]; - using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false); - int outputResolutionHeight = resize[_OutputResolutionHeightIndex]; - PropertyItem[] propertyItems = temp.PropertyItems; - int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex]; - bitmap = new(temp, outputResolutionWidth, outputResolutionHeight); - switch (outputResolutionOrientation) // exif 274 - { - case 0: - break; - case 1: - break; // 1 = Horizontal (normal) - case 2: - bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX); - break; // 2 = Mirror horizontal - case 3: - bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone); - break; // 3 = Rotate 180 - case 4: - bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); - break; // 4 = Mirror vertical - case 5: - bitmap.RotateFlip(RotateFlipType.Rotate270FlipX); - break; // 5 = Mirror horizontal and rotate 270 CW - case 6: - bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone); - break; // 6 = Rotate 90 CW - case 7: - bitmap.RotateFlip(RotateFlipType.Rotate90FlipX); - break; // 7 = Mirror horizontal and rotate 90 CW - case 8: - bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone); - break; // 8 = Rotate 270 CW - default: - break; - } - CopyPropertyItems(bytes, propertyItems, bitmap); - bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters); - bitmap.Dispose(); - } - - private void SaveResizedSubfile5(MappingFromItem mappingFromItem, int[] resize, byte[] bytes) - { - Bitmap bitmap; - using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false); - PropertyItem[] propertyItems = temp.PropertyItems; - int tempResolutionWidth = resize[_TempResolutionWidth]; - int tempResolutionHeight = resize[_TempResolutionHeight]; - int outputResolutionWidth = resize[_OutputResolutionWidthIndex]; - int outputResolutionHeight = resize[_OutputResolutionHeightIndex]; - int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex]; - bitmap = new(temp, tempResolutionWidth, tempResolutionHeight); - switch (outputResolutionOrientation) // exif 274 - { - case 0: - break; - case 1: - break; // 1 = Horizontal (normal) - case 2: - bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX); - break; // 2 = Mirror horizontal - case 3: - bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone); - break; // 3 = Rotate 180 - case 4: - bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); - break; // 4 = Mirror vertical - case 5: - bitmap.RotateFlip(RotateFlipType.Rotate270FlipX); - break; // 5 = Mirror horizontal and rotate 270 CW - case 6: - bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone); - break; // 6 = Rotate 90 CW - case 7: - bitmap.RotateFlip(RotateFlipType.Rotate90FlipX); - break; // 7 = Mirror horizontal and rotate 90 CW - case 8: - bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone); - break; // 8 = Rotate 270 CW - default: - break; - } - Bitmap preRotated; - Rectangle rectangle; - if (tempResolutionHeight < tempResolutionWidth) - rectangle = new Rectangle((int)((tempResolutionWidth - outputResolutionWidth) * .5), 0, outputResolutionWidth, outputResolutionHeight); - else - rectangle = new Rectangle(0, (int)((tempResolutionHeight - outputResolutionHeight) * .5), outputResolutionWidth, outputResolutionHeight); - using (preRotated = new Bitmap(outputResolutionWidth, outputResolutionHeight)) - { - using (Graphics graphics = Graphics.FromImage(preRotated)) - graphics.DrawImage(bitmap, new Rectangle(0, 0, outputResolutionWidth, outputResolutionHeight), rectangle, GraphicsUnit.Pixel); - CopyPropertyItems(bytes, propertyItems, bitmap); - bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters); - } - bitmap.Dispose(); - } - -#pragma warning restore CA1416 - - private void SaveResizedSubfile(Shared.Models.Property property, MappingFromItem mappingFromItem, int[] resize) - { - string dateTimeFormat = IProperty.DateTimeFormat(); - DateTime dateTime = property.DateTimeOriginal is not null ? property.DateTimeOriginal.Value : Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(property); - string dateTimeValue = dateTime.ToString(dateTimeFormat); - byte[] bytes = _ASCIIEncoding.GetBytes(dateTimeValue); - if (_ASCIIEncoding.GetString(bytes, 0, bytes.Length) != dateTimeValue) - throw new Exception(); - if (resize.Length == 3) - SaveResizedSubfile3(mappingFromItem, resize, bytes); - else if (resize.Length == 5) - SaveResizedSubfile5(mappingFromItem, resize, bytes); - else - throw new Exception(); - } - - public void SaveResizedSubfile(Property.Models.Configuration configuration, string outputResolution, string cResultsFullGroupDirectory, List> subFileTuples, Item item, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize) - { - if (mappingFromItem.ResizedFileHolder is null) - throw new NullReferenceException(nameof(mappingFromItem.ResizedFileHolder)); -#pragma warning disable CA1854 - if (!outputResolutionToResize.ContainsKey(_Original)) - throw new Exception(); - if (!outputResolutionToResize.ContainsKey(outputResolution)) - throw new Exception(); -#pragma warning restore CA1854 - FileInfo fileInfo = new(mappingFromItem.ResizedFileHolder.FullName); - bool check = false; - int[] resize = outputResolutionToResize[outputResolution]; - int outputResolutionWidth = resize[_OutputResolutionWidthIndex]; - int outputResolutionHeight = resize[_OutputResolutionHeightIndex]; - int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex]; - int[] originalCollection = outputResolutionToResize[_Original]; - string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)]; - List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); - if (_OverrideForResizeImages) - check = true; - else if (!fileInfo.Exists) - check = true; - if (outputResolutionWidth == originalCollection[_OutputResolutionWidthIndex] && outputResolutionHeight == originalCollection[_OutputResolutionHeightIndex] && outputResolutionOrientation == originalCollection[_OutputResolutionOrientationIndex]) - { - if (!check && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.CreationTime.AddDays(1)) - check = true; - if (check) - { - // if (fileInfo.Exists) - // File.Delete(fileInfo.FullName); - // File.Copy(mappingFromItem.FilePath.FullName, fileInfo.FullName); - // item.SetResizedFileHolder(_FileNameExtension, Shared.Models.Stateless.Methods.IFileHolder.Refresh(mappingFromItem.ResizedFileHolder)); - // subFileTuples.Add(new Tuple(nameof(C_Resize), DateTime.Now)); - } - } - else - { - if (!check && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime) - check = true; - if (check) - { - SaveResizedSubfile(property, mappingFromItem, resize); - item.SetResizedFileHolder(_FileNameExtension, Shared.Models.Stateless.Methods.IFileHolder.Refresh(mappingFromItem.ResizedFileHolder)); - subFileTuples.Add(new Tuple(nameof(C_Resize), DateTime.Now)); - } - } - } - - private static int[] GetCollection(string outputResolution) - { - List results = []; - string[] segments = outputResolution.Split('x'); - results.Add(int.Parse(segments[0])); - results.Add(int.Parse(segments[1])); - return results.ToArray(); - } - - private Dictionary GetImageResizes(Shared.Models.Property property) - { - Dictionary results = []; - int[] desired; - int checkWidth; - int checkHeight; - int desiredWidth; - int desiredHeight; - int orientation = property.Orientation is null || string.IsNullOrEmpty(property.Orientation) || !int.TryParse(property.Orientation, out int propertyOrientation) ? 0 : propertyOrientation; - if (property is null || property.Width is null || property.Height is null) - throw new NotSupportedException(); - checkWidth = property.Width.Value; - checkHeight = property.Height.Value; - if (!_ValidResolutions.Contains(_Original)) - results.Add(_Original, [checkWidth, checkHeight, orientation]); - foreach (string validResolution in _ValidResolutions) - { - if (validResolution == _Original) - { - desiredWidth = checkWidth; - desiredHeight = checkHeight; - } - else - { - desired = GetCollection(validResolution); - desiredWidth = desired[0]; - desiredHeight = desired[1]; - } - if (checkWidth <= desiredWidth && checkHeight <= desiredHeight) - results.Add(validResolution, [checkWidth, checkHeight, orientation]); - else - { - if (desiredWidth != desiredHeight) - { - if (checkWidth * desiredHeight > desiredWidth * checkHeight) - results.Add(validResolution, [desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth), orientation]); - else - results.Add(validResolution, [Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight, orientation]); - } - else - { - if (checkWidth * desiredHeight <= desiredWidth * checkHeight) - results.Add(validResolution, [desiredWidth, desiredHeight, orientation, desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth)]); - else - results.Add(validResolution, [desiredWidth, desiredHeight, orientation, Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight]); - } - } - } - return results; - } - - private FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, FilePath filePath, bool outputResolutionHasNumber, string fileName) - { - FileHolder result; - if (outputResolutionHasNumber) - result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(AngleBracketCollection[0].Replace("<>", _PropertyConfiguration.ResultContent), fileName)); - else - { - CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath); - result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(cResultsFullGroupDirectory, _PropertyConfiguration.ResultContent, cei.Combined, fileName)); - } - return result; - } - public FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, Item item, bool outputResolutionHasNumber) => GetResizedFileHolder(cResultsFullGroupDirectory, item.FilePath, outputResolutionHasNumber, item.FilePath.Name); - public FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, Item item, bool outputResolutionHasNumber, int id) => - GetResizedFileHolder(cResultsFullGroupDirectory, item.FilePath, outputResolutionHasNumber, $"{id}{item.FilePath.ExtensionLowered}"); - - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo) - { - string[] segments = directory.Split(cei.Combined); - string? checkDirectory = segments.Length == 1 ? - Path.Combine(segments[0], $"{cei.Combined[2..]}") : - segments.Length == 2 ? - $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : - null; - if (checkDirectory is not null && Directory.Exists(checkDirectory)) - { - string checkFile = Path.Combine(checkDirectory, fileName); - if (File.Exists(checkFile)) - { - File.Move(checkFile, fileInfo.FullName); - fileInfo.Refresh(); - } - } - } - - public Dictionary GetResizeKeyValuePairs(Property.Models.Configuration configuration, string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem) + public Dictionary GetResizeKeyValuePairs(Configuration configuration, string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem) { Dictionary? results; string json; @@ -528,4 +257,285 @@ public class C_Resize return results; } + private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo) + { + string[] segments = directory.Split(cei.Combined); + string? checkDirectory = segments.Length == 1 ? + Path.Combine(segments[0], $"{cei.Combined[2..]}") : + segments.Length == 2 ? + $"{segments[0]}{cei.Combined[2..]}{segments[1]}" : + null; + if (checkDirectory is not null && Directory.Exists(checkDirectory)) + { + string checkFile = Path.Combine(checkDirectory, fileName); + if (File.Exists(checkFile)) + { + File.Move(checkFile, fileInfo.FullName); + fileInfo.Refresh(); + } + } + } + + private Dictionary GetImageResizes(Shared.Models.Property property) + { + Dictionary results = []; + int[] desired; + int checkWidth; + int checkHeight; + int desiredWidth; + int desiredHeight; + int orientation = property.Orientation is null || string.IsNullOrEmpty(property.Orientation) || !int.TryParse(property.Orientation, out int propertyOrientation) ? 0 : propertyOrientation; + if (property is null || property.Width is null || property.Height is null) + throw new NotSupportedException(); + checkWidth = property.Width.Value; + checkHeight = property.Height.Value; + if (!_ValidResolutions.Contains(_Original)) + results.Add(_Original, [checkWidth, checkHeight, orientation]); + foreach (string validResolution in _ValidResolutions) + { + if (validResolution == _Original) + { + desiredWidth = checkWidth; + desiredHeight = checkHeight; + } + else + { + desired = GetCollection(validResolution); + desiredWidth = desired[0]; + desiredHeight = desired[1]; + } + if (checkWidth <= desiredWidth && checkHeight <= desiredHeight) + results.Add(validResolution, [checkWidth, checkHeight, orientation]); + else + { + if (desiredWidth != desiredHeight) + { + if (checkWidth * desiredHeight > desiredWidth * checkHeight) + results.Add(validResolution, [desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth), orientation]); + else + results.Add(validResolution, [Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight, orientation]); + } + else + { + if (checkWidth * desiredHeight <= desiredWidth * checkHeight) + results.Add(validResolution, [desiredWidth, desiredHeight, orientation, desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth)]); + else + results.Add(validResolution, [desiredWidth, desiredHeight, orientation, Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight]); + } + } + } + return results; + } + + private static int[] GetCollection(string outputResolution) + { + List results = []; + string[] segments = outputResolution.Split('x'); + results.Add(int.Parse(segments[0])); + results.Add(int.Parse(segments[1])); + return results.ToArray(); + } + + public void SaveResizedSubfile(Configuration configuration, string outputResolution, string cResultsFullGroupDirectory, List> subFileTuples, Item item, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize) + { + if (mappingFromItem.ResizedFileHolder is null) + throw new NullReferenceException(nameof(mappingFromItem.ResizedFileHolder)); +#pragma warning disable CA1854 + if (!outputResolutionToResize.ContainsKey(_Original)) + throw new Exception(); + if (!outputResolutionToResize.ContainsKey(outputResolution)) + throw new Exception(); +#pragma warning restore CA1854 + FileInfo fileInfo = new(mappingFromItem.ResizedFileHolder.FullName); + bool check = false; + int[] resize = outputResolutionToResize[outputResolution]; + int outputResolutionWidth = resize[_OutputResolutionWidthIndex]; + int outputResolutionHeight = resize[_OutputResolutionHeightIndex]; + int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex]; + int[] originalCollection = outputResolutionToResize[_Original]; + string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)]; + List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); + if (_OverrideForResizeImages) + check = true; + else if (!fileInfo.Exists) + check = true; + if (outputResolutionWidth == originalCollection[_OutputResolutionWidthIndex] && outputResolutionHeight == originalCollection[_OutputResolutionHeightIndex] && outputResolutionOrientation == originalCollection[_OutputResolutionOrientationIndex]) + { + if (!check && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.CreationTime.AddDays(1)) + check = true; + if (check) + { + // if (fileInfo.Exists) + // File.Delete(fileInfo.FullName); + // File.Copy(mappingFromItem.FilePath.FullName, fileInfo.FullName); + // item.SetResizedFileHolder(_FileNameExtension, Shared.Models.Stateless.Methods.IFileHolder.Refresh(mappingFromItem.ResizedFileHolder)); + // subFileTuples.Add(new Tuple(nameof(C_Resize), DateTime.Now)); + } + } + else + { + if (!check && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime) + check = true; + if (check) + { + SaveResizedSubfile(property, mappingFromItem, resize); + item.SetResizedFileHolder(_FileNameExtension, Shared.Models.Stateless.Methods.IFileHolder.Refresh(mappingFromItem.ResizedFileHolder)); + subFileTuples.Add(new Tuple(nameof(C_Resize), DateTime.Now)); + } + } + } + + private void SaveResizedSubfile(Shared.Models.Property property, MappingFromItem mappingFromItem, int[] resize) + { + string dateTimeFormat = IProperty.DateTimeFormat; + DateTime dateTime = property.DateTimeOriginal is not null ? property.DateTimeOriginal.Value : Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(property); + string dateTimeValue = dateTime.ToString(dateTimeFormat); + byte[] bytes = _ASCIIEncoding.GetBytes(dateTimeValue); + if (_ASCIIEncoding.GetString(bytes, 0, bytes.Length) != dateTimeValue) + throw new Exception(); + if (resize.Length == 3) + SaveResizedSubfile3(mappingFromItem, resize, bytes); + else if (resize.Length == 5) + SaveResizedSubfile5(mappingFromItem, resize, bytes); + else + throw new Exception(); + } + + private void SaveResizedSubfile3(MappingFromItem mappingFromItem, int[] resize, byte[] bytes) + { + Bitmap bitmap; +#pragma warning disable CA1416 + int outputResolutionWidth = resize[_OutputResolutionWidthIndex]; + using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false); + int outputResolutionHeight = resize[_OutputResolutionHeightIndex]; + PropertyItem[] propertyItems = temp.PropertyItems; + int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex]; + bitmap = new(temp, outputResolutionWidth, outputResolutionHeight); + switch (outputResolutionOrientation) // exif 274 + { + case 0: + break; + case 1: + break; // 1 = Horizontal (normal) + case 2: + bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX); + break; // 2 = Mirror horizontal + case 3: + bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone); + break; // 3 = Rotate 180 + case 4: + bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); + break; // 4 = Mirror vertical + case 5: + bitmap.RotateFlip(RotateFlipType.Rotate270FlipX); + break; // 5 = Mirror horizontal and rotate 270 CW + case 6: + bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone); + break; // 6 = Rotate 90 CW + case 7: + bitmap.RotateFlip(RotateFlipType.Rotate90FlipX); + break; // 7 = Mirror horizontal and rotate 90 CW + case 8: + bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone); + break; // 8 = Rotate 270 CW + default: + break; + } + CopyPropertyItems(bytes, propertyItems, bitmap); + bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters); + bitmap.Dispose(); +#pragma warning restore CA1416 + } + + private void CopyPropertyItems(byte[] bytes, PropertyItem[] propertyItems, Bitmap bitmap) + { + bool hasId = false; + int id = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized; + int imageWidth = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageWidth; + int imageHeight = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageHeight; + int orientation = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation; +#pragma warning disable CA1416 + foreach (PropertyItem propertyItem in propertyItems) + { + if (propertyItem.Id == id) + hasId = true; + else if (propertyItem.Id == imageWidth) + continue; + else if (propertyItem.Id == imageHeight) + continue; + else if (propertyItem.Id == orientation) + continue; + bitmap.SetPropertyItem(propertyItem); + } + if (!hasId) + { + PropertyItem propertyItem = (PropertyItem)_ConstructorInfo.Invoke(null); + propertyItem.Id = id; + propertyItem.Len = bytes.Length; + propertyItem.Type = 2; + propertyItem.Value = bytes; + bitmap.SetPropertyItem(propertyItem); + } +#pragma warning restore CA1416 + } + + private void SaveResizedSubfile5(MappingFromItem mappingFromItem, int[] resize, byte[] bytes) + { + Bitmap bitmap; +#pragma warning disable CA1416 + using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false); + PropertyItem[] propertyItems = temp.PropertyItems; + int tempResolutionWidth = resize[_TempResolutionWidth]; + int tempResolutionHeight = resize[_TempResolutionHeight]; + int outputResolutionWidth = resize[_OutputResolutionWidthIndex]; + int outputResolutionHeight = resize[_OutputResolutionHeightIndex]; + int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex]; + bitmap = new(temp, tempResolutionWidth, tempResolutionHeight); + switch (outputResolutionOrientation) // exif 274 + { + case 0: + break; + case 1: + break; // 1 = Horizontal (normal) + case 2: + bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX); + break; // 2 = Mirror horizontal + case 3: + bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone); + break; // 3 = Rotate 180 + case 4: + bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); + break; // 4 = Mirror vertical + case 5: + bitmap.RotateFlip(RotateFlipType.Rotate270FlipX); + break; // 5 = Mirror horizontal and rotate 270 CW + case 6: + bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone); + break; // 6 = Rotate 90 CW + case 7: + bitmap.RotateFlip(RotateFlipType.Rotate90FlipX); + break; // 7 = Mirror horizontal and rotate 90 CW + case 8: + bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone); + break; // 8 = Rotate 270 CW + default: + break; + } + Bitmap preRotated; + Rectangle rectangle; + if (tempResolutionHeight < tempResolutionWidth) + rectangle = new Rectangle((int)((tempResolutionWidth - outputResolutionWidth) * .5), 0, outputResolutionWidth, outputResolutionHeight); + else + rectangle = new Rectangle(0, (int)((tempResolutionHeight - outputResolutionHeight) * .5), outputResolutionWidth, outputResolutionHeight); + using (preRotated = new Bitmap(outputResolutionWidth, outputResolutionHeight)) + { + using (Graphics graphics = Graphics.FromImage(preRotated)) + graphics.DrawImage(bitmap, new Rectangle(0, 0, outputResolutionWidth, outputResolutionHeight), rectangle, GraphicsUnit.Pixel); + CopyPropertyItems(bytes, propertyItems, bitmap); + bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters); + } + bitmap.Dispose(); +#pragma warning restore CA1416 + } + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IDirectory.cs b/Shared/Models/Stateless/Methods/IDirectory.cs index 52f24fe..0449347 100644 --- a/Shared/Models/Stateless/Methods/IDirectory.cs +++ b/Shared/Models/Stateless/Methods/IDirectory.cs @@ -5,59 +5,70 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IDirectory { - char TestStatic_GetDirectory(string fileName) => - GetDirectory(fileName); - static char GetDirectory(string fileName) => - fileName.Split('-').Length > 2 ? '-' : fileName.Split('.')[0][^1]; - - int TestStatic_GetDirectory(char directory) => - GetDirectory(directory); - static int GetDirectory(char directory) => + public static int GetDirectory(char directory) => directory == '-' ? 10 : int.TryParse(directory.ToString(), out int value) ? value : 11; - ReadOnlyCollection TestStatic_GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) => - GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage); - static ReadOnlyCollection GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) => - XDirectory.GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage); + public static char GetDirectory(string fileName) => + fileName.Split('-').Length > 2 ? '-' : fileName.Split('.')[0][^1]; - ReadOnlyCollection> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) => - GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage); - static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) => - XDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage); - - void TestStatic_MoveFiles(List files, string find, string replace) => - MoveFiles(files, find, replace); - static void MoveFiles(List files, string find, string replace) => - XDirectory.MoveFiles(files, find, replace); - - (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) => - GetToDoCollection(propertyConfiguration, filePathsCollection, fileGroups, tick); - static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) => - XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates: false, ifCanUseId: true, filePathsCollection, fileGroups, tick); - - (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) => - GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, tick); - static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) => + public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) => XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, tick); - List TestStatic_CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) => - CopyOrMove(toDoCollection, move, moveBack, tick); - static List CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) => + public static void MoveFiles(List files, string find, string replace) => + XDirectory.MoveFiles(files, find, replace); + + public static List CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) => XDirectory.CopyOrMove(toDoCollection, move, moveBack, tick); - ReadOnlyCollection> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) => - GetFilePathCollections(propertyConfiguration, filesCollection); - static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) => - XDirectory.GetFilePathCollections(propertyConfiguration, filesCollection); - - List TestStatic_GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) => - GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); - static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) => - XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); - - int TestStatic_MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) => - MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension); - static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) => + public static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) => XDirectory.MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension); + public static ReadOnlyCollection GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) => + XDirectory.GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage); + + public static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) => + XDirectory.GetFilePathCollections(propertyConfiguration, filesCollection); + + public static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) => + XDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage); + + public static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) => + XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); + + public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) => + XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates: false, ifCanUseId: true, filePathsCollection, fileGroups, tick); + + internal int TestStatic_GetDirectory(char directory) => + GetDirectory(directory); + + internal char TestStatic_GetDirectory(string fileName) => + GetDirectory(fileName); + + internal void TestStatic_MoveFiles(List files, string find, string replace) => + MoveFiles(files, find, replace); + + internal List TestStatic_CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) => + CopyOrMove(toDoCollection, move, moveBack, tick); + + internal int TestStatic_MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) => + MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension); + + internal ReadOnlyCollection TestStatic_GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) => + GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage); + + internal ReadOnlyCollection> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) => + GetFilePathCollections(propertyConfiguration, filesCollection); + + internal ReadOnlyCollection> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) => + GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage); + + internal List TestStatic_GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) => + GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); + + internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) => + GetToDoCollection(propertyConfiguration, filePathsCollection, fileGroups, tick); + + internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) => + GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, tick); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IId.cs b/Shared/Models/Stateless/Methods/IId.cs index 92ea55c..92ec53b 100644 --- a/Shared/Models/Stateless/Methods/IId.cs +++ b/Shared/Models/Stateless/Methods/IId.cs @@ -1,63 +1,75 @@ +using View_by_Distance.Shared.Models.Properties; + namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IId -{ // ... +{ const int DeterministicHashCode = 9876543; - static bool IsOffsetDeterministicHashCode(Properties.IPropertyConfiguration propertyConfiguration) => - propertyConfiguration.Offset == DeterministicHashCode; - - string TestStatic_GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) => - GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal); - static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) => - Id.GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal); - - int TestStatic_GetId(Properties.IPropertyConfiguration propertyConfiguration, string intelligentId) => - GetId(propertyConfiguration, intelligentId); - static int GetId(Properties.IPropertyConfiguration propertyConfiguration, string intelligentId) => - Id.GetId(propertyConfiguration, intelligentId); - - string TestStatic_GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) => - GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index); - static string GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) => - Id.GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index); - - bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) => - NameWithoutExtensionIsIntelligentIdFormat(propertyConfiguration, fileNameFirstSegment); - static bool NameWithoutExtensionIsIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) => - fileNameFirstSegment.Length - 1 == propertyConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '3' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber); - - bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) => - NameWithoutExtensionIsPaddedIntelligentIdFormat(propertyConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment); - static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) => - fileNameFirstSegment.Length == propertyConfiguration.IntMinValueLength + sortOrderOnlyLengthIndex + 1 - && fileNameFirstSegment[^1] is '1' or '2' or '3' or '7' or '8' or '9' - && fileNameFirstSegment.All(char.IsNumber); - - bool TestStatic_NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) => - NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder); - static bool NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) => - Id.NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder.NameWithoutExtension.Split('.')[0]); - - int TestStatic_GetDeterministicHashCode(byte[] value) => - GetDeterministicHashCode(value); - static int GetDeterministicHashCode(byte[] value) => + public static int GetDeterministicHashCode(byte[] value) => Id.GetDeterministicHashCode(value); - int TestStatic_GetHasIgnoreKeyword(FilePath filePath) => - GetHasIgnoreKeyword(filePath); - static int GetHasIgnoreKeyword(FilePath filePath) => + public static byte GetHasIgnoreKeyword(FilePath filePath) => Id.GetHasIgnoreKeyword(filePath); - int TestStatic_GetMissingDateTimeOriginal(FilePath filePath) => - GetMissingDateTimeOriginal(filePath); - static int GetMissingDateTimeOriginal(FilePath filePath) => - Id.GetMissingDateTimeOriginal(filePath); + public static int GetId(IPropertyConfiguration propertyConfiguration, string intelligentId) => + Id.GetId(propertyConfiguration, intelligentId); - int TestStatic_GetHasDateTimeOriginal(FilePath filePath) => - GetHasDateTimeOriginal(filePath); - static int GetHasDateTimeOriginal(FilePath filePath) => - Id.GetHasDateTimeOriginal(filePath); + public static bool IsOffsetDeterministicHashCode(IPropertyConfiguration propertyConfiguration) => + propertyConfiguration.Offset == DeterministicHashCode; + + public static byte GetHasDateTimeOriginal(IPropertyConfiguration propertyConfiguration, FilePath filePath) => + Id.GetHasDateTimeOriginal(propertyConfiguration, filePath); + + public static byte GetMissingDateTimeOriginal(IPropertyConfiguration propertyConfiguration, FilePath filePath) => + Id.GetMissingDateTimeOriginal(propertyConfiguration, filePath); + + public static bool NameWithoutExtensionIsIdFormat(IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) => + Id.NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder.NameWithoutExtension.Split('.')[0]); + + public static bool NameWithoutExtensionIsIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) => + fileNameFirstSegment.Length - 1 == propertyConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber); + + public static string GetIntelligentId(IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) => + Id.GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal); + + public static string GetPaddedId(IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) => + Id.GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index); + + public static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) => + fileNameFirstSegment.Length == propertyConfiguration.IntMinValueLength + sortOrderOnlyLengthIndex + 1 + && fileNameFirstSegment[^1] is '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' + && fileNameFirstSegment.All(char.IsNumber); + + internal int TestStatic_GetDeterministicHashCode(byte[] value) => + GetDeterministicHashCode(value); + + internal byte TestStatic_GetHasIgnoreKeyword(FilePath filePath) => + GetHasIgnoreKeyword(filePath); + + internal int TestStatic_GetId(IPropertyConfiguration propertyConfiguration, string intelligentId) => + GetId(propertyConfiguration, intelligentId); + + internal byte TestStatic_GetHasDateTimeOriginal(IPropertyConfiguration propertyConfiguration, FilePath filePath) => + GetHasDateTimeOriginal(propertyConfiguration, filePath); + + internal byte TestStatic_GetMissingDateTimeOriginal(IPropertyConfiguration propertyConfiguration, FilePath filePath) => + GetMissingDateTimeOriginal(propertyConfiguration, filePath); + + internal bool TestStatic_NameWithoutExtensionIsIdFormat(IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) => + NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder); + + internal bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) => + NameWithoutExtensionIsIntelligentIdFormat(propertyConfiguration, fileNameFirstSegment); + + internal string TestStatic_GetIntelligentId(IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) => + GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal); + + internal string TestStatic_GetPaddedId(IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) => + GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index); + + internal bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) => + NameWithoutExtensionIsPaddedIntelligentIdFormat(propertyConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IPath.cs b/Shared/Models/Stateless/Methods/IPath.cs index fd28965..4ab786d 100644 --- a/Shared/Models/Stateless/Methods/IPath.cs +++ b/Shared/Models/Stateless/Methods/IPath.cs @@ -4,77 +4,89 @@ using View_by_Distance.Shared.Models.Properties; namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IPath -{ // ... +{ - string TestStatic_GetRelativePath(string path, int length) => - GetRelativePath(path, length); - static string GetRelativePath(string path, int length) => - XPath.GetRelativePath(path, length, forceExtensionToLower: false); - - bool TestStatic_DeleteEmptyDirectories(string rootDirectory) => - DeleteEmptyDirectories(rootDirectory); - static bool DeleteEmptyDirectories(string rootDirectory) => - XPath.DeleteEmptyDirectories(rootDirectory); - - void TestStatic_ChangeDateForEmptyDirectories(string rootDirectory, long ticks) => - ChangeDateForEmptyDirectories(rootDirectory, ticks); - static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) => - XPath.ChangeDateForEmptyDirectories(rootDirectory, ticks); - - void TestStatic_MakeHiddenIfAllItemsAreHidden(string rootDirectory) => - MakeHiddenIfAllItemsAreHidden(rootDirectory); - static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) => - XPath.MakeHiddenIfAllItemsAreHidden(rootDirectory); - - void TestStatic_DeleteEmptyDirectories(string rootDirectory, List deletedDirectories) => - DeleteEmptyDirectories(rootDirectory, deletedDirectories); - static void DeleteEmptyDirectories(string rootDirectory, List deletedDirectories) => - XPath.DeleteEmptyDirectories(rootDirectory, deletedDirectories); - // $dirs = gci "" -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName $dirs | Foreach-Object { Remove-Item $_ } - - string[] TestStatic_GetDirectoryNames(string directory) => - GetDirectoryNames(directory); - static string[] GetDirectoryNames(string directory) => - XPath.GetDirectoryNames(directory).ToArray(); - - string[] TestStatic_GetDirectories(string directory) => - GetDirectories(directory); - static string[] GetDirectories(string directory) => - XPath.GetDirectories(directory).ToArray(); - - string TestStatic_GetRelativePath(string path, int length, bool forceExtensionToLower) => - GetRelativePath(path, length, forceExtensionToLower); - static string GetRelativePath(string path, int length, bool forceExtensionToLower) => - XPath.GetRelativePath(path, length, forceExtensionToLower); - - bool TestStatic_WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) => - WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches); - static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) => - XPath.WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches); - - (int level, List directories) TestStatic_Get(string rootDirectory, string sourceDirectory) => - Get(rootDirectory, sourceDirectory); - static (int level, List directories) Get(string rootDirectory, string sourceDirectory) => - XPath.Get(rootDirectory, sourceDirectory); - - string TestStatic_GetDirectory(string sourceDirectory, int level, string directoryName) => - GetDirectory(sourceDirectory, level, directoryName); - static string GetDirectory(string sourceDirectory, int level, string directoryName) => - XPath.GetDirectory(sourceDirectory, level, directoryName); - - CombinedEnumAndIndex TestStatic_GetCombinedEnumAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) => - GetCombinedEnumAndIndex(propertyConfiguration, filePath); - static CombinedEnumAndIndex GetCombinedEnumAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) => - XPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); - - ReadOnlyDictionary>> TestStatic_GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) => - GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups); - static ReadOnlyDictionary>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) => - XPath.GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups); - - byte TestStatic_GetEnum(FilePath filePath) => - GetEnum(filePath); - static byte GetEnum(FilePath filePath) => + public static byte GetEnum(FilePath filePath) => XPath.GetEnum(filePath); + public static string[] GetDirectories(string directory) => + XPath.GetDirectories(directory).ToArray(); + + public static string[] GetDirectoryNames(string directory) => + XPath.GetDirectoryNames(directory).ToArray(); + + public static string GetRelativePath(string path, int length) => + XPath.GetRelativePath(path, length, forceExtensionToLower: false); + + public static bool DeleteEmptyDirectories(string rootDirectory) => + XPath.DeleteEmptyDirectories(rootDirectory); + + public static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) => + XPath.MakeHiddenIfAllItemsAreHidden(rootDirectory); + + public static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) => + XPath.ChangeDateForEmptyDirectories(rootDirectory, ticks); + + public static string GetRelativePath(string path, int length, bool forceExtensionToLower) => + XPath.GetRelativePath(path, length, forceExtensionToLower); + + public static string GetDirectory(string sourceDirectory, int level, string directoryName) => + XPath.GetDirectory(sourceDirectory, level, directoryName); + + public static void DeleteEmptyDirectories(string rootDirectory, List deletedDirectories) => + XPath.DeleteEmptyDirectories(rootDirectory, deletedDirectories); + public static (int level, List directories) Get(string rootDirectory, string sourceDirectory) => + XPath.Get(rootDirectory, sourceDirectory); + + public static CombinedEnumAndIndex GetCombinedEnumAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) => + XPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); + + public static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) => + XPath.WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches); + + public static ReadOnlyDictionary>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) => + XPath.GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups); + + internal byte TestStatic_GetEnum(FilePath filePath) => + GetEnum(filePath); + + internal string[] TestStatic_GetDirectories(string directory) => + GetDirectories(directory); + + internal string[] TestStatic_GetDirectoryNames(string directory) => + GetDirectoryNames(directory); + + internal string TestStatic_GetRelativePath(string path, int length) => + GetRelativePath(path, length); + + internal bool TestStatic_DeleteEmptyDirectories(string rootDirectory) => + DeleteEmptyDirectories(rootDirectory); + + internal void TestStatic_MakeHiddenIfAllItemsAreHidden(string rootDirectory) => + MakeHiddenIfAllItemsAreHidden(rootDirectory); + + internal void TestStatic_ChangeDateForEmptyDirectories(string rootDirectory, long ticks) => + ChangeDateForEmptyDirectories(rootDirectory, ticks); + + internal string TestStatic_GetRelativePath(string path, int length, bool forceExtensionToLower) => + GetRelativePath(path, length, forceExtensionToLower); + + internal string TestStatic_GetDirectory(string sourceDirectory, int level, string directoryName) => + GetDirectory(sourceDirectory, level, directoryName); + + internal void TestStatic_DeleteEmptyDirectories(string rootDirectory, List deletedDirectories) => + DeleteEmptyDirectories(rootDirectory, deletedDirectories); + + internal (int level, List directories) TestStatic_Get(string rootDirectory, string sourceDirectory) => + Get(rootDirectory, sourceDirectory); + + internal CombinedEnumAndIndex TestStatic_GetCombinedEnumAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) => + GetCombinedEnumAndIndex(propertyConfiguration, filePath); + + internal bool TestStatic_WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) => + WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches); + + internal ReadOnlyDictionary>> TestStatic_GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) => + GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/Id.cs b/Shared/Models/Stateless/Methods/Id.cs index 28a901e..ed3b348 100644 --- a/Shared/Models/Stateless/Methods/Id.cs +++ b/Shared/Models/Stateless/Methods/Id.cs @@ -5,6 +5,51 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Id { + internal static int GetDeterministicHashCode(byte[] value) + { + int result; + unchecked + { + int hash1 = (5381 << 16) + 5381; + int hash2 = hash1; + for (int i = 0; i < value.Length; i += 2) + { + hash1 = ((hash1 << 5) + hash1) ^ value[i]; + if (i == value.Length - 1) + break; + hash2 = ((hash2 << 5) + hash2) ^ value[i + 1]; + } + result = hash1 + (hash2 * 1566083941); + } + return result; + } + + internal static byte GetHasIgnoreKeyword(FilePath filePath) => + (byte)(filePath.Id > -1 ? 8 : 2); + + internal static int GetId(Properties.IPropertyConfiguration propertyConfiguration, string intelligentId) + { + int result; + StringBuilder results = new(); + if (propertyConfiguration.IntMinValueLength < (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2)) + throw new NotSupportedException(); + for (int i = intelligentId.Length - (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2); i > -1; i--) + _ = results.Append(intelligentId[i]); + _ = results.Append(intelligentId[^3]).Append(intelligentId[^2]); + result = int.Parse(results.ToString()); + if (intelligentId[^1] is '1' or '2' or '3' or '4') + result *= -1; + else if (intelligentId[^1] is not '9' and not '8' and not '7' and not '6' and not '5') + throw new NotSupportedException(); + return result; + } + + internal static byte GetHasDateTimeOriginal(Properties.IPropertyConfiguration propertyConfiguration, FilePath filePath) => + (byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 9 : 1 : filePath.Id > -1 ? 6 : 4); + + internal static byte GetMissingDateTimeOriginal(Properties.IPropertyConfiguration propertyConfiguration, FilePath filePath) => + (byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 7 : 3 : 5); + internal static bool NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameWithoutExtension) { bool result; @@ -18,32 +63,6 @@ internal abstract class Id return result; } - internal static int GetId(Properties.IPropertyConfiguration propertyConfiguration, string intelligentId) - { - int result; - StringBuilder results = new(); - if (propertyConfiguration.IntMinValueLength < (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2)) - throw new NotSupportedException(); - for (int i = intelligentId.Length - (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2); i > -1; i--) - _ = results.Append(intelligentId[i]); - _ = results.Append(intelligentId[^3]).Append(intelligentId[^2]); - result = int.Parse(results.ToString()); - if (intelligentId[^1] is '1' or '2' or '3') - result *= -1; - else if (intelligentId[^1] is not '9' and not '8' and not '7') - throw new NotSupportedException(); - return result; - } - - internal static int GetHasIgnoreKeyword(FilePath filePath) => - filePath.Id > -1 ? 8 : 2; - - internal static int GetMissingDateTimeOriginal(FilePath filePath) => - filePath.Id > -1 ? 7 : 3; - - internal static int GetHasDateTimeOriginal(FilePath filePath) => - filePath.Id > -1 ? 9 : 1; - internal static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) { string result; @@ -89,23 +108,4 @@ internal abstract class Id return result; } - internal static int GetDeterministicHashCode(byte[] value) - { - int result; - unchecked - { - int hash1 = (5381 << 16) + 5381; - int hash2 = hash1; - for (int i = 0; i < value.Length; i += 2) - { - hash1 = ((hash1 << 5) + hash1) ^ value[i]; - if (i == value.Length - 1) - break; - hash2 = ((hash2 << 5) + hash2) ^ value[i + 1]; - } - result = hash1 + (hash2 * 1566083941); - } - return result; - } - } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/XDirectory.cs b/Shared/Models/Stateless/Methods/XDirectory.cs index fd568ec..b0859fc 100644 --- a/Shared/Models/Stateless/Methods/XDirectory.cs +++ b/Shared/Models/Stateless/Methods/XDirectory.cs @@ -5,31 +5,119 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract partial class XDirectory { - private static int GetCeilingAverage(List fileCollection) + internal static void MoveFiles(List files, string find, string replace) { - List counts = []; - foreach (string[] files in fileCollection) - counts.Add(files.Length); - int average = (int)Math.Ceiling(counts.Average()); - return average; + string checkFile; + string? checkDirectory; + List directories = []; + foreach (string file in files) + { + checkDirectory = Path.GetDirectoryName(file.Replace(find, replace)); + if (string.IsNullOrEmpty(checkDirectory) || directories.Contains(checkDirectory)) + continue; + directories.Add(checkDirectory); + } + foreach (string directory in directories) + { + if (Directory.Exists(directory)) + continue; + _ = Directory.CreateDirectory(directory); + } + foreach (string file in files) + { + if (!File.Exists(file)) + continue; + checkFile = file.Replace(find, replace); + if (File.Exists(checkFile)) + { + File.Delete(checkFile); + continue; + } + File.Move(file, checkFile); + } } - private static List GetFilesCollection(List fileCollection, int ceilingAverage) + internal static List CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) { - List results = []; - foreach (string[] files in fileCollection) + List results = []; + FileInfo fileInfo; + List distinctExtensions = []; + foreach ((FilePath filePath, string to) in toDoCollection) { - if (files.Length < ceilingAverage) - results.Add(files); - } - foreach (string[] files in fileCollection) - { - if (files.Length >= ceilingAverage) - results.Add(files); + tick?.Invoke(); + fileInfo = new(to); + if (fileInfo.Exists) + { + if (filePath.Length == fileInfo.Length && filePath.LastWriteTicks == fileInfo.LastWriteTime.Ticks) + continue; + fileInfo.Delete(); + } + results.Add(filePath.NameWithoutExtension); + try + { + if (!distinctExtensions.Contains(filePath.ExtensionLowered)) + distinctExtensions.Add(filePath.ExtensionLowered); + if (move || moveBack) + File.Move(filePath.FullName, to); + else + File.Copy(filePath.FullName, to); + } + catch (Exception) { } } return results; } + internal static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) + { + FileInfo? toFileInfo; + FileInfo fromFileInfo; + string checkDirectory; + List<(string, string)> rename = []; + foreach (FilePair filePair in filePairs) + { + if (filePair.IsUnique) + continue; + IsNotUniqueLoop(propertyConfiguration, jsonGroupDirectory, extension, filePair, rename); + } + foreach ((string from, string to) in rename) + { + toFileInfo = null; + checkDirectory = to; + fromFileInfo = new(from); + if (!fromFileInfo.Exists) + continue; + for (int i = 0; i < int.MaxValue; i++) + { + toFileInfo = new(checkDirectory); + if (toFileInfo.Directory is null) + continue; + if (!toFileInfo.Directory.Exists) + _ = Directory.CreateDirectory(toFileInfo.Directory.FullName); + if (checkDirectory.Length > 199) + throw new Exception(); + if (!toFileInfo.Exists) + break; + else if (fromFileInfo.Length == toFileInfo.Length && fromFileInfo.LastWriteTime == toFileInfo.LastWriteTime) + checkDirectory = string.Concat(checkDirectory, ".del"); + else + checkDirectory = string.Concat(checkDirectory, ".j"); + } + File.Move(from, checkDirectory); + } + return rename.Count; + } + + private static void IsNotUniqueLoop(Properties.IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, FilePair filePair, List<(string, string)> rename) + { + int length = propertyConfiguration.RootDirectory.Length; + foreach (string path in filePair.Collection) + { + if (filePair.Match is null || path != filePair.Match) + continue; + rename.Add(new(path, string.Concat(jsonGroupDirectory, filePair.Path[length..], extension))); + } + } + internal static ReadOnlyCollection GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) { List results = []; @@ -60,6 +148,53 @@ internal abstract partial class XDirectory return results.AsReadOnly(); } + private static int GetCeilingAverage(List fileCollection) + { + List counts = []; + foreach (string[] files in fileCollection) + counts.Add(files.Length); + int average = (int)Math.Ceiling(counts.Average()); + return average; + } + + private static List GetFilesCollection(List fileCollection, int ceilingAverage) + { + List results = []; + foreach (string[] files in fileCollection) + { + if (files.Length < ceilingAverage) + results.Add(files); + } + foreach (string[] files in fileCollection) + { + if (files.Length >= ceilingAverage) + results.Add(files); + } + return results; + } + + internal static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) + { + List> results = []; + FilePath filePath; + List filePaths; + Models.FileHolder fileHolder; + foreach (string[] files in filesCollection) + { + filePaths = []; + foreach (string file in files) + { + fileHolder = IFileHolder.Get(file); + if (propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) + continue; + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + filePaths.Add(filePath); + } + results.Add(new(filePaths)); + } + return results.AsReadOnly(); + } + internal static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) { ReadOnlyCollection> results; @@ -68,48 +203,6 @@ internal abstract partial class XDirectory return results; } - private static bool GetIsNotUniqueAndNeedsReview(string file, List collection) - { - bool result = false; - FileInfo possibleFileInfo; - FileInfo fileInfo = new(file); - foreach (string possible in collection) - { - if (possible == file) - continue; - possibleFileInfo = new(possible); - if (possibleFileInfo.LastWriteTime != fileInfo.LastWriteTime) - File.SetLastWriteTime(file, new DateTime[] { possibleFileInfo.LastWriteTime, fileInfo.LastWriteTime }.Max()); - if (possibleFileInfo.LastWriteTime == fileInfo.LastWriteTime && possibleFileInfo.Length == fileInfo.Length) - continue; - if (!result) - result = true; - } - return result; - } - - private static string? GetMatch(string file, List collection) - { - string? result = null; - FileInfo possibleFileInfo; - List lengths = []; - List matches = []; - FileInfo fileInfo = new(file); - List creationTimes = []; - foreach (string possible in collection) - { - possibleFileInfo = new(possible); - lengths.Add(possibleFileInfo.Length); - creationTimes.Add(possibleFileInfo.CreationTime); - if (possibleFileInfo.CreationTime != fileInfo.LastWriteTime) - continue; - matches.Add(possible); - } - if (matches.Count == 1 || (matches.Count > 0 && lengths.Distinct().Count() == 1 && creationTimes.Distinct().Count() == 1)) - result = matches.First(); - return result; - } - internal static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) { List results = []; @@ -168,126 +261,51 @@ internal abstract partial class XDirectory return results; } - private static void IsNotUniqueLoop(Properties.IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, FilePair filePair, List<(string, string)> rename) + private static bool GetIsNotUniqueAndNeedsReview(string file, List collection) { - int length = propertyConfiguration.RootDirectory.Length; - foreach (string path in filePair.Collection) + bool result = false; + FileInfo possibleFileInfo; + FileInfo fileInfo = new(file); + foreach (string possible in collection) { - if (filePair.Match is null || path != filePair.Match) + if (possible == file) continue; - rename.Add(new(path, string.Concat(jsonGroupDirectory, filePair.Path[length..], extension))); + possibleFileInfo = new(possible); + if (possibleFileInfo.LastWriteTime != fileInfo.LastWriteTime) + File.SetLastWriteTime(file, new DateTime[] { possibleFileInfo.LastWriteTime, fileInfo.LastWriteTime }.Max()); + if (possibleFileInfo.LastWriteTime == fileInfo.LastWriteTime && possibleFileInfo.Length == fileInfo.Length) + continue; + if (!result) + result = true; } + return result; } - internal static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) + private static string? GetMatch(string file, List collection) { - FileInfo? toFileInfo; - FileInfo fromFileInfo; - string checkDirectory; - List<(string, string)> rename = []; - foreach (FilePair filePair in filePairs) + string? result = null; + FileInfo possibleFileInfo; + List lengths = []; + List matches = []; + FileInfo fileInfo = new(file); + List creationTimes = []; + foreach (string possible in collection) { - if (filePair.IsUnique) + possibleFileInfo = new(possible); + lengths.Add(possibleFileInfo.Length); + creationTimes.Add(possibleFileInfo.CreationTime); + if (possibleFileInfo.CreationTime != fileInfo.LastWriteTime) continue; - IsNotUniqueLoop(propertyConfiguration, jsonGroupDirectory, extension, filePair, rename); + matches.Add(possible); } - foreach ((string from, string to) in rename) - { - toFileInfo = null; - checkDirectory = to; - fromFileInfo = new(from); - if (!fromFileInfo.Exists) - continue; - for (int i = 0; i < int.MaxValue; i++) - { - toFileInfo = new(checkDirectory); - if (toFileInfo.Directory is null) - continue; - if (!toFileInfo.Directory.Exists) - _ = Directory.CreateDirectory(toFileInfo.Directory.FullName); - if (checkDirectory.Length > 199) - throw new Exception(); - if (!toFileInfo.Exists) - break; - else if (fromFileInfo.Length == toFileInfo.Length && fromFileInfo.LastWriteTime == toFileInfo.LastWriteTime) - checkDirectory = string.Concat(checkDirectory, ".del"); - else - checkDirectory = string.Concat(checkDirectory, ".j"); - } - File.Move(from, checkDirectory); - } - return rename.Count; - } - - internal static void MoveFiles(List files, string find, string replace) - { - string checkFile; - string? checkDirectory; - List directories = []; - foreach (string file in files) - { - checkDirectory = Path.GetDirectoryName(file.Replace(find, replace)); - if (string.IsNullOrEmpty(checkDirectory) || directories.Contains(checkDirectory)) - continue; - directories.Add(checkDirectory); - } - foreach (string directory in directories) - { - if (Directory.Exists(directory)) - continue; - _ = Directory.CreateDirectory(directory); - } - foreach (string file in files) - { - if (!File.Exists(file)) - continue; - checkFile = file.Replace(find, replace); - if (File.Exists(checkFile)) - { - File.Delete(checkFile); - continue; - } - File.Move(file, checkFile); - } - } - - private static FilePath[] GetSortedRecords(ReadOnlyCollection> filePathsCollection) - { - List results = []; - foreach (ReadOnlyCollection filePaths in filePathsCollection) - { - foreach (FilePath filePath in filePaths) - results.Add(filePath); - } - return (from l in results orderby l.CreationTicks, l.FullName.Length descending select l).ToArray(); - } - - internal static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) - { - List> results = []; - FilePath filePath; - List filePaths; - Models.FileHolder fileHolder; - foreach (string[] files in filesCollection) - { - filePaths = []; - foreach (string file in files) - { - fileHolder = IFileHolder.Get(file); - if (propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) - continue; - filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); - filePaths.Add(filePath); - } - results.Add(new(filePaths)); - } - return results.AsReadOnly(); + if (matches.Count == 1 || (matches.Count > 0 && lengths.Distinct().Count() == 1 && creationTimes.Distinct().Count() == 1)) + result = matches.First(); + return result; } internal static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary>> fileGroups, Action? tick) { List<(FilePath, string)> results = []; - string key; string paddedId; string checkFile; string directory; @@ -305,6 +323,8 @@ internal abstract partial class XDirectory List distinctDirectories = []; FilePath[] sortedRecords = GetSortedRecords(filePathsCollection); ReadOnlyDictionary>? keyValuePairs; + if (!fileGroups.TryGetValue(propertyConfiguration.ResultContent, out keyValuePairs)) + throw new NotImplementedException(); bool isOffsetDeterministicHashCode = IId.IsOffsetDeterministicHashCode(propertyConfiguration); for (int i = 0; i < sortedRecords.Length; i++) { @@ -312,11 +332,8 @@ internal abstract partial class XDirectory filePath = sortedRecords[i]; if (filePath.Name.EndsWith("len") || filePath.ExtensionLowered == ".id" || filePath.ExtensionLowered == ".lsv" || filePath.DirectoryFullPath is null) continue; - key = propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? propertyConfiguration.ResultContentCollection : propertyConfiguration.ResultContent; - if (!fileGroups.TryGetValue(key, out keyValuePairs)) - continue; - cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); fileDirectoryName = Path.GetFileName(filePath.DirectoryFullPath); + cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); if (fileDirectoryName.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength + 3 || !filePath.Name.StartsWith(fileDirectoryName)) { if (wrapped) @@ -409,34 +426,15 @@ internal abstract partial class XDirectory return (distinctDirectories.ToArray(), results); } - internal static List CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) + private static FilePath[] GetSortedRecords(ReadOnlyCollection> filePathsCollection) { - List results = []; - FileInfo fileInfo; - List distinctExtensions = []; - foreach ((FilePath filePath, string to) in toDoCollection) + List results = []; + foreach (ReadOnlyCollection filePaths in filePathsCollection) { - tick?.Invoke(); - fileInfo = new(to); - if (fileInfo.Exists) - { - if (filePath.Length == fileInfo.Length && filePath.LastWriteTicks == fileInfo.LastWriteTime.Ticks) - continue; - fileInfo.Delete(); - } - results.Add(filePath.NameWithoutExtension); - try - { - if (!distinctExtensions.Contains(filePath.ExtensionLowered)) - distinctExtensions.Add(filePath.ExtensionLowered); - if (move || moveBack) - File.Move(filePath.FullName, to); - else - File.Copy(filePath.FullName, to); - } - catch (Exception) { } + foreach (FilePath filePath in filePaths) + results.Add(filePath); } - return results; + return (from l in results orderby l.CreationTicks, l.FullName.Length descending select l).ToArray(); } } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/XPath.cs b/Shared/Models/Stateless/Methods/XPath.cs index 5fa0101..84fad36 100644 --- a/Shared/Models/Stateless/Methods/XPath.cs +++ b/Shared/Models/Stateless/Methods/XPath.cs @@ -6,35 +6,21 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class XPath { - internal static string GetRelativePath(string path, int length, bool forceExtensionToLower) + private static ReadOnlyDictionary> Convert(List collection) { - string result; - if (forceExtensionToLower) + Dictionary> results = []; + List? c; + foreach (CombinedEnumAndIndex cei in collection) { - string extension = Path.GetExtension(path); - string extensionLowered = Path.GetExtension(path).ToLower(); - if (extension != extensionLowered) + if (!results.TryGetValue(cei.Enum, out c)) { - string? directoryName = Path.GetDirectoryName(path); - if (string.IsNullOrEmpty(directoryName)) - throw new NullReferenceException(directoryName); - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); - if (string.IsNullOrEmpty(fileNameWithoutExtension)) - throw new NullReferenceException(fileNameWithoutExtension); - path = Path.Combine(directoryName, $"{fileNameWithoutExtension}{extensionLowered}"); + results.Add(cei.Enum, []); + if (!results.TryGetValue(cei.Enum, out c)) + throw new Exception(); } + c.Add(cei.Combined); } - result = path[length..].Replace(@"\", "/"); - return result; - } - - internal static bool DeleteEmptyDirectories(string rootDirectory) - { - bool result; - List results = []; - DeleteEmptyDirectories(rootDirectory, results); - result = results.Count > 0; - return result; + return Convert(results); } internal static void DeleteEmptyDirectories(string rootDirectory, List deletedDirectories) @@ -72,45 +58,79 @@ internal abstract class XPath } } - internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches) + internal static CombinedEnumAndIndex GetCombinedEnumAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath, string fileNameFirstSegment) { - bool result; - string text; - if (!compareBeforeWrite) - result = true; + CombinedEnumAndIndex result; + int converted; + string combined; + byte @enum = GetEnum(filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal); + string check = fileNameFirstSegment.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength ? + new('-', propertyConfiguration.ResultAllInOneSubdirectoryLength) : + fileNameFirstSegment.Split('.')[0][^propertyConfiguration.ResultAllInOneSubdirectoryLength..]; + if (check.Any(l => !char.IsNumber(l))) + { + combined = $"{@enum}{new('-', propertyConfiguration.ResultAllInOneSubdirectoryLength)}"; + converted = int.Parse($"1{new string('0', propertyConfiguration.ResultAllInOneSubdirectoryLength)}"); + } else { - if (!File.Exists(path)) - text = string.Empty; - else - text = File.ReadAllText(path); - result = text != contents; - if (!result && updateDateWhenMatches) - { - if (updateToWhenMatches is null) - File.SetLastWriteTime(path, DateTime.Now); - else - File.SetLastWriteTime(path, updateToWhenMatches.Value); - } - } - if (result) - { - if (path.Contains("()")) - File.WriteAllText(path, contents); - else if (path.Contains("{}") && !path.EndsWith(".json")) - File.WriteAllText(path, contents); - else if (path.Contains("[]") && !path.EndsWith(".json")) - File.WriteAllText(path, contents); - else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{') - File.WriteAllText(path, contents); - else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[') - File.WriteAllText(path, contents); - else - File.WriteAllText(path, contents); + combined = $"{@enum}{check}"; + converted = int.Parse(check); } + result = new(combined, @enum, converted); return result; } + private static byte GetEnum(bool? ik, bool? dto) + { + byte result; + if (ik is not null && ik.Value && dto is not null && dto.Value) + result = 11; + else if (ik is not null && ik.Value && dto is not null && !dto.Value) + result = 15; + else if (ik is not null && ik.Value && dto is null) + result = 19; + else if (ik is not null && !ik.Value && dto is not null && dto.Value) + result = 51; + else if (ik is not null && !ik.Value && dto is not null && !dto.Value) + result = 55; + else if (ik is not null && !ik.Value && dto is null) + result = 59; + else if (ik is null && dto is not null && dto.Value) + result = 91; + else if (ik is null && dto is not null && !dto.Value) + result = 95; + else if (ik is null && dto is null) + result = 99; + else + throw new Exception(); + return result; + } + + internal static byte GetEnum(FilePath filePath) => + GetEnum(filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal); + + internal static List GetDirectories(string directory) + { + List results = []; + string? checkDirectory = directory; + string? pathRoot = Path.GetPathRoot(directory); + if (string.IsNullOrEmpty(pathRoot)) + throw new NullReferenceException(nameof(pathRoot)); + if (Directory.Exists(directory)) + results.Add(directory); + for (int i = 0; i < int.MaxValue; i++) + { + checkDirectory = Path.GetDirectoryName(checkDirectory); + if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot) + break; + results.Add(checkDirectory); + } + results.Add(pathRoot); + results.Reverse(); + return results; + } + internal static List GetDirectoryNames(string directory) { List results = []; @@ -147,83 +167,15 @@ internal abstract class XPath return results; } - internal static List GetDirectories(string directory) + internal static bool DeleteEmptyDirectories(string rootDirectory) { + bool result; List results = []; - string? checkDirectory = directory; - string? pathRoot = Path.GetPathRoot(directory); - if (string.IsNullOrEmpty(pathRoot)) - throw new NullReferenceException(nameof(pathRoot)); - if (Directory.Exists(directory)) - results.Add(directory); - for (int i = 0; i < int.MaxValue; i++) - { - checkDirectory = Path.GetDirectoryName(checkDirectory); - if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot) - break; - results.Add(checkDirectory); - } - results.Add(pathRoot); - results.Reverse(); - return results; - } - - internal static (int level, List directories) Get(string rootDirectory, string sourceDirectory) - { - int result = 0; - string? directory; - string? checkDirectory; - List results = []; - checkDirectory = sourceDirectory; - for (int i = 0; i < int.MaxValue; i++) - { - result += 1; - directory = Path.GetFileName(checkDirectory); - if (string.IsNullOrEmpty(directory)) - break; - results.Add(directory); - checkDirectory = Path.GetDirectoryName(checkDirectory); - if (checkDirectory == rootDirectory) - break; - } - results.Reverse(); - return new(result, results); - } - - internal static string GetDirectory(string sourceDirectory, int level, string directoryName) - { - string result; - string? checkDirectory; - checkDirectory = Path.GetDirectoryName(sourceDirectory); - for (int i = 0; i < level; i++) - checkDirectory = Path.GetDirectoryName(checkDirectory); - if (string.IsNullOrEmpty(checkDirectory)) - throw new Exception(); - checkDirectory = Path.Combine(checkDirectory, directoryName); - if (!Directory.Exists(checkDirectory)) - _ = Directory.CreateDirectory(checkDirectory); - result = checkDirectory; + DeleteEmptyDirectories(rootDirectory, results); + result = results.Count > 0; return result; } - internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) - { - DateTime dateTime = new(ticks); - IEnumerable fileSystemEntries; - string[] directories; - if (!Directory.Exists(rootDirectory)) - directories = []; - else - directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.AllDirectories); - foreach (string directory in directories) - { - fileSystemEntries = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.TopDirectoryOnly); - if (fileSystemEntries.Any()) - continue; - Directory.SetLastWriteTime(directory, dateTime); - } - } - internal static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) { bool check; @@ -266,104 +218,131 @@ internal abstract class XPath } } - private static byte GetEnum(bool? ik, bool? dto) + internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) { - byte result; - if (ik is not null && ik.Value && dto is not null && dto.Value) - result = 11; - else if (ik is not null && ik.Value && dto is not null && !dto.Value) - result = 15; - else if (ik is not null && ik.Value && dto is null) - result = 19; - else if (ik is not null && !ik.Value && dto is not null && dto.Value) - result = 51; - else if (ik is not null && !ik.Value && dto is not null && !dto.Value) - result = 55; - else if (ik is not null && !ik.Value && dto is null) - result = 59; - else if (ik is null && dto is not null && dto.Value) - result = 91; - else if (ik is null && dto is not null && !dto.Value) - result = 95; - else if (ik is null && dto is null) - result = 99; + DateTime dateTime = new(ticks); + IEnumerable fileSystemEntries; + string[] directories; + if (!Directory.Exists(rootDirectory)) + directories = []; else - throw new Exception(); + directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.AllDirectories); + foreach (string directory in directories) + { + fileSystemEntries = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.TopDirectoryOnly); + if (fileSystemEntries.Any()) + continue; + Directory.SetLastWriteTime(directory, dateTime); + } + } + + internal static string GetRelativePath(string path, int length, bool forceExtensionToLower) + { + string result; + if (forceExtensionToLower) + { + string extension = Path.GetExtension(path); + string extensionLowered = Path.GetExtension(path).ToLower(); + if (extension != extensionLowered) + { + string? directoryName = Path.GetDirectoryName(path); + if (string.IsNullOrEmpty(directoryName)) + throw new NullReferenceException(directoryName); + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); + if (string.IsNullOrEmpty(fileNameWithoutExtension)) + throw new NullReferenceException(fileNameWithoutExtension); + path = Path.Combine(directoryName, $"{fileNameWithoutExtension}{extensionLowered}"); + } + } + result = path[length..].Replace(@"\", "/"); return result; } - internal static byte GetEnum(FilePath filePath) => - GetEnum(filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal); - - internal static CombinedEnumAndIndex GetCombinedEnumAndIndex(int resultAllInOneSubdirectoryLength, FilePath filePath, string fileName) + internal static string GetDirectory(string sourceDirectory, int level, string directoryName) { - CombinedEnumAndIndex result; - int converted; - string combined; - byte @enum = GetEnum(filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal); - string check = fileName.Length < resultAllInOneSubdirectoryLength ? - new('-', resultAllInOneSubdirectoryLength) : - fileName.Split('.')[0][^resultAllInOneSubdirectoryLength..]; - if (check.Any(l => !char.IsNumber(l))) - { - combined = $"{@enum}{new('-', resultAllInOneSubdirectoryLength)}"; - converted = int.Parse($"1{new string('0', resultAllInOneSubdirectoryLength)}"); - } - else - { - combined = $"{@enum}{check}"; - converted = int.Parse(check); - } - result = new(combined, @enum, converted); + string result; + string? checkDirectory; + checkDirectory = Path.GetDirectoryName(sourceDirectory); + for (int i = 0; i < level; i++) + checkDirectory = Path.GetDirectoryName(checkDirectory); + if (string.IsNullOrEmpty(checkDirectory)) + throw new Exception(); + checkDirectory = Path.Combine(checkDirectory, directoryName); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + result = checkDirectory; return result; } + internal static (int level, List directories) Get(string rootDirectory, string sourceDirectory) + { + int result = 0; + string? directory; + string? checkDirectory; + List results = []; + checkDirectory = sourceDirectory; + for (int i = 0; i < int.MaxValue; i++) + { + result += 1; + directory = Path.GetFileName(checkDirectory); + if (string.IsNullOrEmpty(directory)) + break; + results.Add(directory); + checkDirectory = Path.GetDirectoryName(checkDirectory); + if (checkDirectory == rootDirectory) + break; + } + results.Reverse(); + return new(result, results); + } + internal static CombinedEnumAndIndex GetCombinedEnumAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) { CombinedEnumAndIndex result; if (filePath.Id is not null) - result = GetCombinedEnumAndIndex(propertyConfiguration.ResultAllInOneSubdirectoryLength, filePath, filePath.Id.Value.ToString()); + result = GetCombinedEnumAndIndex(propertyConfiguration, filePath, filePath.Id.Value.ToString()); else - result = GetCombinedEnumAndIndex(propertyConfiguration.ResultAllInOneSubdirectoryLength, filePath, filePath.FileNameFirstSegment); + result = GetCombinedEnumAndIndex(propertyConfiguration, filePath, filePath.FileNameFirstSegment); return result; } - private static byte[] GetBytes() => - [ - 11, - 15, - 19, - 51, - 55, - 59, - 91, - 95, - 99 - ]; - - private static ReadOnlyDictionary> Convert(Dictionary> keyValuePairs) + internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches) { - Dictionary> results = []; - foreach (KeyValuePair> keyValuePair in keyValuePairs) - results.Add(keyValuePair.Key, new(keyValuePair.Value)); - return results.AsReadOnly(); - } - - private static ReadOnlyDictionary> Convert(List collection) - { - Dictionary> results = []; - List? c; - foreach (CombinedEnumAndIndex cei in collection) + bool result; + string text; + if (!compareBeforeWrite) + result = true; + else { - if (!results.TryGetValue(cei.Enum, out c)) + if (!File.Exists(path)) + text = string.Empty; + else + text = File.ReadAllText(path); + result = text != contents; + if (!result && updateDateWhenMatches) { - results.Add(cei.Enum, []); - if (!results.TryGetValue(cei.Enum, out c)) - throw new Exception(); + if (updateToWhenMatches is null) + File.SetLastWriteTime(path, DateTime.Now); + else + File.SetLastWriteTime(path, updateToWhenMatches.Value); } - c.Add(cei.Combined); } - return Convert(results); + if (result) + { + if (path.Contains("()")) + File.WriteAllText(path, contents); + else if (path.Contains("{}") && !path.EndsWith(".json")) + File.WriteAllText(path, contents); + else if (path.Contains("[]") && !path.EndsWith(".json")) + File.WriteAllText(path, contents); + else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{') + File.WriteAllText(path, contents); + else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[') + File.WriteAllText(path, contents); + else + File.WriteAllText(path, contents); + } + return result; } internal static ReadOnlyDictionary>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) @@ -420,4 +399,25 @@ internal abstract class XPath return results.AsReadOnly(); } + private static byte[] GetBytes() => + [ + 11, + 15, + 19, + 51, + 55, + 59, + 91, + 95, + 99 + ]; + + private static ReadOnlyDictionary> Convert(Dictionary> keyValuePairs) + { + Dictionary> results = []; + foreach (KeyValuePair> keyValuePair in keyValuePairs) + results.Add(keyValuePair.Key, new(keyValuePair.Value)); + return results.AsReadOnly(); + } + } \ No newline at end of file