From c7ded16e509f83749e9a3294ef5da5cf16dbcf94 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sun, 6 Apr 2025 18:23:57 -0700 Subject: [PATCH] Switch to ExifDirectory from Property --- .gitignore | 3 + .vscode/mklink.md | 11 + .vscode/tasks.json | 48 ++ Container/Container.csproj | 1 + .../Models/Stateless/Methods/Container.cs | 632 +++++++----------- .../Models/Stateless/Methods/IContainer.cs | 8 +- Copy-Distinct/CopyDistinct.cs | 148 ++-- Date-Group/DateGroup.cs | 568 ++++++++-------- Drag-Drop-Search/DragDropSearch.cs | 114 ++-- Duplicate-Search/DuplicateSearch.cs | 278 ++++---- Face/Models/_D_Face.cs | 10 +- Instance/DlibDotNet.cs | 563 +++++++++------- Instance/Instance.csproj | 1 - Map/Models/MapLogic.cs | 4 +- Map/Models/Stateless/MapLogic.cs | 2 +- Metadata/Models/B_Metadata.cs | 137 ++-- Property/Models/A_Property.cs | 13 +- Property/Models/Stateless/Property.cs | 16 +- Resize/Models/_C_Resize.cs | 37 +- Shared/Models/C_Resize.cs | 34 + Shared/Models/D2_FaceParts.cs | 24 + Shared/Models/D_Face.cs | 24 + Shared/Models/Face.cs | 20 +- Shared/Models/FilePair.cs | 10 +- Shared/Models/FilePath.cs | 34 +- Shared/Models/Item.cs | 26 +- Shared/Models/MappingFromItem.cs | 18 +- Shared/Models/Methods/IMetadataFile.cs | 8 - Shared/Models/Properties/IItem.cs | 2 +- Shared/Models/Stateless/Methods/FilePair.cs | 151 +++++ Shared/Models/Stateless/Methods/IDate.cs | 38 ++ Shared/Models/Stateless/Methods/IDirectory.cs | 50 +- .../Models/Stateless/Methods/IDlibDotNet.cs | 1 + Shared/Models/Stateless/Methods/IFilePair.cs | 21 + Shared/Models/Stateless/Methods/IId.cs | 24 +- .../Stateless/Methods/IMappingFromItem.cs | 16 +- Shared/Models/Stateless/Methods/IMetaBase.cs | 32 + .../Models/Stateless/Methods/IMetadataFile.cs | 8 - Shared/Models/Stateless/Methods/IPath.cs | 6 + Shared/Models/Stateless/Methods/Id.cs | 29 +- Shared/Models/Stateless/Methods/MetaBase.cs | 83 +++ Shared/Models/Stateless/Methods/XDate.cs | 252 +++++++ Shared/Models/Stateless/Methods/XDirectory.cs | 231 ++++--- Shared/Models/Stateless/Methods/XPath.cs | 32 +- Tests/Tests.csproj | 1 - Tests/UnitTestHardCoded.cs | 291 ++++---- Tests/UnitTestResize.cs | 162 +++-- .../TestsWithFaceRecognitionDotNet.csproj | 5 +- .../UnitTestFace.cs | 254 ++++--- View-by-Distance-MKLink-Console.sln | 12 - 50 files changed, 2647 insertions(+), 1846 deletions(-) create mode 100644 Shared/Models/C_Resize.cs create mode 100644 Shared/Models/D2_FaceParts.cs create mode 100644 Shared/Models/D_Face.cs create mode 100644 Shared/Models/Stateless/Methods/FilePair.cs create mode 100644 Shared/Models/Stateless/Methods/IDate.cs create mode 100644 Shared/Models/Stateless/Methods/IFilePair.cs create mode 100644 Shared/Models/Stateless/Methods/IMetaBase.cs create mode 100644 Shared/Models/Stateless/Methods/MetaBase.cs create mode 100644 Shared/Models/Stateless/Methods/XDate.cs diff --git a/.gitignore b/.gitignore index ae27aae..6588c57 100644 --- a/.gitignore +++ b/.gitignore @@ -470,4 +470,7 @@ globalStorage/ Shared/.kanbn .Immich/immich-assets.json +Tests/.vscode/.UserSecrets/* Instance/.vscode/.UserSecrets/* +Drag-Drop-Set-Property-Item/.vscode/.UserSecrets/* +TestsWithFaceRecognitionDotNet/.vscode/.UserSecrets/* diff --git a/.vscode/mklink.md b/.vscode/mklink.md index c086128..150885e 100644 --- a/.vscode/mklink.md +++ b/.vscode/mklink.md @@ -18,3 +18,14 @@ mklink /J "L:\Git\View-by-Distance-MKLink-Console\.Immich" "D:\1-Images-A\Images mklink /J "V:\Tmp\Phares\Pictures-Results" "V:\6-Other-Large-Z\Current-Results-Test" mklink /J "V:\1-Images-A\Images-0b793904-Results" "V:\6-Other-Large-Z\Current-Results" ``` + +```bash 1742827407172 = 638784242071720000 = 2025-1.Spring = Mon Mar 24 2025 07:43:26 GMT-0700 (Mountain Standard Time) +mkdir "L:\Git\View-by-Distance-MKLink-Console\Instance\.vscode" +mklink /J "L:\Git\View-by-Distance-MKLink-Console\Instance\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\2999dda1-5329-4d9f-9d68-cccfabe0e47f" +mkdir "L:\Git\View-by-Distance-MKLink-Console\Tests\.vscode" +mklink /J "L:\Git\View-by-Distance-MKLink-Console\Tests\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\e8c3d25d-9715-4b35-9010-1cdc74840190" +mkdir "L:\Git\View-by-Distance-MKLink-Console\TestsWithFaceRecognitionDotNet\.vscode" +mklink /J "L:\Git\View-by-Distance-MKLink-Console\TestsWithFaceRecognitionDotNet\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\ecbdc76d-6037-4046-86a4-1a7626a3d342" +mkdir "L:\Git\View-by-Distance-MKLink-Console\Drag-Drop-Set-Property-Item\.vscode" +mklink /J "L:\Git\View-by-Distance-MKLink-Console\Drag-Drop-Set-Property-Item\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\c64a15ed-0ba3-4378-8f80-0c19d0531747" +``` diff --git a/.vscode/tasks.json b/.vscode/tasks.json index cc276e3..0b85cfb 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -100,6 +100,54 @@ ], "problemMatcher": "$msCompile" }, + { + "label": "buildTests", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Tests/Tests.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "buildTestsWithFaceRecognitionDotNet", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "buildCopyDistinct", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Copy-Distinct/Copy-Distinct.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "buildDragDropSetPropertyItem", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Drag-Drop-Set-Property-Item/Drag-Drop-Set-Property-Item.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, { "label": "File-Folder-Helper AOT s X Day-Helper-2025-03-20", "type": "shell", diff --git a/Container/Container.csproj b/Container/Container.csproj index bc45f6b..ddd1afe 100644 --- a/Container/Container.csproj +++ b/Container/Container.csproj @@ -47,5 +47,6 @@ + \ No newline at end of file diff --git a/Container/Models/Stateless/Methods/Container.cs b/Container/Models/Stateless/Methods/Container.cs index 8fae951..e3a2e6d 100644 --- a/Container/Models/Stateless/Methods/Container.cs +++ b/Container/Models/Stateless/Methods/Container.cs @@ -1,5 +1,4 @@ using System.Collections.ObjectModel; -using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless.Methods; @@ -10,7 +9,7 @@ internal abstract class Container { private record FilePair(bool IsUnique, - List Collection, + List Collection, FilePath FilePath, Item Item); @@ -48,11 +47,11 @@ internal abstract class Container continue; foreach (Item item in filteredItems) { - if (item.Property?.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) continue; - if (results.Contains(item.Property.Id.Value)) + if (results.Contains(item.ExifDirectory.FilePath.Id.Value)) continue; - results.Add(item.Property.Id.Value); + results.Add(item.ExifDirectory.FilePath.Id.Value); } } return results; @@ -62,21 +61,23 @@ internal abstract class Container { int count; Models.Container[] results; + bool useIgnoreExtensions = true; const bool useCeilingAverage = true; const string fileSearchFilter = "*"; IDlibDotNet dlibDotNet = GetDlibDotNet(); const string directorySearchFilter = "*"; + ReadOnlyDictionary? exifDirectoriesById = null; 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); + ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions); + (count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, 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) + private static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary? exifDirectoriesById, string directorySearchFilter) { List results = []; string directory; @@ -104,7 +105,7 @@ internal abstract class Container 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); + List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter); foreach (FilePair filePair in filePairs) { if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) @@ -125,463 +126,112 @@ internal abstract class 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 = []; - string fileNameWithoutExtension; - string fileNameWithoutExtensionSecond; - string fileNameWithoutExtensionSecondMinusOne; - List? collection; - foreach (string[] files in filesCollection) - { - foreach (string file in files) - { - fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(fileNameWithoutExtension); - if (string.IsNullOrEmpty(fileNameWithoutExtensionSecond) || fileNameWithoutExtensionSecond == fileNameWithoutExtension) - continue; - fileNameWithoutExtensionSecondMinusOne = fileNameWithoutExtensionSecond[..^1]; - if (!results.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection)) - { - results.Add(fileNameWithoutExtensionSecondMinusOne, []); - if (!results.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection)) - throw new Exception(); - } - collection.Add(file); - } - } - 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; - string checkFile; - string directory; - string fileNameWith; - string checkDirectory; - string directoryName; - List? collection; - string fileNameWithoutExtension; - List directoryNames = []; - string fileNameWithoutExtensionSecond; - string fileNameWithoutExtensionSecondMinusOne; - foreach (string file in files) - { - if (!file.EndsWith(extension)) - throw new Exception(); - fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); - fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(fileNameWithoutExtension); - if (string.IsNullOrEmpty(fileNameWithoutExtensionSecond) || fileNameWithoutExtensionSecond == fileNameWithoutExtension) - continue; - fileNameWithoutExtensionSecondMinusOne = fileNameWithoutExtensionSecond[..^1]; - if (!fileNamesToFiles.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection)) - renameCollection.Add(file); - else - { - directoryNames.Clear(); - directoryName = Path.GetFileName(Path.GetDirectoryName(file)) ?? throw new Exception(); - foreach (string f in collection) - directoryNames.Add(Path.GetFileName(Path.GetDirectoryName(f)) ?? throw new Exception()); - if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1) - continue; - if (directoryName != directoryNames[0]) - { - directory = Path.GetDirectoryName(Path.GetDirectoryName(file)) ?? throw new Exception(); - checkDirectory = Path.Combine(directory, directoryNames[0]); - if (!Directory.Exists(checkDirectory)) - _ = Directory.CreateDirectory(checkDirectory); - fileNameWith = collection.Count > 1 ? Path.GetFileName(file) : $"{Path.GetFileName(collection[0])}{extension}"; - checkFile = Path.Combine(checkDirectory, fileNameWith); - 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); - } - } - } - } - return result; - } - - private static void AnyMovedFace(string extension, string hiddenExtension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) - { - 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) - { - 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); - } - } - } - 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) - { - 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; - 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) + private static List GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary? exifDirectoriesById, string directorySearchFilter) { List results = []; const string extension = ".json"; - List filePairs; + ReadOnlyCollection 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); + filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection); _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => - ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, filePairs[i], results)); + ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, exifDirectoriesById, 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) + private static void ParallelFor(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, int rootDirectoryLength, ReadOnlyDictionary? exifDirectoriesById, Shared.Models.FilePair filePair, List results) { dlibDotNet?.Tick(); bool abandoned = false; FileHolder sourceDirectoryFileHolder; - Property? property = GetProperty(filePair); - FileHolder fileHolder = IFileHolder.Get(filePair.Path); - ExifDirectory? exifDirectory = GetExifDirectory(filePair); - FilePath filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); - bool? fileSizeChanged = property is not null ? property.FileSize != filePath.Length : null; - bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); - bool? shouldIgnore = property is null || property.Keywords is null ? null : propertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l)); - bool? isArchive = filePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePath.Id.Value, out Identifier? identifier); - if (property is not null && filePath.Id is not null && filePath.HasIgnoreKeyword is not null && filePath.HasDateTimeOriginal is not null) + if (exifDirectoriesById is null || filePair.FilePath.Id is null || !exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out ExifDirectory? exifDirectory)) + exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); + ReadOnlyCollection keywords = IMetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories); + bool? shouldIgnore = propertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains); + bool? fileSizeChanged = exifDirectory is not null ? exifDirectory.FilePath.Length != filePair.FilePath.Length : null; + bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePair.FilePath.ExtensionLowered); + bool? isArchive = filePair.FilePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePair.FilePath.Id.Value, out Identifier? identifier); + if (exifDirectory is not null && filePair.FilePath.Id is not null && filePair.FilePath.HasIgnoreKeyword is not null && filePair.FilePath.HasDateTimeOriginal is not null) { char? change; ReadOnlyCollection? filePaths = null; - char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePath).ToString()[0]; - char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePath).ToString()[0]; - char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePath).ToString()[0]; + DateTime? dateTime = IDate.GetDateTimeOriginal(exifDirectory); + char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePair.FilePath).ToString()[0]; + char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0]; + char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0]; if (shouldIgnore is not null && shouldIgnore.Value) { - if (filePath.NameWithoutExtension[^1] == hasIgnoreKeyword) + if (filePair.FilePath.FileNameFirstSegment[^1] == hasIgnoreKeyword) change = null; else { change = hasIgnoreKeyword; - if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null) - throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); + if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null) + throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>"); } } - else if ((shouldIgnore is null || !shouldIgnore.Value) && property.DateTimeOriginal is null) + else if ((shouldIgnore is null || !shouldIgnore.Value) && dateTime is null) { - if (filePath.NameWithoutExtension[^1] == missingDateTimeOriginal) + if (filePair.FilePath.FileNameFirstSegment[^1] == missingDateTimeOriginal) change = null; else { change = missingDateTimeOriginal; - if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null) - throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); + if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null) + throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>"); } } - else if (filePath.NameWithoutExtension[^1] != hasDateTimeOriginal) + else if (filePair.FilePath.FileNameFirstSegment[^1] != hasDateTimeOriginal) { change = hasDateTimeOriginal; - if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null) - throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); + if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null) + throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>"); } else change = null; if (filePaths is not null && change is not null) - RenameFile(extension, filePair, filePath, change.Value, filePaths); + RenameFile(filePair, filePair.FilePath, change.Value, filePaths); } - string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.Path, rootDirectoryLength, forceExtensionToLower: true); - bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != filePath.LastWriteTicks : null; + string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.FilePath.FullName, rootDirectoryLength, forceExtensionToLower: true); + bool? lastWriteTimeChanged = exifDirectory is not null ? propertyConfiguration.PropertiesChangedForProperty || exifDirectory.FilePath.LastWriteTicks != filePair.FilePath.LastWriteTicks : null; if (filePair.Match is not null) sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match); else if (!filePair.IsUnique) sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(jsonGroupDirectory, relativePath, extension))); else { - string fileName = Path.GetFileName(filePair.Path); - CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath); + string fileName = Path.GetFileName(filePair.FilePath.FullName); + CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePair.FilePath); string directory = Path.Combine(jsonGroupDirectory, cei.Combined); string jsonFileName = $"{fileName}{extension}"; string fullFileName = Path.Combine(directory, jsonFileName); MoveIf(jsonFileName, cei, directory, fullFileName); sourceDirectoryFileHolder = IFileHolder.Get(fullFileName); } - if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) + if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePair.FilePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) { - File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePath.LastWriteTicks)); + File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePair.FilePath.LastWriteTicks)); File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); } - Item item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged); + Item item = Item.Get(filePair.FilePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, exifDirectory, abandoned, fileSizeChanged, lastWriteTimeChanged); lock (results) - results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item)); + results.Add(new(filePair.IsUnique, filePair.Collection, filePair.FilePath, item)); } - 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 RenameFile(string extension, Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection filePaths) + private static void RenameFile(Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection filePaths) { string checkFile; if (filePath.DirectoryFullPath.Contains("Results") && filePath.DirectoryFullPath.Contains("Resize")) File.Delete(filePath.FullName); - if (!string.IsNullOrEmpty(filePair.Match)) + if (filePair.Match is not null) { - 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}"); + string fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(filePair.Match.NameWithoutExtension); + string extensionSecond = Path.GetExtension(filePair.Match.Name); + checkFile = Path.Combine(filePair.Match.DirectoryFullPath, $"{fileNameWithoutExtensionSecond[..^1]}{change}{extensionSecond}{filePair.Match.ExtensionLowered}"); if (!File.Exists(checkFile)) - File.Move(filePair.Match, checkFile); + File.Move(filePair.Match.FullName, checkFile); } foreach (FilePath f in filePaths) { @@ -621,7 +271,7 @@ internal abstract class Container continue; foreach (Item item in filteredItems) { - if (item.Property?.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) continue; if (results.Contains(item.FilePath.FileNameFirstSegment)) continue; @@ -650,13 +300,13 @@ internal abstract class Container } foreach (Item item in filteredItems) { - if (item.Property?.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) continue; if (distinctItems) { - if (distinct.Contains(item.Property.Id.Value)) + if (distinct.Contains(item.ExifDirectory.FilePath.Id.Value)) continue; - distinct.Add(item.Property.Id.Value); + distinct.Add(item.ExifDirectory.FilePath.Id.Value); } results.Add(item); } @@ -664,4 +314,174 @@ internal abstract class Container return results.AsReadOnly(); } + internal static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary exifDirectoriesById) + { + Models.Container[] results; + const string directorySearchFilter = "*"; + (_, results) = GetContainers(dlibDotNet, propertyConfiguration, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, 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(); + ReadOnlyDictionary> fileNamesToFiles = FilePath.GetFilesKeyValuePairs(filePathsCollection); + string bMetaSingletonDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(bMetaSingletonDirectory)) + _ = Directory.CreateDirectory(bMetaSingletonDirectory); + _ = IFilePair.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); + _ = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, cResizeSingletonDirectory, filePathsCollection, fileNamesToFiles); + string dFaceCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultCollection); + if (!Directory.Exists(dFaceCollectionDirectory)) + _ = Directory.CreateDirectory(dFaceCollectionDirectory); + _ = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, dFaceCollectionDirectory, filePathsCollection, fileNamesToFiles); + string dFaceContentDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultContent); + if (!Directory.Exists(dFaceContentDirectory)) + _ = Directory.CreateDirectory(dFaceContentDirectory); + AnyMovedFace(propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, fileNamesToFiles, dFaceContentDirectory); + AnyMovedDistance(propertyConfiguration, facesFileNameExtension, fileNamesToFiles, eDistanceContentDirectory); + } + + private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, string extension, string hiddenExtension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) + { + 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) + { + 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); + } + } + } + if (directories.Count > 0) + AnyMovedFace(propertyConfiguration, fileNamesToFiles, directories); + } + + private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary> fileNamesToFiles, List directories) + { + bool result = false; + string checkFile; + FilePath filePath; + string subDirectory; + string directoryName; + string checkDirectory; + FileHolder fileHolder; + string directoryNameWith; + List? collection; + List directoryNames = []; + foreach (string directory in directories) + { + fileHolder = IFileHolder.Get(Path.GetFileName(directory)); + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + if (filePath.Id is null) + continue; + if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection)) + throw new Exception(); + directoryNames.Clear(); + foreach (FilePath f in collection) + directoryNames.Add(Path.GetFileName(f.DirectoryFullPath) ?? throw new Exception()); + if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1) + 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 : $"{collection[0].NameWithoutExtension}"; + 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(IPropertyConfiguration propertyConfiguration, string extension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) + { + bool result = false; + string checkFile; + string directory; + FilePath filePath; + FileHolder fileHolder; + string[] fileNameSegments; + List? collection; + List fileNames = []; + string[] files = Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories); + foreach (string file in files) + { + fileHolder = IFileHolder.Get(file); + if (!fileHolder.Exists) + continue; + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + if (filePath.Id is null) + continue; + fileNameSegments = filePath.Name.Split('.'); + if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection)) + continue; + fileNames.Clear(); + foreach (FilePath f in collection) + fileNames.Add(f.NameWithoutExtension ?? throw new Exception()); + if (fileNames.Count == 0 || fileNames.Distinct().Count() != 1) + continue; + if (filePath.FileNameFirstSegment != 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); + } + } + } + } + } \ No newline at end of file diff --git a/Container/Models/Stateless/Methods/IContainer.cs b/Container/Models/Stateless/Methods/IContainer.cs index 1d3752a..d5169ac 100644 --- a/Container/Models/Stateless/Methods/IContainer.cs +++ b/Container/Models/Stateless/Methods/IContainer.cs @@ -29,8 +29,8 @@ public interface IContainer 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); + public static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary exifDirectoriesById) => + Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById); internal DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection items) => GetContainerDateTimes(items); @@ -53,7 +53,7 @@ public interface IContainer 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); + internal ReadOnlyCollection TestStatic_GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary exifDirectoriesById) => + GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById); } \ No newline at end of file diff --git a/Copy-Distinct/CopyDistinct.cs b/Copy-Distinct/CopyDistinct.cs index dc53d50..2785c76 100644 --- a/Copy-Distinct/CopyDistinct.cs +++ b/Copy-Distinct/CopyDistinct.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Phares.Shared; using ShellProgressBar; @@ -19,7 +19,7 @@ public class CopyDistinct private readonly IsEnvironment _IsEnvironment; private readonly IConfigurationRoot _ConfigurationRoot; private readonly Property.Models.Configuration _PropertyConfiguration; - private readonly ReadOnlyDictionary>> _FileGroups; + private readonly ReadOnlyDictionary> _FileGroups; public CopyDistinct(List args, ILogger logger, IsEnvironment isEnvironment, IConfigurationRoot configurationRoot, AppSettings appSettings, string workingDirectory, bool isSilent, IConsole console) { @@ -37,7 +37,9 @@ public class CopyDistinct _Configuration = configuration; logger?.LogInformation(propertyConfiguration.RootDirectory); (bool move, ReadOnlyCollection filesCollection, bool anyLenFiles, bool moveBack) = Verify(); - _FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, appSettings.CopyTo, [appSettings.ResultDirectoryKey]); + ReadOnlyDictionary>> keyValuePairs = + Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, appSettings.CopyTo, [appSettings.ResultDirectoryKey]); + _FileGroups = keyValuePairs[appSettings.ResultDirectoryKey]; List lines = CopyDistinctFilesInDirectories(logger, move, filesCollection, anyLenFiles, moveBack); if (lines.Count != 0) File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); @@ -93,6 +95,76 @@ public class CopyDistinct return (move, new(filesCollection), anyLenFiles, moveBack); } + private List CopyDistinctFilesInDirectories(ILogger? logger, bool move, ReadOnlyCollection filesCollection, bool anyLenFiles, bool moveBack) + { + List results = []; + ProgressBar progressBar; + string[] distinctDirectories; + ConsoleKey? consoleKey = null; + string message = nameof(CopyDistinct); + List<(FilePath, string)> toDoCollection; + int count = filesCollection.Select(l => l.Length).Sum(); + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + if (moveBack) + { + if (!anyLenFiles) + throw new NotSupportedException(); + (distinctDirectories, toDoCollection) = GetMoveBackToDoCollection(_PropertyConfiguration, filesCollection); + } + else + { + progressBar = new(count, message, options); + string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey; + if (key != _PropertyConfiguration.ResultContent) + throw new NotImplementedException("Changed but didn't update!"); + ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, filesCollection, useIgnoreExtensions: true); + (distinctDirectories, toDoCollection) = IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, _AppSettings.IfCanUseId, filePathsCollection, _FileGroups, () => progressBar.Tick()); + progressBar.Dispose(); + } + foreach (string distinctDirectory in distinctDirectories) + { + if (!Directory.Exists(distinctDirectory)) + _ = Directory.CreateDirectory(distinctDirectory); + } + if (move) + logger?.LogInformation($"Ready to Move {toDoCollection.Count} file(s)?"); + else if (!moveBack) + logger?.LogInformation($"Ready to Copy {toDoCollection.Count} file(s)?"); + else + logger?.LogInformation($"Ready to Move back {toDoCollection.Count} file(s)?"); + for (int y = 0; y < int.MaxValue; y++) + { + if (move) + logger?.LogInformation("Press \"Y\" key to move file(s), \"N\" key to log file(s) or close console to not move files"); + else if (!moveBack) + logger?.LogInformation("Press \"Y\" key to copy file(s), \"N\" key to log file(s) or close console to not copy files"); + else + logger?.LogInformation("Press \"Y\" key to move back file(s), \"N\" key to log file(s) or close console to not move back files"); + consoleKey = System.Console.ReadKey().Key; + if (consoleKey is ConsoleKey.Y or ConsoleKey.N) + break; + } + logger?.LogInformation(". . ."); + if (consoleKey is null || consoleKey.Value != ConsoleKey.Y) + { + if (move || moveBack) + logger?.LogInformation("Nothing moved!"); + else + logger?.LogInformation("Nothing copied!"); + } + else + { + progressBar = new(count, message, options); + results.AddRange(IDirectory.CopyOrMove(toDoCollection, move, moveBack, () => progressBar.Tick())); + progressBar.Dispose(); + if (move || moveBack) + logger?.LogInformation("Done moving"); + else + logger?.LogInformation("Done copying"); + } + return results; + } + private static (string[], List<(FilePath, string)>) GetMoveBackToDoCollection(Property.Models.Configuration propertyConfiguration, ReadOnlyCollection filesCollection) { List<(FilePath, string)> results = []; @@ -159,74 +231,4 @@ public class CopyDistinct return (distinctDirectories.ToArray(), results); } - private List CopyDistinctFilesInDirectories(ILogger? logger, bool move, ReadOnlyCollection filesCollection, bool anyLenFiles, bool moveBack) - { - List results = []; - ProgressBar progressBar; - string[] distinctDirectories; - ConsoleKey? consoleKey = null; - string message = nameof(CopyDistinct); - List<(FilePath, string)> toDoCollection; - int count = filesCollection.Select(l => l.Length).Sum(); - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - if (moveBack) - { - if (!anyLenFiles) - throw new NotSupportedException(); - (distinctDirectories, toDoCollection) = GetMoveBackToDoCollection(_PropertyConfiguration, filesCollection); - } - else - { - progressBar = new(count, message, options); - string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey; - if (key != _PropertyConfiguration.ResultContent) - throw new NotImplementedException("Changed but didn't update!"); - ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, filesCollection); - (distinctDirectories, toDoCollection) = IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, _AppSettings.IfCanUseId, filePathsCollection, _FileGroups, () => progressBar.Tick()); - progressBar.Dispose(); - } - foreach (string distinctDirectory in distinctDirectories) - { - if (!Directory.Exists(distinctDirectory)) - _ = Directory.CreateDirectory(distinctDirectory); - } - if (move) - logger?.LogInformation($"Ready to Move {toDoCollection.Count} file(s)?"); - else if (!moveBack) - logger?.LogInformation($"Ready to Copy {toDoCollection.Count} file(s)?"); - else - logger?.LogInformation($"Ready to Move back {toDoCollection.Count} file(s)?"); - for (int y = 0; y < int.MaxValue; y++) - { - if (move) - logger?.LogInformation("Press \"Y\" key to move file(s), \"N\" key to log file(s) or close console to not move files"); - else if (!moveBack) - logger?.LogInformation("Press \"Y\" key to copy file(s), \"N\" key to log file(s) or close console to not copy files"); - else - logger?.LogInformation("Press \"Y\" key to move back file(s), \"N\" key to log file(s) or close console to not move back files"); - consoleKey = System.Console.ReadKey().Key; - if (consoleKey is ConsoleKey.Y or ConsoleKey.N) - break; - } - logger?.LogInformation(". . ."); - if (consoleKey is null || consoleKey.Value != ConsoleKey.Y) - { - if (move || moveBack) - logger?.LogInformation("Nothing moved!"); - else - logger?.LogInformation("Nothing copied!"); - } - else - { - progressBar = new(count, message, options); - results.AddRange(IDirectory.CopyOrMove(toDoCollection, move, moveBack, () => progressBar.Tick())); - progressBar.Dispose(); - if (move || moveBack) - logger?.LogInformation("Done moving"); - else - logger?.LogInformation("Done copying"); - } - return results; - } - } \ No newline at end of file diff --git a/Date-Group/DateGroup.cs b/Date-Group/DateGroup.cs index 3075263..9460b51 100644 --- a/Date-Group/DateGroup.cs +++ b/Date-Group/DateGroup.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Phares.Shared; using System.Globalization; @@ -96,203 +96,6 @@ public class DateGroup throw new Exception("Change configuration!"); } - private static bool WriteAllText(string path, string contents, bool compareBeforeWrite) - { - bool result; - string text; - if (!compareBeforeWrite) - result = true; - else - { - if (!File.Exists(path)) - text = string.Empty; - else - text = File.ReadAllText(path); - result = text != contents; - } - 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; - } - - private List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> GetMoveFileCollection(string destinationDirectory, string topDirectory, Item[] filteredItems) - { - List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> results = []; - char flag; - string day; - int season; - string year; - string month; - string? check; - string fileName; - string? pathRoot; - DateTime dateTime; - string seasonName; - string weekOfYear; - bool? isWrongYear; - string seasonValue; - string directoryName; - string topDirectoryName; - List dateTimes; - string[]? matches = null; - string[] directorySegments; - List destinationCollection; - List directoryNames = []; - List topDirectorySegments = []; - StringBuilder destinationDirectoryName = new(); - Calendar calendar = new CultureInfo("en-US").Calendar; - for (int z = 1; z < 3; z++) - { - if (z == 1) - { - check = Path.Combine(destinationDirectory, "."); - pathRoot = Path.GetPathRoot(destinationDirectory); - } - else if (z == 2) - { - check = Path.Combine(topDirectory, "."); - pathRoot = Path.GetPathRoot(topDirectory); - } - else - throw new Exception(); - if (string.IsNullOrEmpty(pathRoot)) - continue; - for (int i = 0; i < int.MaxValue; i++) - { - check = Path.GetDirectoryName(check); - if (string.IsNullOrEmpty(check) || check == pathRoot) - break; - directoryName = Path.GetFileName(check); - directorySegments = directoryName.Split(' '); - topDirectorySegments.AddRange(directorySegments); - (_, matches) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(directorySegments, string.Empty); - if (matches.Length != 0) - break; - } - if (matches is not null && matches.Length != 0) - break; - } - foreach (Item item in filteredItems) - { - directoryNames.Clear(); - destinationCollection = []; - _ = destinationDirectoryName.Clear(); - if (item.Property is not null) - dateTimes = item.Property.GetDateTimes(); - else - dateTimes = [new(item.FilePath.LastWriteTicks)]; - if (item.Property is not null && item.Property.DateTimeOriginal is not null) - dateTime = item.Property.DateTimeOriginal.Value; - else if (item.Property is null) - dateTime = new(item.FilePath.LastWriteTicks); - else - dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - day = dateTime.ToString("MM-dd"); - month = dateTime.ToString("MMMM"); - if (item.Property?.Id is null) - { - flag = '#'; - isWrongYear = null; - } - else - { - (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes); - if (isWrongYear is null) - flag = '#'; - else if (isWrongYear.Value) - flag = '~'; - else - { - if (item.Property.DateTimeOriginal is not null && dateTime.DayOfYear != item.Property.DateTimeOriginal.Value.DayOfYear && Math.Abs(new TimeSpan(dateTime.Ticks - item.Property.DateTimeOriginal.Value.Ticks).TotalHours) > 8) - flag = '^'; - else - flag = '='; - } - } - (season, seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); - if ((from l in topDirectorySegments where l == "Christmas" select true).Any()) - seasonValue = string.Empty; - else - seasonValue = $".{season}"; - if (isWrongYear is null || !isWrongYear.Value) - year = $"{flag}{dateTime:yyyy}{seasonValue}"; - else if (matches is null || matches.Length < 3) - year = "----"; - else - { - if (matches[0][0] != '~') - year = $"{flag}{matches[0].Split('.')[0]}{seasonValue}"; - else - year = $"{flag}{matches[0][1..].Split('.')[0]}{seasonValue}"; - } - topDirectoryName = Path.GetFileName(topDirectory); - weekOfYear = calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00"); - if (_Configuration.ByHash) - directoryNames.Add($"{year} {seasonName}"); - else if (_Configuration.BySeason && topDirectoryName.Length == 1 && topDirectoryName[0] == '_') - directoryNames.Add($"{year} {seasonName}"); - else - { - if (!_Configuration.KeepFullPath) - { - if (topDirectoryName.Length > 1) - _ = destinationDirectoryName.Append(topDirectoryName); - if (_Configuration.BySeason) - directoryNames.AddRange([$"{destinationDirectoryName} {year} {seasonName}"]); - else if (_Configuration.ByDay) - directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year}-{day}"]); - else if (_Configuration.ByWeek) - directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year} {month}"]); - else - throw new Exception(); - } - else - { - foreach (string sourceDirectoryNameSegment in topDirectorySegments) - { - if (matches is not null && matches.Contains(sourceDirectoryNameSegment)) - _ = destinationDirectoryName.Append(year); - else - _ = destinationDirectoryName.Append(sourceDirectoryNameSegment); - } - if (_Configuration.BySeason) - directoryNames.Add($"{year} {seasonName}"); - else if (_Configuration.ByDay) - directoryNames.Add($"{weekOfYear}) {year} {day}"); - else if (_Configuration.ByWeek) - directoryNames.Add($"{weekOfYear}) {month} {year}"); - else - throw new Exception(); - } - } - if (!_Configuration.ByHash || item.Property?.Id is null) - fileName = item.FilePath.Name; - else - fileName = $"{item.Property.Id.Value}{item.FilePath.ExtensionLowered}"; - destinationCollection.Add(destinationDirectory); - destinationCollection.AddRange(directoryNames); - destinationCollection.Add(fileName); - if (item.Property is null) - results.Add(new(item, item.FilePath.LastWriteTicks, dateTime.Ticks, destinationCollection.ToArray())); - else - results.Add(new(item, item.Property.LastWriteTime.Ticks, dateTime.Ticks, destinationCollection.ToArray())); - } - return results; - } - private A_Property GetPropertyLogic(bool reverse, string outputExtension, string aResultsFullGroupDirectory) { A_Property result; @@ -302,54 +105,64 @@ public class DateGroup return result; } - private static Item[] GetFilterItems(Container.Models.Container container) + private static void CreateDateShortcut(Property.Models.Configuration configuration, Container.Models.Container[] containers) { - List results = []; - foreach (Item item in container.Items) - { - if (item.FilePath is not null) - results.Add(item); - } - return results.ToArray(); - } - - private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers) - { - (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results; - Item[] filteredItems; - string? topDirectory; - string? checkDirectory; - string destinationDirectory; - List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollection = []; - List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollectionDirectory; + string path; + string fileName; + string directory; + DateTime dateTime; + int selectedTotal; + const int minimum = 3; + List dateTimes; + List selectedItems; + const int maximumHours = 24; + string? relativePathDirectory; + WindowsShortcut windowsShortcut; + TimeSpan threeStandardDeviationHigh; + string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()"); foreach (Container.Models.Container container in containers) { if (container.Items.Count == 0) continue; - if (!_Configuration.KeepFullPath) - destinationDirectory = destinationRoot; - else - destinationDirectory = string.Concat(destinationRoot, container.SourceDirectory[configuration.RootDirectory.Length..]); - checkDirectory = Path.GetFullPath(container.SourceDirectory); - for (int z = 0; z < int.MaxValue; z++) + selectedTotal = 0; + threeStandardDeviationHigh = Property.Models.Stateless.IProperty.GetThreeStandardDeviationHigh(minimum, container); + if (threeStandardDeviationHigh.TotalHours > maximumHours) + threeStandardDeviationHigh = new(maximumHours, 0, 0); + for (int i = 0; i < container.Items.Count; i++) { - if (checkDirectory == configuration.RootDirectory) - break; - checkDirectory = Path.GetDirectoryName(checkDirectory); - if (string.IsNullOrEmpty(checkDirectory)) - break; + (i, dateTimes, selectedItems) = Property.Models.Stateless.IProperty.Get(container, threeStandardDeviationHigh, i); + selectedTotal += selectedItems.Count; + foreach (Item item in selectedItems) + { + if (item.ExifDirectory is null) + continue; + relativePathDirectory = Path.GetDirectoryName(item.RelativePath); + if (string.IsNullOrEmpty(relativePathDirectory)) + continue; + if (item.ExifDirectory is null) + dateTime = new(item.FilePath.LastWriteTicks); + else if (item.ExifDirectory is not null && item.ExifDirectory.DateTimeOriginal is not null) + dateTime = item.ExifDirectory.DateTimeOriginal.Value; + else + dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.ExifDirectory); + path = Path.GetFullPath($"{configuration.RootDirectory}{item.RelativePath[..^5]}"); + directory = Path.Combine($"{aPropertyContentDirectory}{relativePathDirectory}", $"{dateTimes.Min():yyyy-MM-dd_HH-mm-ss}---{dateTimes.Max():yyyy-MM-dd_HH-mm-ss}"); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + fileName = Path.Combine(directory, $"{Path.GetFileName(item.RelativePath[..^5])}.lnk"); + if (File.Exists(fileName)) + continue; + windowsShortcut = new() { Path = path }; + windowsShortcut.Save(fileName); + windowsShortcut.Dispose(); + if (!File.Exists(fileName)) + continue; + File.SetLastWriteTime(fileName, dateTime); + } } - if (string.IsNullOrEmpty(checkDirectory)) + if (selectedTotal < container.Items.Count && selectedTotal < (from l in container.Items where l.Property is not null select true).Count()) continue; - topDirectory = checkDirectory; - filteredItems = GetFilterItems(container); - if (filteredItems.Length == 0) - continue; - fileMoveCollectionDirectory = GetMoveFileCollection(destinationDirectory, topDirectory, filteredItems); - fileMoveCollection.AddRange(fileMoveCollectionDirectory); } - results = (from l in fileMoveCollection orderby l.MinimumDateTimeTicks descending, l.LastWriteTimeTicks descending select l).ToArray(); - return results; } private void MoveFiles(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers) @@ -453,64 +266,251 @@ public class DateGroup _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(configuration.RootDirectory); } - private static void CreateDateShortcut(Property.Models.Configuration configuration, Container.Models.Container[] containers) + private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers) { - string path; - string fileName; - string directory; - DateTime dateTime; - int selectedTotal; - const int minimum = 3; - List dateTimes; - List selectedItems; - const int maximumHours = 24; - string? relativePathDirectory; - WindowsShortcut windowsShortcut; - TimeSpan threeStandardDeviationHigh; - string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()"); + (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results; + Item[] filteredItems; + string? topDirectory; + string? checkDirectory; + string destinationDirectory; + List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollection = []; + List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollectionDirectory; foreach (Container.Models.Container container in containers) { if (container.Items.Count == 0) continue; - selectedTotal = 0; - threeStandardDeviationHigh = Property.Models.Stateless.IProperty.GetThreeStandardDeviationHigh(minimum, container); - if (threeStandardDeviationHigh.TotalHours > maximumHours) - threeStandardDeviationHigh = new(maximumHours, 0, 0); - for (int i = 0; i < container.Items.Count; i++) + if (!_Configuration.KeepFullPath) + destinationDirectory = destinationRoot; + else + destinationDirectory = string.Concat(destinationRoot, container.SourceDirectory[configuration.RootDirectory.Length..]); + checkDirectory = Path.GetFullPath(container.SourceDirectory); + for (int z = 0; z < int.MaxValue; z++) { - (i, dateTimes, selectedItems) = Property.Models.Stateless.IProperty.Get(container, threeStandardDeviationHigh, i); - selectedTotal += selectedItems.Count; - foreach (Item item in selectedItems) + if (checkDirectory == configuration.RootDirectory) + break; + checkDirectory = Path.GetDirectoryName(checkDirectory); + if (string.IsNullOrEmpty(checkDirectory)) + break; + } + if (string.IsNullOrEmpty(checkDirectory)) + continue; + topDirectory = checkDirectory; + filteredItems = GetFilterItems(container); + if (filteredItems.Length == 0) + continue; + fileMoveCollectionDirectory = GetMoveFileCollection(destinationDirectory, topDirectory, filteredItems); + fileMoveCollection.AddRange(fileMoveCollectionDirectory); + } + results = (from l in fileMoveCollection orderby l.MinimumDateTimeTicks descending, l.LastWriteTimeTicks descending select l).ToArray(); + return results; + } + + private static Item[] GetFilterItems(Container.Models.Container container) + { + List results = []; + foreach (Item item in container.Items) + { + if (item.FilePath is not null) + results.Add(item); + } + return results.ToArray(); + } + + private List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> GetMoveFileCollection(string destinationDirectory, string topDirectory, Item[] filteredItems) + { + List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> results = []; + char flag; + string day; + int season; + string year; + string month; + string? check; + string fileName; + string? pathRoot; + DateTime dateTime; + string seasonName; + string weekOfYear; + bool? isWrongYear; + string seasonValue; + string directoryName; + string topDirectoryName; + List dateTimes; + string[]? matches = null; + string[] directorySegments; + List destinationCollection; + List directoryNames = []; + List topDirectorySegments = []; + StringBuilder destinationDirectoryName = new(); + Calendar calendar = new CultureInfo("en-US").Calendar; + for (int z = 1; z < 3; z++) + { + if (z == 1) + { + check = Path.Combine(destinationDirectory, "."); + pathRoot = Path.GetPathRoot(destinationDirectory); + } + else if (z == 2) + { + check = Path.Combine(topDirectory, "."); + pathRoot = Path.GetPathRoot(topDirectory); + } + else + throw new Exception(); + if (string.IsNullOrEmpty(pathRoot)) + continue; + for (int i = 0; i < int.MaxValue; i++) + { + check = Path.GetDirectoryName(check); + if (string.IsNullOrEmpty(check) || check == pathRoot) + break; + directoryName = Path.GetFileName(check); + directorySegments = directoryName.Split(' '); + topDirectorySegments.AddRange(directorySegments); + (_, matches) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(directorySegments, string.Empty); + if (matches.Length != 0) + break; + } + if (matches is not null && matches.Length != 0) + break; + } + foreach (Item item in filteredItems) + { + directoryNames.Clear(); + destinationCollection = []; + _ = destinationDirectoryName.Clear(); + if (item.ExifDirectory is not null) + dateTimes = item.ExifDirectory.GetDateTimes(); + else + dateTimes = [new(item.FilePath.LastWriteTicks)]; + if (item.ExifDirectory is not null && item.ExifDirectory.DateTimeOriginal is not null) + dateTime = item.ExifDirectory.DateTimeOriginal.Value; + else if (item.ExifDirectory is null) + dateTime = new(item.FilePath.LastWriteTicks); + else + dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.ExifDirectory); + day = dateTime.ToString("MM-dd"); + month = dateTime.ToString("MMMM"); + if (item.ExifDirectory?.FilePath.Id is null) + { + flag = '#'; + isWrongYear = null; + } + else + { + (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.ExifDirectory.DateTimeOriginal, dateTimes); + if (isWrongYear is null) + flag = '#'; + else if (isWrongYear.Value) + flag = '~'; + else { - if (item.Property is null) - continue; - relativePathDirectory = Path.GetDirectoryName(item.RelativePath); - if (string.IsNullOrEmpty(relativePathDirectory)) - continue; - if (item.Property is null) - dateTime = new(item.FilePath.LastWriteTicks); - else if (item.Property is not null && item.Property.DateTimeOriginal is not null) - dateTime = item.Property.DateTimeOriginal.Value; + if (item.ExifDirectory.DateTimeOriginal is not null && dateTime.DayOfYear != item.ExifDirectory.DateTimeOriginal.Value.DayOfYear && Math.Abs(new TimeSpan(dateTime.Ticks - item.ExifDirectory.DateTimeOriginal.Value.Ticks).TotalHours) > 8) + flag = '^'; else - dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - path = Path.GetFullPath($"{configuration.RootDirectory}{item.RelativePath[..^5]}"); - directory = Path.Combine($"{aPropertyContentDirectory}{relativePathDirectory}", $"{dateTimes.Min():yyyy-MM-dd_HH-mm-ss}---{dateTimes.Max():yyyy-MM-dd_HH-mm-ss}"); - if (!Directory.Exists(directory)) - _ = Directory.CreateDirectory(directory); - fileName = Path.Combine(directory, $"{Path.GetFileName(item.RelativePath[..^5])}.lnk"); - if (File.Exists(fileName)) - continue; - windowsShortcut = new() { Path = path }; - windowsShortcut.Save(fileName); - windowsShortcut.Dispose(); - if (!File.Exists(fileName)) - continue; - File.SetLastWriteTime(fileName, dateTime); + flag = '='; } } - if (selectedTotal < container.Items.Count && selectedTotal < (from l in container.Items where l.Property is not null select true).Count()) - continue; + (season, seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); + if ((from l in topDirectorySegments where l == "Christmas" select true).Any()) + seasonValue = string.Empty; + else + seasonValue = $".{season}"; + if (isWrongYear is null || !isWrongYear.Value) + year = $"{flag}{dateTime:yyyy}{seasonValue}"; + else if (matches is null || matches.Length < 3) + year = "----"; + else + { + if (matches[0][0] != '~') + year = $"{flag}{matches[0].Split('.')[0]}{seasonValue}"; + else + year = $"{flag}{matches[0][1..].Split('.')[0]}{seasonValue}"; + } + topDirectoryName = Path.GetFileName(topDirectory); + weekOfYear = calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00"); + if (_Configuration.ByHash) + directoryNames.Add($"{year} {seasonName}"); + else if (_Configuration.BySeason && topDirectoryName.Length == 1 && topDirectoryName[0] == '_') + directoryNames.Add($"{year} {seasonName}"); + else + { + if (!_Configuration.KeepFullPath) + { + if (topDirectoryName.Length > 1) + _ = destinationDirectoryName.Append(topDirectoryName); + if (_Configuration.BySeason) + directoryNames.AddRange([$"{destinationDirectoryName} {year} {seasonName}"]); + else if (_Configuration.ByDay) + directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year}-{day}"]); + else if (_Configuration.ByWeek) + directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year} {month}"]); + else + throw new Exception(); + } + else + { + foreach (string sourceDirectoryNameSegment in topDirectorySegments) + { + if (matches is not null && matches.Contains(sourceDirectoryNameSegment)) + _ = destinationDirectoryName.Append(year); + else + _ = destinationDirectoryName.Append(sourceDirectoryNameSegment); + } + if (_Configuration.BySeason) + directoryNames.Add($"{year} {seasonName}"); + else if (_Configuration.ByDay) + directoryNames.Add($"{weekOfYear}) {year} {day}"); + else if (_Configuration.ByWeek) + directoryNames.Add($"{weekOfYear}) {month} {year}"); + else + throw new Exception(); + } + } + if (!_Configuration.ByHash || item.ExifDirectory?.FilePath.Id is null) + fileName = item.FilePath.Name; + else + fileName = $"{item.ExifDirectory.FilePath.Id.Value}{item.FilePath.ExtensionLowered}"; + destinationCollection.Add(destinationDirectory); + destinationCollection.AddRange(directoryNames); + destinationCollection.Add(fileName); + if (item.ExifDirectory is null) + results.Add(new(item, item.FilePath.LastWriteTicks, dateTime.Ticks, destinationCollection.ToArray())); + else + results.Add(new(item, item.ExifDirectory.LastWriteTime.Ticks, dateTime.Ticks, destinationCollection.ToArray())); } + return results; + } + + private static bool WriteAllText(string path, string contents, bool compareBeforeWrite) + { + bool result; + string text; + if (!compareBeforeWrite) + result = true; + else + { + if (!File.Exists(path)) + text = string.Empty; + else + text = File.ReadAllText(path); + result = text != contents; + } + 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; } } \ No newline at end of file diff --git a/Drag-Drop-Search/DragDropSearch.cs b/Drag-Drop-Search/DragDropSearch.cs index 1811159..abf2426 100644 --- a/Drag-Drop-Search/DragDropSearch.cs +++ b/Drag-Drop-Search/DragDropSearch.cs @@ -64,63 +64,6 @@ public partial class DragDropSearch : Form Controls.Add(_TextBox); } - private void Form1_Load(object? sender, EventArgs e) - { - try - { - AllowDrop = true; - DragDrop += new DragEventHandler(Form1_DragDrop); - DragEnter += new DragEventHandler(Form1_DragEnter); - _TextBox.LostFocus += new EventHandler(TextBox_LostFocus); - } - catch (Exception) - { - throw; - } - } - - private void TextBox_LostFocus(object? sender, EventArgs e) - { - try - { - if (_TextBox.Text == "ps") - throw new NotImplementedException(); - } - catch (Exception) - { - throw; - } - } - - private void Form1_DragEnter(object? sender, DragEventArgs e) - { - try - { - if (e.Data is not null && e.Data.GetDataPresent(DataFormats.FileDrop)) - e.Effect = DragDropEffects.Copy; - } - catch (Exception) - { - throw; - } - } - - private void LoadData() - { - Container.Models.Container[] containers; - string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}"); - (_, containers) = View_by_Distance.Container.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory); - List collection = Program.GetItemCollection(_Configuration, containers); - foreach (Item item in collection) - { - if (item.Property?.Id is null) - continue; - if (_IdToItem.ContainsKey(item.Property.Id.Value)) - continue; - _IdToItem.Add(item.Property.Id.Value, item); - } - } - public static string? GetFaceEncoding(string file) { string? result; @@ -147,6 +90,37 @@ public partial class DragDropSearch : Form return result; } + private void LoadData() + { + Container.Models.Container[] containers; + string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}"); + (_, containers) = View_by_Distance.Container.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory); + List collection = Program.GetItemCollection(_Configuration, containers); + foreach (Item item in collection) + { + if (item.ExifDirectory?.FilePath.Id is null) + continue; + if (_IdToItem.ContainsKey(item.ExifDirectory.FilePath.Id.Value)) + continue; + _IdToItem.Add(item.ExifDirectory.FilePath.Id.Value, item); + } + } + + private void Form1_Load(object? sender, EventArgs e) + { + try + { + AllowDrop = true; + DragDrop += new DragEventHandler(Form1_DragDrop); + DragEnter += new DragEventHandler(Form1_DragEnter); + _TextBox.LostFocus += new EventHandler(TextBox_LostFocus); + } + catch (Exception) + { + throw; + } + } + private void GetDirectoriesOrDoDragDrop(string[] paths) { string name; @@ -168,6 +142,19 @@ public partial class DragDropSearch : Form } } + private void TextBox_LostFocus(object? sender, EventArgs e) + { + try + { + if (_TextBox.Text == "ps") + throw new NotImplementedException(); + } + catch (Exception) + { + throw; + } + } + private void Form1_DragDrop(object? sender, DragEventArgs e) { try @@ -183,4 +170,17 @@ public partial class DragDropSearch : Form } } + private void Form1_DragEnter(object? sender, DragEventArgs e) + { + try + { + if (e.Data is not null && e.Data.GetDataPresent(DataFormats.FileDrop)) + e.Effect = DragDropEffects.Copy; + } + catch (Exception) + { + throw; + } + } + } \ No newline at end of file diff --git a/Duplicate-Search/DuplicateSearch.cs b/Duplicate-Search/DuplicateSearch.cs index 82e1694..a897783 100644 --- a/Duplicate-Search/DuplicateSearch.cs +++ b/Duplicate-Search/DuplicateSearch.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Phares.Shared; using ShellProgressBar; @@ -61,6 +61,144 @@ public class DuplicateSearch File.WriteAllText(Path.Combine(alongSideDirectory, $"{directoryName}-{ticks}.json"), json); } + private static Container.Models.Container[] GetContainers(long ticks, Configuration configuration) + { + int f; + Container.Models.Container[] containers; + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + string message = $") Building Container(s) - {totalSeconds} total second(s)"; + ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; + using (ProgressBar progressBar = new(1, message, options)) + { + progressBar.Tick(); + string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}"); + (f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory); + } + return containers; + } + + private static List GetPreloadIds(string destinationRoot) + { + List results = []; + string[] lines; + string preloadDirectory = Path.Combine(destinationRoot, "Preload"); + if (!Directory.Exists(preloadDirectory)) + _ = Directory.CreateDirectory(preloadDirectory); + string[] files = Directory.GetFiles(preloadDirectory, "*.lsv", SearchOption.TopDirectoryOnly); + foreach (string file in files) + { + lines = File.ReadAllLines(file); + foreach (string line in lines) + { + if (string.IsNullOrEmpty(line) || !int.TryParse(line, out int id) || id == 0) + continue; + results.Add(id); + } + } + return results; + } + + private static Dictionary> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, Container.Models.Container[] containers, string destinationRoot, List preloadIds) + { + Dictionary> results = []; + string directory; + const int zero = 0; + FileHolder resizedFileHolder; + DateTime[] containerDateTimes; + MappingFromItem? mappingFromItem; + List? collection; + ReadOnlyCollection validImageItems; + const string duplicates = "-Duplicate(s)"; + if (containers.Length != 0) + { + foreach (int id in preloadIds) + results.Add(id, [null]); + } + foreach (Container.Models.Container container in containers) + { + if (container.Items.Count == 0) + continue; + if (!argZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) + continue; + validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(configuration, container); + if (validImageItems.Count == 0) + continue; + containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(validImageItems); + foreach (Item item in validImageItems) + { + if (item.ExifDirectory?.FilePath.Id is null) + { + if (int.TryParse(item.FilePath.NameWithoutExtension, out int id)) + continue; + continue; + } + if (!results.TryGetValue(item.ExifDirectory.FilePath.Id.Value, out collection)) + results.Add(item.ExifDirectory.FilePath.Id.Value, []); + if (collection is null && !results.TryGetValue(item.ExifDirectory.FilePath.Id.Value, out collection)) + continue; + if (collection.Count == 0) + directory = $"0{duplicates}"; + else + directory = $"{collection.Count + 1}{duplicates}"; + if (collection.Count == 1) + { + mappingFromItem = collection[zero]; + if (mappingFromItem is not null) + { + resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(mappingFromItem.ResizedFileHolder.FullName.Replace($"0{duplicates}", $"1{duplicates}")); + collection[0] = new(mappingFromItem.ContainerDateTimes, item.Property.DateTimeDigitized, item.Property.DateTimeOriginal, mappingFromItem.Id, mappingFromItem.IsArchive, mappingFromItem.FilePath, mappingFromItem.IsWrongYear, item.Property.Keywords ?? [], mappingFromItem.MinimumDateTime, item.Property.Model, mappingFromItem.RelativePath, resizedFileHolder); + } + } + resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(string.Concat(Path.Combine(destinationRoot, directory), item.RelativePath)); + mappingFromItem = Shared.Models.Stateless.Methods.IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); + collection.Add(mappingFromItem); + } + } + return results; + } + + private static void QuestionMove(long ticks, ILogger? logger, string destinationRoot, Dictionary> idToCollection, int duplicates) + { + int[] ids = (from l in idToCollection orderby l.Key where l.Value.Any(m => m is not null) select l.Key).ToArray(); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(destinationRoot, $"{ticks}-id(s).lsv"), string.Join(Environment.NewLine, ids), updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + string json = JsonSerializer.Serialize(idToCollection, new JsonSerializerOptions { WriteIndented = true }); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(destinationRoot, $"{ticks}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + logger?.LogInformation($"Found {duplicates} duplicate file(s)"); + for (int y = 0; y < int.MaxValue; y++) + { + logger?.LogInformation("Press \"Y\" key to continue or close console to leave them moved"); + if (System.Console.ReadKey().Key != ConsoleKey.Y) + continue; + logger?.LogInformation(". . ."); + List<(FilePath FilePath, string Destination)> collection = GetCollectionAndCreateDirectories(idToCollection); + Move(logger, ticks, destinationRoot, collection); + } + } + + private static List<(FilePath FilePath, string Destination)> GetCollectionAndCreateDirectories(Dictionary> idToCollection) + { + List<(FilePath FilePath, string Destination)> results = []; + List collection = []; + foreach (KeyValuePair> keyValuePair in idToCollection) + { + foreach (MappingFromItem? mappingFromItem in keyValuePair.Value) + { + if (mappingFromItem?.ResizedFileHolder.DirectoryFullPath is null) + continue; + if (mappingFromItem.ResizedFileHolder.Exists) + continue; + collection.Add(mappingFromItem.ResizedFileHolder.DirectoryFullPath); + results.Add(new(mappingFromItem.FilePath, mappingFromItem.ResizedFileHolder.FullName)); + } + } + foreach (string directory in collection.Distinct()) + { + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); + } + return results; + } + private static void Move(ILogger? logger, long ticks, string destinationRoot, List<(FilePath FilePath, string Destination)> collection) { StringBuilder stringBuilder = new(); @@ -107,142 +245,4 @@ public class DuplicateSearch logger?.LogInformation(". . ."); } - private static void QuestionMove(long ticks, ILogger? logger, string destinationRoot, Dictionary> idToCollection, int duplicates) - { - int[] ids = (from l in idToCollection orderby l.Key where l.Value.Any(m => m is not null) select l.Key).ToArray(); - _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(destinationRoot, $"{ticks}-id(s).lsv"), string.Join(Environment.NewLine, ids), updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - string json = JsonSerializer.Serialize(idToCollection, new JsonSerializerOptions { WriteIndented = true }); - _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(destinationRoot, $"{ticks}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); - logger?.LogInformation($"Found {duplicates} duplicate file(s)"); - for (int y = 0; y < int.MaxValue; y++) - { - logger?.LogInformation("Press \"Y\" key to continue or close console to leave them moved"); - if (System.Console.ReadKey().Key != ConsoleKey.Y) - continue; - logger?.LogInformation(". . ."); - List<(FilePath FilePath, string Destination)> collection = GetCollectionAndCreateDirectories(idToCollection); - Move(logger, ticks, destinationRoot, collection); - } - } - - private static Container.Models.Container[] GetContainers(long ticks, Configuration configuration) - { - int f; - Container.Models.Container[] containers; - int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - string message = $") Building Container(s) - {totalSeconds} total second(s)"; - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using (ProgressBar progressBar = new(1, message, options)) - { - progressBar.Tick(); - string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}"); - (f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory); - } - return containers; - } - - private static Dictionary> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, Container.Models.Container[] containers, string destinationRoot, List preloadIds) - { - Dictionary> results = []; - string directory; - const int zero = 0; - FileHolder resizedFileHolder; - DateTime[] containerDateTimes; - MappingFromItem? mappingFromItem; - List? collection; - ReadOnlyCollection validImageItems; - const string duplicates = "-Duplicate(s)"; - if (containers.Length != 0) - { - foreach (int id in preloadIds) - results.Add(id, [null]); - } - foreach (Container.Models.Container container in containers) - { - if (container.Items.Count == 0) - continue; - if (!argZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) - continue; - validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(configuration, container); - if (validImageItems.Count == 0) - continue; - containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(validImageItems); - foreach (Item item in validImageItems) - { - if (item.Property?.Id is null) - { - if (int.TryParse(item.FilePath.NameWithoutExtension, out int id)) - continue; - continue; - } - if (!results.TryGetValue(item.Property.Id.Value, out collection)) - results.Add(item.Property.Id.Value, []); - if (collection is null && !results.TryGetValue(item.Property.Id.Value, out collection)) - continue; - if (collection.Count == 0) - directory = $"0{duplicates}"; - else - directory = $"{collection.Count + 1}{duplicates}"; - if (collection.Count == 1) - { - mappingFromItem = collection[zero]; - if (mappingFromItem is not null) - { - resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(mappingFromItem.ResizedFileHolder.FullName.Replace($"0{duplicates}", $"1{duplicates}")); - collection[0] = new(mappingFromItem.ContainerDateTimes, item.Property.DateTimeDigitized, item.Property.DateTimeOriginal, mappingFromItem.Id, mappingFromItem.IsArchive, mappingFromItem.FilePath, mappingFromItem.IsWrongYear, item.Property.Keywords ?? [], mappingFromItem.MinimumDateTime, item.Property.Model, mappingFromItem.RelativePath, resizedFileHolder); - } - } - resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(string.Concat(Path.Combine(destinationRoot, directory), item.RelativePath)); - mappingFromItem = Shared.Models.Stateless.Methods.IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); - collection.Add(mappingFromItem); - } - } - return results; - } - - private static List<(FilePath FilePath, string Destination)> GetCollectionAndCreateDirectories(Dictionary> idToCollection) - { - List<(FilePath FilePath, string Destination)> results = []; - List collection = []; - foreach (KeyValuePair> keyValuePair in idToCollection) - { - foreach (MappingFromItem? mappingFromItem in keyValuePair.Value) - { - if (mappingFromItem?.ResizedFileHolder.DirectoryFullPath is null) - continue; - if (mappingFromItem.ResizedFileHolder.Exists) - continue; - collection.Add(mappingFromItem.ResizedFileHolder.DirectoryFullPath); - results.Add(new(mappingFromItem.FilePath, mappingFromItem.ResizedFileHolder.FullName)); - } - } - foreach (string directory in collection.Distinct()) - { - if (!Directory.Exists(directory)) - _ = Directory.CreateDirectory(directory); - } - return results; - } - - private static List GetPreloadIds(string destinationRoot) - { - List results = []; - string[] lines; - string preloadDirectory = Path.Combine(destinationRoot, "Preload"); - if (!Directory.Exists(preloadDirectory)) - _ = Directory.CreateDirectory(preloadDirectory); - string[] files = Directory.GetFiles(preloadDirectory, "*.lsv", SearchOption.TopDirectoryOnly); - foreach (string file in files) - { - lines = File.ReadAllLines(file); - foreach (string line in lines) - { - if (string.IsNullOrEmpty(line) || !int.TryParse(line, out int id) || id == 0) - continue; - results.Add(id); - } - } - return results; - } - } \ No newline at end of file diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index 895e4c1..94fb512 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -264,7 +264,7 @@ public class D_Face : IFaceD } } - public List GetFaces(string outputResolution, string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List? mappingFromPhotoPrismCollection) + public List GetFaces(string outputResolution, string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List? mappingFromPhotoPrismCollection) { List? results; string? json; @@ -314,7 +314,7 @@ public class D_Face : IFaceD locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum); if (results is null || locations.Count > 0) { - results = GetFaces(outputResolution, cResultsFullGroupDirectory, property, mappingFromItem, outputResolutionToResize, locations); + results = GetFaces(outputResolution, cResultsFullGroupDirectory, exifDirectory, mappingFromItem, outputResolutionToResize, locations); if (results.Count == 0) File.Move(mappingFromItem.ResizedFileHolder.FullName, $"{mappingFromItem.ResizedFileHolder.FullName}.err"); else @@ -338,7 +338,7 @@ public class D_Face : IFaceD return results; } - private List GetFaces(string outputResolution, string cResultsFullGroupDirectory, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List locations) + private List GetFaces(string outputResolution, string cResultsFullGroupDirectory, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List locations) { if (_PropertyConfiguration.NumberOfJitters is null) throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); @@ -374,7 +374,7 @@ public class D_Face : IFaceD 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)); + results.Add(new(exifDirectory, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null)); else { double[] rawEncoding; @@ -382,7 +382,7 @@ public class D_Face : IFaceD Shared.Models.FaceEncoding convertedFaceEncoding; foreach ((Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary? faceParts) in collection) { - face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location); + face = new(exifDirectory, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location); if (faceEncoding is not null) { rawEncoding = faceEncoding.GetRawEncoding(); diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index b3d030b..26d9e84 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -25,6 +25,13 @@ namespace View_by_Distance.Instance; public partial class DlibDotNet : IDlibDotNet, IDisposable { + public record Record(ReadOnlyDictionary ExifDirectoriesById, + string FilesCollectionRootDirectory, + bool FilesCollectionCountIsOne, + ReadOnlyCollection> FilePathsCollection, + ReadOnlyDictionary>? IdToFilePaths, + ReadOnlyDictionary? SplatNineIdentifiers); + private readonly D_Face _Faces; private ProgressBar? _ProgressBar; private readonly C_Resize _Resize; @@ -40,6 +47,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable private readonly DistanceLimits _DistanceLimits; private readonly bool _PropertyRootExistedBefore; private readonly Models.Configuration _Configuration; + private readonly ProgressBarOptions _ProgressBarOptions; private readonly bool _ArgZeroIsConfigurationRootDirectory; private readonly Map.Models.Configuration _MapConfiguration; private readonly List<(string Directory, long PersonKey)> _JLinkResolvedDirectories; @@ -56,15 +64,16 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { string message; _Logger = logger; + _Exceptions = []; _Console = console; _AppSettings = appSettings; _IsEnvironment = isEnvironment; long ticks = DateTime.Now.Ticks; - _Exceptions = []; _JLinkResolvedDirectories = []; if (ticks.ToString().Last() == '0') ticks += 1; ReadOnlyCollection personContainers; + _ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); Models.Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); _Logger?.LogInformation(propertyConfiguration.RootDirectory); @@ -119,8 +128,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); message = $") Building People Collection - {totalSeconds} total second(s)"; - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(1, message, options); + using ProgressBar progressBar = new(1, message, _ProgressBarOptions); progressBar.Tick(); string peopleRootDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(A2_People)); string? rootResultsDirectory = Path.GetDirectoryName(Path.GetDirectoryName(peopleRootDirectory)) ?? throw new Exception(); @@ -165,8 +173,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable _Logger?.LogInformation("First run completed. Run again if wanted"); } - private int FullParallelForWork(A_Property propertyLogic, - B_Metadata metadata, + private int FullParallelForWork(B_Metadata metadata, MapLogic mapLogic, string outputResolution, bool outputResolutionHasNumber, @@ -174,59 +181,47 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory, List> sourceDirectoryChanges, Dictionary> fileNameToCollection, + Record record, Container.Models.Container container, Item item, DateTime[] containerDateTimes, bool? isFocusRelativePath) { int result = 0; + bool? shouldIgnore; List faces; long ticks = DateTime.Now.Ticks; DateTime dateTime = DateTime.Now; - Shared.Models.Property? property; List parseExceptions = []; string[] changesFrom = [nameof(A_Property)]; List> subFileTuples = []; FileHolder resizedFileHolder = _Resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber); - if (item.Property is null || item.Property.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) + if (item.ExifDirectory is null || item.ExifDirectory.FilePath.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) + throw new Exception(); + if (_Configuration.PropertyConfiguration.ForcePropertyLastWriteTimeToCreationTime && item.SourceDirectoryFileHolder.LastWriteTime.Value != item.SourceDirectoryFileHolder.CreationTime.Value) { - LogItemPropertyIsNull(item); - int? propertyHashCode = item.Property?.GetHashCode(); - property = propertyLogic.GetProperty(metadata, item, subFileTuples, parseExceptions); - item.Update(property); - if (propertyHashCode is null) - { - lock (sourceDirectoryChanges) - sourceDirectoryChanges.Add(new Tuple(nameof(A_Property), DateTime.Now)); - } - else if (propertyHashCode.Value != property.GetHashCode()) - { - lock (sourceDirectoryChanges) - sourceDirectoryChanges.Add(new Tuple(nameof(A_Property), DateTime.Now)); - } + File.SetLastWriteTime(item.SourceDirectoryFileHolder.FullName, item.SourceDirectoryFileHolder.CreationTime.Value); + subFileTuples.Add(new Tuple(nameof(A_Property), item.SourceDirectoryFileHolder.CreationTime.Value)); + } + else if (item.SourceDirectoryFileHolder.LastWriteTime is not null) + subFileTuples.Add(new Tuple(nameof(A_Property), item.SourceDirectoryFileHolder.LastWriteTime.Value)); + else + subFileTuples.Add(new Tuple(nameof(A_Property), new FileInfo(item.SourceDirectoryFileHolder.FullName).LastWriteTime)); + if (resizedFileHolder.Exists && item.ExifDirectory.Width is not null && item.ExifDirectory.Width.Value > 4 && _Configuration.SaveBlurHashForOutputResolutions.Contains(outputResolution)) + { + string? file = _BlurHasher.GetFile(item.FilePath); + if (file is not null && !File.Exists(file)) + _ = _BlurHasher.EncodeAndSave(item.FilePath, resizedFileHolder); + } + if (item.FilePath.Id is null || !record.ExifDirectoriesById.TryGetValue(item.FilePath.Id.Value, out ExifDirectory? exifDirectory)) + { + shouldIgnore = null; + exifDirectory = null; } else { - property = item.Property; - if (_Configuration.PropertyConfiguration.ForcePropertyLastWriteTimeToCreationTime && item.SourceDirectoryFileHolder.LastWriteTime.Value != item.SourceDirectoryFileHolder.CreationTime.Value) - { - File.SetLastWriteTime(item.SourceDirectoryFileHolder.FullName, item.SourceDirectoryFileHolder.CreationTime.Value); - subFileTuples.Add(new Tuple(nameof(A_Property), item.SourceDirectoryFileHolder.CreationTime.Value)); - } - else if (item.SourceDirectoryFileHolder.LastWriteTime is not null) - subFileTuples.Add(new Tuple(nameof(A_Property), item.SourceDirectoryFileHolder.LastWriteTime.Value)); - else - subFileTuples.Add(new Tuple(nameof(A_Property), new FileInfo(item.SourceDirectoryFileHolder.FullName).LastWriteTime)); - if (resizedFileHolder.Exists && item.Property.Width is not null && item.Property.Width.Value > 4 && _Configuration.SaveBlurHashForOutputResolutions.Contains(outputResolution)) - { - string? file = _BlurHasher.GetFile(item.FilePath); - if (file is not null && !File.Exists(file)) - _ = _BlurHasher.EncodeAndSave(item.FilePath, resizedFileHolder); - } - } - bool? shouldIgnore = property is null || property.Keywords is null ? null : _Configuration.PropertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l)); - if (shouldIgnore is not null) - { + ReadOnlyCollection keywords = IMetaBase.GetKeywords(exifDirectory.ExifBaseDirectories); + shouldIgnore = _Configuration.PropertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains); if (shouldIgnore.Value) { FileInfo fileInfo = new(resizedFileHolder.FullName); @@ -241,11 +236,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable throw new NotSupportedException($"Rename File! <{item.FilePath.FileNameFirstSegment}>"); } } - if (property is null || item.Property is null) - throw new NullReferenceException(nameof(property)); item.SetResizedFileHolder(_Resize.FileNameExtension, resizedFileHolder); MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); - ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); + exifDirectory ??= metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); if (_AppSettings.Places.Count > 0) { float latitude; @@ -268,13 +261,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable distance += 1; } } - Dictionary outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.Property, mappingFromItem); + Dictionary outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem); if (_Configuration.SaveResizedSubfiles) { if (shouldIgnore is not null && item.FilePath.HasIgnoreKeyword is not null && item.FilePath.HasIgnoreKeyword.Value != shouldIgnore.Value) faces = []; else - _Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize); + _Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize); } if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) faces = []; @@ -286,7 +279,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection)) mappingFromPhotoPrismCollection = null; bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution); - faces = _Faces.GetFaces(outputResolution, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection); + faces = _Faces.GetFaces(outputResolution, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection); result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces); List<(Shared.Models.Face, FileHolder?, string, bool Saved)> faceCollection = _Faces.SaveFaces(item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces); if (move && faceCollection.All(l => !l.Saved)) @@ -319,7 +312,6 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } private (int, bool) FullParallelWork(int maxDegreeOfParallelism, - A_Property propertyLogic, B_Metadata metadata, MapLogic mapLogic, string outputResolution, @@ -328,6 +320,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory, List> sourceDirectoryChanges, Dictionary> fileNameToCollection, + Record record, Container.Models.Container container, ReadOnlyCollection filteredItems, string message) @@ -336,17 +329,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable int exceptionsCount = 0; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; DateTime[] containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; string focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory)); bool? isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath); bool anyPropertiesChangedForX = _Configuration.PropertyConfiguration.PropertiesChangedForProperty || _Configuration.PropertiesChangedForDistance || _Configuration.PropertiesChangedForFaces || _Configuration.PropertiesChangedForIndex || _Configuration.PropertiesChangedForMetadata || _Configuration.PropertiesChangedForResize; - using ProgressBar progressBar = new(filteredItems.Count, message, options); + using ProgressBar progressBar = new(filteredItems.Count, message, _ProgressBarOptions); _ = Parallel.For(0, filteredItems.Count, parallelOptions, (i, state) => { try { - result += FullParallelForWork(propertyLogic, - metadata, + result += FullParallelForWork(metadata, mapLogic, outputResolution, outputResolutionHasNumber, @@ -354,6 +345,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable d2ResultsFullGroupDirectory, sourceDirectoryChanges, fileNameToCollection, + record, container, filteredItems[i], containerDateTimes, @@ -371,6 +363,98 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return (result, exceptionsCount > 0); } + private void FullDoWork(string argZero, + string propertyRoot, + long ticks, + string fPhotoPrismSingletonDirectory, + int count, + B_Metadata metadata, + Record record, + ReadOnlyCollection readOnlyContainers, + 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}"; + if (outputResolutionHasNumber) + _Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory); + (notMapped, exceptions) = FullParallelWork(maxDegreeOfParallelism, + metadata, + mapLogic, + outputResolution, + outputResolutionHasNumber, + cResultsFullGroupDirectory, + d2ResultsFullGroupDirectory, + sourceDirectoryChanges, + fileNameToCollection, + record, + 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}"; + using ProgressBar progressBar = new(1, message, _ProgressBarOptions); + progressBar.Tick(); + } + } + void IDlibDotNet.Tick() => _ProgressBar?.Tick(); @@ -399,6 +483,12 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory); } + void IDlibDotNet.ConstructProgressBar(int maxTicks, string message) + { + _ProgressBar?.Dispose(); + _ProgressBar = new(maxTicks, message, _ProgressBarOptions); + } + (string, string, string, string) IDlibDotNet.GetResultsFullGroupDirectories(string outputResolution) { string cResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( @@ -466,7 +556,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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); + string paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, index: null); result = new(directoryNames, filePath.ExtensionLowered, filePath.HasDateTimeOriginal, filePath.Id.Value, filePath.Length, paddedId, filePath.LastWriteTicks); return result; } @@ -513,6 +603,22 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable throw new NullReferenceException(nameof(configuration.ModelDirectory)); } + private static ExifDirectory? GetExifDirectory(FilePair filePair) + { + ExifDirectory? result; + if (filePair.Match is null) + result = null; + else + { + string json = File.ReadAllText(filePair.Match.FullName); + if (string.IsNullOrEmpty(json)) + result = null; + else + result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + } + return result; + } + private static void DeleteContinueFiles(ReadOnlyCollection personContainers) { foreach (PersonContainer personContainer in personContainers) @@ -554,7 +660,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } } - private static bool IsFilesCollectionCountIsOne(ReadOnlyCollection> filePathsCollection) + private static bool GetFilesCollectionCountIsOne(ReadOnlyCollection> filePathsCollection) { bool result = true; int count = 0; @@ -582,11 +688,11 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable foreach (Item item in distinctFilteredItems) { found = false; - if (item.Property?.Id is null) + if (item.ExifDirectory?.FilePath.Id is null) continue; foreach (Mapping mapping in distinctFilteredMappingCollection) { - if (mapping.MappingFromItem.Id != item.Property.Id.Value) + if (mapping.MappingFromItem.Id != item.ExifDirectory.FilePath.Id.Value) continue; found = true; break; @@ -601,13 +707,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string fileName; string directory; bool? isWrongYear; - List dateTimes; + DateTime? dateTime; + string? exifDirectoryModel; List distinct = []; WindowsShortcut windowsShortcut; + ReadOnlyCollection dateTimes; List<(string, string, string)> collection = []; foreach (Item item in distinctValidImageItems) { - if (item.Property?.Id is null) + if (item.ExifDirectory?.FilePath.Id is null) continue; if (item.IsNotUniqueAndNeedsReview is null || !item.IsNotUniqueAndNeedsReview.Value) continue; @@ -620,15 +728,19 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } foreach (Item item in distinctValidImageItems) { - if (item.Property?.Id is null || item.Property.DateTimeOriginal is null) + if (item.ExifDirectory?.FilePath.Id is null) continue; - dateTimes = item.Property.GetDateTimes(); - (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes); + dateTime = IDate.GetDateTimeOriginal(item.ExifDirectory); + if (dateTime is null) + continue; + dateTimes = IDate.GetDateTimes(item.ExifDirectory); + (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, dateTime, dateTimes.ToList()); 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); + exifDirectoryModel = IMetaBase.GetModel(item.ExifDirectory?.ExifBaseDirectories); + model = string.IsNullOrEmpty(exifDirectoryModel) ? "Unknown" : CameraRegex().Replace(exifDirectoryModel.Trim(), "_"); + directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Item)})", dateTime.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)) @@ -797,15 +909,19 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return results; } - private bool? GetIsFocusModel(Shared.Models.Property? property) + private bool? GetIsFocusModel(ExifDirectory? exifDirectory) { 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); + { + string? model = IMetaBase.GetModel(exifDirectory?.ExifBaseDirectories); + if (exifDirectory is null || string.IsNullOrEmpty(model)) + result = null; + else + result = model.Contains(_Configuration.FocusModel); + } return result; } @@ -867,7 +983,39 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return result; } - private bool GetRunToDoCollectionFirst(Models.Configuration configuration, long ticks) + 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 ParallelFor(IDlibDotNet dlibDotNet, FilePair filePair, Dictionary results) + { + dlibDotNet?.Tick(); + if (filePair.FilePath.Id is null || results.ContainsKey(filePair.FilePath.Id.Value)) + return; + ExifDirectory? exifDirectory = GetExifDirectory(filePair); + if (exifDirectory is null) + return; + lock (results) + results.Add(filePair.FilePath.Id.Value, exifDirectory); + } + + private bool GetRunToDoCollectionFirst(Models.Configuration configuration, long ticks, string[] checkDirectories) { bool result = configuration.SaveSortingWithoutPerson; if (!result) @@ -887,20 +1035,16 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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 (!Directory.Exists(checkDirectory)) + result = true; 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); + result = true; if (result) continue; directories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly); @@ -923,32 +1067,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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; + Record? record = null; + string seasonDirectory; A_Property propertyLogic; IDlibDotNet dlibDotNet = this; + DateTime dateTime = new(ticks); string eDistanceContentDirectory; string? a2PeopleContentDirectory; string aResultsFullGroupDirectory; @@ -957,23 +1084,24 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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); + (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); + string[] checkDirectories = + [ + Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Ancestry"), + Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Facebook"), + Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "LinkedIn") + ]; + bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks, checkDirectories); (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())); + B_Metadata metadata = new(dlibDotNet, _Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, ticks, bResultsFullGroupDirectory); if (runToDoCollectionFirst) mapLogic = null; else @@ -985,18 +1113,16 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable configurationOutputResolutionsHas = true; if (!runToDoCollectionFirst) break; - (filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne) = GetFilesCollectionThenCopyOrMove(ticks, fileSearchFilter, directorySearchFilter, options, outputResolution); - keyValuePairs = FilePath.GetKeyValuePairs(filePathsCollection); - splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, keyValuePairs); + record = GetFilesCollectionThenCopyOrMove(dlibDotNet, ticks, fileSearchFilter, directorySearchFilter, bResultsFullGroupDirectory, outputResolution); 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 (record is not null && record.FilesCollectionCountIsOne) { - if (filePathsCollection is null) - throw new NullReferenceException(nameof(filePathsCollection)); + if (record.FilePathsCollection is null) + throw new NullReferenceException(nameof(record.FilePathsCollection)); string resultsGroupDirectory; a2PeopleContentDirectory = null; eDistanceContentDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultContent); @@ -1007,7 +1133,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable resultsGroupDirectory = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, string.Empty, create: true); _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(resultsGroupDirectory); } - argZero = SaveUrlAndGetNewRootDirectory(filePathsCollection.First()); + argZero = SaveUrlAndGetNewRootDirectory(record.FilePathsCollection.First()); _Configuration.PropertyConfiguration.ChangeRootDirectory(argZero); (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); propertyRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), create: false); @@ -1019,24 +1145,55 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { if (outputResolution.Any(char.IsNumber)) continue; + Dictionary exifDirectoriesById = []; (cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); - filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useCeilingAverage: true); + string? filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); + ReadOnlyCollection>? filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: true); + record = new(FilesCollectionRootDirectory: filesCollectionRootDirectory, + FilesCollectionCountIsOne: false, + FilePathsCollection: filePathsCollection, + ExifDirectoriesById: new(exifDirectoriesById), + IdToFilePaths: null, + SplatNineIdentifiers: null); break; } } - if (filesCollectionRootDirectory is null || filePathsCollection is null) - throw new NullReferenceException(nameof(filePathsCollection)); - int count = filePathsCollection.Select(l => l.Count).Sum(); + if (string.IsNullOrEmpty(record?.FilesCollectionRootDirectory) || record.FilePathsCollection.Count == 0) + throw new NullReferenceException(nameof(record.FilePathsCollection)); + foreach (string checkDirectory in checkDirectories) + { + seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}"); + if (!Directory.Exists(seasonDirectory)) + _ = Directory.CreateDirectory(seasonDirectory); + } + int count = record.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 = new(count, message, _ProgressBarOptions); + ReadOnlyCollection readOnlyContainers = + Container.Models.Stateless.Methods.IContainer.GetContainers(dlibDotNet, + _Configuration.PropertyConfiguration, + _Faces.FileNameExtension, + _Faces.HiddenFileNameExtension, + eDistanceContentDirectory, + record.FilesCollectionRootDirectory, + record.IdToFilePaths, + record.SplatNineIdentifiers, + record.FilePathsCollection, + record.ExifDirectoriesById); _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); + MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory); + FullDoWork(argZero, + propertyRoot, + ticks, + fPhotoPrismSingletonDirectory, + count, + metadata, + record, + readOnlyContainers, + mapLogic); ReadOnlyCollection distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); if (_Configuration.LookForAbandoned) { @@ -1044,9 +1201,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory; foreach (string outputResolution in _Configuration.OutputResolutions) { - _ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), options); + _ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), _ProgressBarOptions); (cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - mapLogic.LookForAbandoned(this, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); + mapLogic.LookForAbandoned(dlibDotNet, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); _ProgressBar.Dispose(); } } @@ -1077,8 +1234,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable && _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 (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && record?.SplatNineIdentifiers is not null && distinctValidImageMappingCollection.Count > 0) + _Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.ImmichOwnerId, _Configuration.ImmichRoot, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, record.SplatNineIdentifiers, distinctValidImageMappingCollection); if (_IsEnvironment.Development) continue; if (!_IsEnvironment.Development) @@ -1096,19 +1253,37 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } } - private (string, ReadOnlyCollection>, bool) GetFilesCollectionThenCopyOrMove(long ticks, string fileSearchFilter, string directorySearchFilter, ProgressBarOptions options, string outputResolution) + private Record GetFilesCollectionThenCopyOrMove(IDlibDotNet dlibDotNet, long ticks, string fileSearchFilter, string directorySearchFilter, string bResultsFullGroupDirectory, string outputResolution) { + Record result; + int count; + string message; ProgressBar progressBar; - IDlibDotNet dlibDotNet = this; + const string extension = ".json"; + ReadOnlyCollection filePairs; + int maxDegreeOfParallelism = Environment.ProcessorCount; + Dictionary exifDirectoriesById = []; string filesCollectionRootDirectory = _Configuration.PropertyConfiguration.RootDirectory; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; (string cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - IReadOnlyDictionary>> fileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, [_Configuration.PropertyConfiguration.ResultContent]); - ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useCeilingAverage: false); - int count = filePathsCollection.Select(l => l.Count).Sum(); - bool filesCollectionCountIsOne = IsFilesCollectionCountIsOne(filePathsCollection); - string message = $") Selecting for ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - progressBar = new(count, message, options); - (string[] distinctDirectories, List<(FilePath, string)> toDoCollection) = IDirectory.GetToDoCollection(_Configuration.PropertyConfiguration, filePathsCollection, fileGroups, () => progressBar.Tick()); + string jsonGroupDirectory = Path.Combine(bResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton); + if (!Directory.Exists(jsonGroupDirectory)) + _ = Directory.CreateDirectory(jsonGroupDirectory); + IReadOnlyDictionary>> keyValuePairs = + Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, [_Configuration.PropertyConfiguration.ResultContent]); + ReadOnlyDictionary> fileGroups = keyValuePairs[_Configuration.PropertyConfiguration.ResultContent]; + ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: false); + count = filePathsCollection.Select(l => l.Count).Sum(); + filePairs = IFilePair.GetFilePairs(_Configuration.PropertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection); + message = $") Preloading ExifDirectory Dictionary - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; + progressBar = new(count, message, _ProgressBarOptions); + _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(dlibDotNet, filePairs[i], exifDirectoriesById)); + progressBar.Dispose(); + count = filePathsCollection.Select(l => l.Count).Sum(); + bool filesCollectionCountIsOne = GetFilesCollectionCountIsOne(filePathsCollection); + message = $") Selecting for ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; + progressBar = new(count, message, _ProgressBarOptions); + (string[] distinctDirectories, List<(FilePath, string)> toDoCollection) = IDirectory.GetToDoCollection(_Configuration.PropertyConfiguration, filePathsCollection, fileGroups, exifDirectoriesById, () => progressBar.Tick()); progressBar.Dispose(); foreach (string distinctDirectory in distinctDirectories) { @@ -1116,13 +1291,21 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable _ = Directory.CreateDirectory(distinctDirectory); } message = $") Copying to ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - progressBar = new(count, message, options); + progressBar = new(count, message, _ProgressBarOptions); _ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, () => progressBar.Tick()); progressBar.Dispose(); - return (filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne); + ReadOnlyDictionary> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection); + ReadOnlyDictionary splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, idToFilePaths); + result = new(ExifDirectoriesById: new(exifDirectoriesById), + FilesCollectionRootDirectory: filesCollectionRootDirectory, + FilesCollectionCountIsOne: filesCollectionCountIsOne, + FilePathsCollection: filePathsCollection, + IdToFilePaths: idToFilePaths, + SplatNineIdentifiers: splatNineIdentifiers); + return result; } - private void MapFaceFileLogic(long ticks, ReadOnlyCollection personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory, ProgressBarOptions options) + private void MapFaceFileLogic(long ticks, ReadOnlyCollection personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory) { foreach (string outputResolution in _Configuration.OutputResolutions) { @@ -1140,7 +1323,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable continue; if (!_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) continue; - List saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, options, mapLogic, outputResolution); + List saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, mapLogic, outputResolution); if (saveContainers.Count > 0) { int updated = 0; @@ -1150,7 +1333,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } } - private List GetSaveContainers(long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, ProgressBarOptions options, MapLogic mapLogic, string outputResolution) + private List GetSaveContainers(long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, MapLogic mapLogic, string outputResolution) { List results; IDlibDotNet dlibDotNet = this; @@ -1179,8 +1362,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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 = new(postFiltered.Count, message, _ProgressBarOptions); + ReadOnlyCollection matrix = E_Distance.GetMatrixLocationContainers(dlibDotNet, _MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered); _ProgressBar.Dispose(); ReadOnlyDictionary onlyOne = GetOnlyOne(distanceLimits, matrix); if (onlyOne.Count == 0) @@ -1195,92 +1378,6 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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 = []; @@ -1306,14 +1403,14 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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) + if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) continue; mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, item.ResizedFileHolder); if (distinctItems) { - if (distinct.Contains(item.Property.Id.Value)) + if (distinct.Contains(item.ExifDirectory.FilePath.Id.Value)) continue; - distinct.Add(item.Property.Id.Value); + distinct.Add(item.ExifDirectory.FilePath.Id.Value); } count++; anyValidFaces = false; @@ -1345,7 +1442,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable bool? eyeReview = null; int confidencePercent = 0; int faceAreaPermyriad = 0; - bool? isFocusModel = GetIsFocusModel(item.Property); + bool? isFocusModel = GetIsFocusModel(item.ExifDirectory); 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); @@ -1501,12 +1598,12 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable MappingFromLocation? mappingFromLocation; MappingFromFilterPre mappingFromFilterPre; MappingFromFilterPost mappingFromFilterPost; - bool? isFocusModel = GetIsFocusModel(item.Property); - ReadOnlyDictionary>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.Property?.Id); + bool? isFocusModel = GetIsFocusModel(item.ExifDirectory); + ReadOnlyDictionary>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.ExifDirectory?.FilePath.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) + if (item.ExifDirectory?.FilePath.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) { canReMap = null; isFocusPerson = null; @@ -1529,7 +1626,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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); + inSkipCollection = mapLogic.InSkipCollection(item.ExifDirectory.FilePath.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); diff --git a/Instance/Instance.csproj b/Instance/Instance.csproj index 1aaef30..5b0b651 100644 --- a/Instance/Instance.csproj +++ b/Instance/Instance.csproj @@ -55,7 +55,6 @@ - diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 975b1b6..08ecd54 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -320,12 +320,12 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic public ReadOnlyCollection GetLocationContainers(Item item) { LocationContainer[] results; - if (item.Property?.Id is null) + if (item.ExifDirectory?.FilePath.Id is null) results = []; else { List? locationContainers; - if (_IdToLocationContainers.TryGetValue(item.Property.Id.Value, out locationContainers)) + if (_IdToLocationContainers.TryGetValue(item.ExifDirectory.FilePath.Id.Value, out locationContainers)) results = locationContainers.ToArray(); else results = []; diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index 6846fd8..13b5880 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -110,7 +110,7 @@ internal abstract class MapLogic List results = []; foreach (Item item in items) { - if (item.Property?.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) continue; foreach (Face face in item.Faces) { diff --git a/Metadata/Models/B_Metadata.cs b/Metadata/Models/B_Metadata.cs index dae1ad5..16ff588 100644 --- a/Metadata/Models/B_Metadata.cs +++ b/Metadata/Models/B_Metadata.cs @@ -4,6 +4,7 @@ using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; using View_by_Distance.Shared.Models.Properties; +using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Metadata.Models; @@ -13,30 +14,28 @@ namespace View_by_Distance.Metadata.Models; public class B_Metadata : IMetadata { + public string DateGroupDirectory { get; init; } + public ReadOnlyCollection Collection { get; private set; } + public ReadOnlyDictionary> SingletonById { get; private set; } + public ReadOnlyDictionary ExifDirectoriesById { get; private set; } + + // First + // Set DateGroupDirectory + // Create a directories for Singleton + // Populate Collection + // Populate Singleton + // Populate existing ExifDirectories + // Run similar for resize + // Second + // Populate needed ExifDirectories Dictionary can't be init only + private readonly bool _PropertiesChangedForMetadata; private readonly IPropertyConfiguration _PropertyConfiguration; private readonly bool _ForceMetadataLastWriteTimeToCreationTime; private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions; private readonly ReadOnlyDictionary>[] _ResultSingletonFileGroups; - public B_Metadata(IPropertyConfiguration propertyConfiguration) - { - _PropertiesChangedForMetadata = false; - _PropertyConfiguration = propertyConfiguration; - _ForceMetadataLastWriteTimeToCreationTime = false; - _ResultSingletonFileGroups = [new(new Dictionary>())]; - _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; - ReadOnlyDictionary>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, null, [propertyConfiguration.ResultSingleton]); - foreach (KeyValuePair>> keyValuePair in keyValuePairs) - { - if (keyValuePair.Key == _PropertyConfiguration.ResultSingleton) - _ResultSingletonFileGroups[0] = keyValuePair.Value; - else - throw new Exception(); - } - } - - public B_Metadata(IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, string bResultsFullGroupDirectory) + public B_Metadata(IDlibDotNet? dlibDotNet, IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, long ticks, string bResultsFullGroupDirectory) { _PropertyConfiguration = propertyConfiguration; _PropertiesChangedForMetadata = propertiesChangedForMetadata; @@ -51,31 +50,68 @@ public class B_Metadata : IMetadata else throw new Exception(); } - } - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, _WriteIndentedJsonSerializerOptions); - return result; - } - - 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 && System.IO.Directory.Exists(checkDirectory)) + List results = []; + string jsonGroupDirectory; + const string extension = ".json"; + const string fileSearchFilter = "*"; + string filesCollectionRootDirectory; + const string directorySearchFilter = "*"; + int maxDegreeOfParallelism = Environment.ProcessorCount; + filesCollectionRootDirectory = propertyConfiguration.RootDirectory; + Dictionary exifDirectoriesById = []; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + string jsonGroupSingletonDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + string jsonGroupCollectionDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultCollection); + ReadOnlyCollection directories = new([jsonGroupSingletonDirectory, jsonGroupCollectionDirectory]); + Shared.Models.Stateless.Methods.IPath.CreateDirectories(directories); + ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: false); + ReadOnlyDictionary> fileNamesToFiles = FilePath.GetFilesKeyValuePairs(filePathsCollection); + ReadOnlyCollection filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupSingletonDirectory, filePathsCollection, fileNamesToFiles); + string message = $") Preloading ExifDirectory Dictionary - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; + dlibDotNet?.ConstructProgressBar(filePairs.Count, message); + _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(dlibDotNet, filePairs[i], results)); + jsonGroupDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultCollection); + foreach (ExifDirectory exifDirectory in results) { - string checkFile = Path.Combine(checkDirectory, fileName); - if (File.Exists(checkFile)) - { - File.Move(checkFile, fileInfo.FullName); - fileInfo.Refresh(); - } + if (exifDirectory.FilePath.Id is null || exifDirectoriesById.ContainsKey(exifDirectory.FilePath.Id.Value)) + continue; + exifDirectoriesById.Add(exifDirectory.FilePath.Id.Value, exifDirectory); } + ExifDirectoriesById = new(exifDirectoriesById); + DateGroupDirectory = bResultsFullGroupDirectory; + SingletonById = fileNamesToFiles; + filesCollectionRootDirectory = jsonGroupCollectionDirectory; + ReadOnlyCollection> filePathsSingletonCollection = IDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, + useIgnoreExtensions: false, useCeilingAverage: false); + Collection = filePathsSingletonCollection[0]; + } + + private void ParallelFor(IDlibDotNet? dlibDotNet, FilePair filePair, List results) + { + dlibDotNet?.Tick(); + if (filePair.FilePath.Id is null) + return; + ExifDirectory? exifDirectory = GetExifDirectory(filePair); + if (exifDirectory is null) + return; + lock (results) + results.Add(exifDirectory); + } + + private static ExifDirectory? GetExifDirectory(FilePair filePair) + { + ExifDirectory? result; + if (filePair.Match is null) + result = null; + else + { + string json = File.ReadAllText(filePair.Match.FullName); + if (string.IsNullOrEmpty(json)) + result = null; + else + result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + } + return result; } public ExifDirectory GetMetadataCollection(FilePath filePath, List> subFileTuples, List parseExceptions, string[] changesFrom, MappingFromItem mappingFromItem) @@ -142,6 +178,25 @@ public class B_Metadata : IMetadata return result; } + 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 && System.IO.Directory.Exists(checkDirectory)) + { + string checkFile = Path.Combine(checkDirectory, fileName); + if (File.Exists(checkFile)) + { + File.Move(checkFile, fileInfo.FullName); + fileInfo.Refresh(); + } + } + } + (DateTime?, DateTime?[]) IMetadata.GetDateTimes(FilePath filePath, IReadOnlyList directories) { List results = []; diff --git a/Property/Models/A_Property.cs b/Property/Models/A_Property.cs index 64fb431..dd3826e 100644 --- a/Property/Models/A_Property.cs +++ b/Property/Models/A_Property.cs @@ -66,6 +66,7 @@ public class A_Property converted: false)); } + [Obsolete("Use ExifDirectory")] public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata metadata, int t, Container.Models.Container[] containers) { int total = 0; @@ -111,6 +112,7 @@ public class A_Property SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); } + [Obsolete("Use ExifDirectory")] private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata metadata, List exceptions, List> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection items, string message) { List> sourceDirectoryFileTuples = []; @@ -140,6 +142,7 @@ public class A_Property }); } + [Obsolete("Use ExifDirectory")] private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata metadata, string sourceDirectory, List> sourceDirectoryFileTuples, List> sourceDirectoryChanges, Item item) { Shared.Models.Property property; @@ -148,16 +151,17 @@ public class A_Property 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) + if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.ExifDirectory 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); + item.Update(null); } } + [Obsolete("Use ExifDirectory")] private Shared.Models.Property GetImageProperty(Shared.Models.Methods.IMetadata metadata, Item item, List> sourceDirectoryFileTuples, List parseExceptions, bool isIgnoreExtension) { Shared.Models.Property? result; @@ -202,8 +206,8 @@ public class A_Property json = File.ReadAllText(fileInfo.FullName); try { - if (item.Property is not null) - result = item.Property; + if (item.ExifDirectory is not null) + result = null; else result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); if (result is not null && json.Contains("WrongYear")) @@ -301,6 +305,7 @@ public class A_Property } } + [Obsolete("Use ExifDirectory")] public Shared.Models.Property GetProperty(Shared.Models.Methods.IMetadata metadata, Item item, List> sourceDirectoryFileTuples, List parseExceptions) { Shared.Models.Property result; diff --git a/Property/Models/Stateless/Property.cs b/Property/Models/Stateless/Property.cs index c7ca681..1ca687b 100644 --- a/Property/Models/Stateless/Property.cs +++ b/Property/Models/Stateless/Property.cs @@ -21,9 +21,9 @@ internal partial class Property List ticksCollection = []; foreach (Item item in container.Items) { - if (item.Property is null) + if (item.ExifDirectory is null) continue; - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); + minimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(item.ExifDirectory); if (minimumDateTime is null) continue; ticksCollection.Add(minimumDateTime.Value.Ticks); @@ -108,12 +108,12 @@ internal partial class Property format = dateFormat[1]; length = dateFormat[0].Length + dateFormat[1].Length; for (int i = dateFormat[0].Length; i < length; i++) - _ = value.Append(filePath.NameWithoutExtension[i]); + _ = value.Append(filePath.FileNameFirstSegment[i]); if (value.Length != format.Length) continue; if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime)) { - if (filePath.NameWithoutExtension.Length < ticksExample.Length || !long.TryParse(filePath.NameWithoutExtension[^ticksExample.Length..], out long ticks)) + if (filePath.NameWithoutExtension.Length < ticksExample.Length || !long.TryParse(filePath.FileNameFirstSegment[^ticksExample.Length..], out long ticks)) result = checkDateTime; else result = new DateTime(ticks); @@ -195,17 +195,17 @@ internal partial class Property { ticks = null; item = container.Items[j]; - if (item.Property is null) + if (item.ExifDirectory is null) continue; - minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); + minimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(item.ExifDirectory); if (minimumDateTime is null) continue; for (int k = j + 1; k < container.Items.Count; k++) { nextItem = container.Items[k]; - if (nextItem.Property is null) + if (nextItem.ExifDirectory is null) continue; - nextMinimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(nextItem.Property); + nextMinimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(nextItem.ExifDirectory); if (nextMinimumDateTime is null) continue; ticks = nextMinimumDateTime.Value.Ticks; diff --git a/Resize/Models/_C_Resize.cs b/Resize/Models/_C_Resize.cs index 905288b..26145bd 100644 --- a/Resize/Models/_C_Resize.cs +++ b/Resize/Models/_C_Resize.cs @@ -191,7 +191,7 @@ public class C_Resize public FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, Item item, bool outputResolutionHasNumber) => GetResizedFileHolder(cResultsFullGroupDirectory, item.FilePath, outputResolutionHasNumber, item.FilePath.Name); - public Dictionary GetResizeKeyValuePairs(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, ExifDirectory exifDirectory, MappingFromItem mappingFromItem) { Dictionary? results; string json; @@ -238,7 +238,7 @@ public class C_Resize } if (results is null) { - results = GetImageResizes(property); + results = GetImageResizes(exifDirectory); json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions); bool updateDateWhenMatches = dateTimes.Count != 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime; DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); @@ -276,7 +276,7 @@ public class C_Resize } } - private Dictionary GetImageResizes(Shared.Models.Property property) + private Dictionary GetImageResizes(ExifDirectory exifDirectory) { Dictionary results = []; int[] desired; @@ -284,13 +284,13 @@ public class C_Resize 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) + int? orientation = Shared.Models.Stateless.Methods.IMetaBase.GetOrientation(exifDirectory.ExifBaseDirectories); + if (exifDirectory is null || orientation is null || exifDirectory.Width is null || exifDirectory.Height is null) throw new NotSupportedException(); - checkWidth = property.Width.Value; - checkHeight = property.Height.Value; + checkWidth = exifDirectory.Width.Value; + checkHeight = exifDirectory.Height.Value; if (!_ValidResolutions.Contains(_Original)) - results.Add(_Original, [checkWidth, checkHeight, orientation]); + results.Add(_Original, [checkWidth, checkHeight, orientation.Value]); foreach (string validResolution in _ValidResolutions) { if (validResolution == _Original) @@ -305,22 +305,22 @@ public class C_Resize desiredHeight = desired[1]; } if (checkWidth <= desiredWidth && checkHeight <= desiredHeight) - results.Add(validResolution, [checkWidth, checkHeight, orientation]); + results.Add(validResolution, [checkWidth, checkHeight, orientation.Value]); else { if (desiredWidth != desiredHeight) { if (checkWidth * desiredHeight > desiredWidth * checkHeight) - results.Add(validResolution, [desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth), orientation]); + results.Add(validResolution, [desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth), orientation.Value]); else - results.Add(validResolution, [Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight, orientation]); + results.Add(validResolution, [Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight, orientation.Value]); } else { if (checkWidth * desiredHeight <= desiredWidth * checkHeight) - results.Add(validResolution, [desiredWidth, desiredHeight, orientation, desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth)]); + results.Add(validResolution, [desiredWidth, desiredHeight, orientation.Value, desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth)]); else - results.Add(validResolution, [desiredWidth, desiredHeight, orientation, Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight]); + results.Add(validResolution, [desiredWidth, desiredHeight, orientation.Value, Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight]); } } } @@ -336,7 +336,7 @@ public class C_Resize return results.ToArray(); } - public void SaveResizedSubfile(Configuration configuration, string outputResolution, string cResultsFullGroupDirectory, List> subFileTuples, Item item, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize) + public void SaveResizedSubfile(Configuration configuration, string outputResolution, string cResultsFullGroupDirectory, List> subFileTuples, Item item, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize) { if (mappingFromItem.ResizedFileHolder is null) throw new NullReferenceException(nameof(mappingFromItem.ResizedFileHolder)); @@ -378,18 +378,19 @@ public class C_Resize check = true; if (check) { - SaveResizedSubfile(property, mappingFromItem, resize); + SaveResizedSubfile(exifDirectory, 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) + private void SaveResizedSubfile(ExifDirectory exifDirectory, 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); + DateTime? dateTime = Shared.Models.Stateless.Methods.IDate.GetDateTimeOriginal(exifDirectory); + dateTime ??= Shared.Models.Stateless.Methods.IDate.GetMinimum(exifDirectory); + string dateTimeValue = dateTime.Value.ToString(dateTimeFormat); byte[] bytes = _ASCIIEncoding.GetBytes(dateTimeValue); if (_ASCIIEncoding.GetString(bytes, 0, bytes.Length) != dateTimeValue) throw new Exception(); diff --git a/Shared/Models/C_Resize.cs b/Shared/Models/C_Resize.cs new file mode 100644 index 0000000..082cfd8 --- /dev/null +++ b/Shared/Models/C_Resize.cs @@ -0,0 +1,34 @@ +using System.Collections.ObjectModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace View_by_Distance.Shared.Models; + +public record C_ResizeX(ReadOnlyDictionary> Amazon, + ReadOnlyDictionary> Content, + string OutputResolutionDirectory, + ReadOnlyDictionary> OutputResolutionToResize, + ReadOnlyDictionary> Singleton) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, C_ResizeXSourceGenerationContext.Default.C_ResizeX); + return result; + } + + // If first + // Set OutputResolutionDirectory + // Create a directories for Amazon, Content and Singleton + // Populate Amazon + // Populate Content + // Populate Singleton + // Populate existing OutputResolutionToResize + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(C_ResizeX))] +public partial class C_ResizeXSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/D2_FaceParts.cs b/Shared/Models/D2_FaceParts.cs new file mode 100644 index 0000000..acc0dc9 --- /dev/null +++ b/Shared/Models/D2_FaceParts.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace View_by_Distance.Shared.Models; + +public record D2_FacePartsX(Dictionary> Amazon, + List Collection, + Dictionary> Content, + string OutputResolutionWithDirectory) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, D2_FacePartsXSourceGenerationContext.Default.D2_FacePartsX); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(D2_FacePartsX))] +public partial class D2_FacePartsXSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/D_Face.cs b/Shared/Models/D_Face.cs new file mode 100644 index 0000000..6f15291 --- /dev/null +++ b/Shared/Models/D_Face.cs @@ -0,0 +1,24 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace View_by_Distance.Shared.Models; + +public record D_FaceX(Dictionary> Amazon, + Dictionary> Content, + Dictionary> Collection, + string OutputResolutionWithDirectory) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, D_FaceXSourceGenerationContext.Default.D_FaceX); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(D_FaceX))] +public partial class D_FaceXSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/Face.cs b/Shared/Models/Face.cs index 922641c..d8e23f3 100644 --- a/Shared/Models/Face.cs +++ b/Shared/Models/Face.cs @@ -1,3 +1,4 @@ +using System.Collections.ObjectModel; using System.Text.Json; using System.Text.Json.Serialization; @@ -33,13 +34,12 @@ public class Face : Properties.IFace _OutputResolution = outputResolution; } - public Face(Property property, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, Location? location) : + public Face(ExifDirectory exifDirectory, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, Location? location) : this(DateTime.MinValue, null, null, null, location, null, null) { - DateTime?[] dateTimes; _OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth); - dateTimes = [property.CreationTime, property.LastWriteTime, property.DateTime, property.DateTimeDigitized, property.DateTimeFromName, property.DateTimeOriginal, property.GPSDateStamp]; - _DateTime = (from l in dateTimes where l.HasValue select l.Value).Min(); + ReadOnlyCollection dateTimes = Stateless.Methods.IDate.GetDateTimes(exifDirectory); + _DateTime = dateTimes.Min(); } public override string ToString() @@ -48,12 +48,16 @@ public class Face : Properties.IFace return result; } - public void SetFaceEncoding(FaceEncoding faceEncoding) => _FaceEncoding = faceEncoding; + public void SetFaceEncoding(FaceEncoding faceEncoding) => + _FaceEncoding = faceEncoding; - public void SetFaceParts(Dictionary faceParts) => _FaceParts = faceParts; + public void SetFaceParts(Dictionary faceParts) => + _FaceParts = faceParts; - public void SetMapping(Mapping mapping) => _Mapping = mapping; + public void SetMapping(Mapping mapping) => + _Mapping = mapping; - public void SetFaceDistance(FaceDistance? faceDistance) => _FaceDistance = faceDistance; + public void SetFaceDistance(FaceDistance? faceDistance) => + _FaceDistance = faceDistance; } \ No newline at end of file diff --git a/Shared/Models/FilePair.cs b/Shared/Models/FilePair.cs index 3fbed92..065114a 100644 --- a/Shared/Models/FilePair.cs +++ b/Shared/Models/FilePair.cs @@ -3,11 +3,11 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; -public record FilePair(string Path, - bool IsUnique, - bool? IsNotUniqueAndNeedsReview, - List Collection, - string? Match) +public record FilePair(FilePath FilePath, + bool IsUnique, + bool? IsNotUniqueAndNeedsReview, + List Collection, + FilePath? Match) { public override string ToString() diff --git a/Shared/Models/FilePath.cs b/Shared/Models/FilePath.cs index cf813e2..7c41812 100644 --- a/Shared/Models/FilePath.cs +++ b/Shared/Models/FilePath.cs @@ -21,12 +21,6 @@ public record FilePath(long CreationTicks, int? SortOrder) { - public override string ToString() - { - string result = JsonSerializer.Serialize(this, FilePathSourceGenerationContext.Default.FilePath); - return result; - } - public static FilePath Get(Properties.IPropertyConfiguration propertyConfiguration, FileHolder fileHolder, int? index) { if (fileHolder.CreationTime is null) @@ -84,6 +78,28 @@ public record FilePath(long CreationTicks, return result; } + public static ReadOnlyDictionary> GetFilesKeyValuePairs(ReadOnlyCollection> filePathsCollection) + { + Dictionary> results = []; + List? collection; + foreach (ReadOnlyCollection filePaths in filePathsCollection) + { + foreach (FilePath filePath in filePaths) + { + if (filePath.Id is null) + continue; + if (!results.TryGetValue(filePath.Id.Value, out collection)) + { + results.Add(filePath.Id.Value, []); + if (!results.TryGetValue(filePath.Id.Value, out collection)) + throw new Exception(); + } + collection.Add(filePath); + } + } + return new(results); + } + public static ReadOnlyDictionary> GetKeyValuePairs(ReadOnlyCollection> filePathsCollection) { Dictionary> results = []; @@ -111,6 +127,12 @@ public record FilePath(long CreationTicks, return results.AsReadOnly(); } + public override string ToString() + { + string result = JsonSerializer.Serialize(this, FilePathSourceGenerationContext.Default.FilePath); + return result; + } + } [JsonSourceGenerationOptions(WriteIndented = true)] diff --git a/Shared/Models/Item.cs b/Shared/Models/Item.cs index e07f9af..4b95ecb 100644 --- a/Shared/Models/Item.cs +++ b/Shared/Models/Item.cs @@ -6,6 +6,7 @@ namespace View_by_Distance.Shared.Models; public class Item : Properties.IItem { + protected ExifDirectory? _ExifDirectory; protected List _Faces; protected readonly bool? _FileSizeChanged; protected readonly FilePath _FilePath; @@ -15,10 +16,10 @@ public class Item : Properties.IItem protected bool _IsValidImageFormatExtension; protected bool? _LastWriteTimeChanged; protected bool? _Moved; - protected Property? _Property; protected readonly string _RelativePath; protected FileHolder? _ResizedFileHolder; protected readonly FileHolder _SourceDirectoryFileHolder; + public ExifDirectory? ExifDirectory => _ExifDirectory; public List Faces => _Faces; public bool? FileSizeChanged => _FileSizeChanged; public FilePath FilePath => _FilePath; @@ -28,13 +29,12 @@ public class Item : Properties.IItem public bool IsValidImageFormatExtension => _IsValidImageFormatExtension; public bool? LastWriteTimeChanged => _LastWriteTimeChanged; public bool? Moved => _Moved; - public Property? Property => _Property; public string RelativePath => _RelativePath; public FileHolder? ResizedFileHolder => _ResizedFileHolder; public FileHolder SourceDirectoryFileHolder => _SourceDirectoryFileHolder; [JsonConstructor] - public Item(List faces, FilePath filePath, bool? fileSizeChanged, bool? isArchive, bool? isNotUniqueAndNeedsReview, bool isUniqueFileName, bool isValidImageFormatExtension, bool? lastWriteTimeChanged, bool? moved, Property? property, string relativePath, FileHolder? resizedFileHolder, FileHolder sourceDirectoryFileHolder) + public Item(List faces, FilePath filePath, bool? fileSizeChanged, bool? isArchive, bool? isNotUniqueAndNeedsReview, bool isUniqueFileName, bool isValidImageFormatExtension, bool? lastWriteTimeChanged, bool? moved, ExifDirectory? exifDirectory, string relativePath, FileHolder? resizedFileHolder, FileHolder sourceDirectoryFileHolder) { _Faces = faces; _FilePath = filePath; @@ -45,25 +45,28 @@ public class Item : Properties.IItem _IsValidImageFormatExtension = isValidImageFormatExtension; _LastWriteTimeChanged = lastWriteTimeChanged; _Moved = moved; - _Property = property; + _ExifDirectory = exifDirectory; _RelativePath = relativePath; _ResizedFileHolder = resizedFileHolder; _SourceDirectoryFileHolder = sourceDirectoryFileHolder; } - public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool? isArchive, bool? isNotUniqueAndNeedsReview, bool isUniqueFileName, bool isValidImageFormatExtension, Property? property, bool? abandoned, bool? fileSizeChanged, bool? lastWriteTimeChanged) + public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool isValidImageFormatExtension) => + Get(filePath, sourceDirectoryFileHolder, relativePath, null, null, false, isValidImageFormatExtension, null, null, null, null); + + public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool? isArchive, bool? isNotUniqueAndNeedsReview, bool isUniqueFileName, bool isValidImageFormatExtension, ExifDirectory? exifDirectory, bool? abandoned, bool? fileSizeChanged, bool? lastWriteTimeChanged) { Item result; if (relativePath.EndsWith(".json")) throw new ArgumentException("Can not be a *.json file!"); if (filePath.ExtensionLowered is ".json") throw new ArgumentException("Can not be a *.json file!"); - result = new([], filePath, fileSizeChanged, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, lastWriteTimeChanged, null, property, relativePath, null, sourceDirectoryFileHolder); + result = new([], filePath, fileSizeChanged, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, lastWriteTimeChanged, null, exifDirectory, relativePath, null, sourceDirectoryFileHolder); return result; } - public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool isValidImageFormatExtension) => - Get(filePath, sourceDirectoryFileHolder, relativePath, null, null, false, isValidImageFormatExtension, null, null, null, null); + public bool Any() => + !_SourceDirectoryFileHolder.Exists || (_FileSizeChanged.HasValue && _FileSizeChanged.Value) || (_LastWriteTimeChanged.HasValue && _LastWriteTimeChanged.Value) || (_Moved.HasValue && _Moved.Value); public override string ToString() { @@ -71,6 +74,9 @@ public class Item : Properties.IItem return result; } + public void Update(ExifDirectory exifDirectory) => + _ExifDirectory = exifDirectory; + public void SetMoved(bool moved) => _Moved = moved; public void SetResizedFileHolder(string filenameExtension, FileHolder fileHolder) @@ -83,8 +89,4 @@ public class Item : Properties.IItem _ResizedFileHolder = fileHolder; } - public bool Any() => !_SourceDirectoryFileHolder.Exists || (_FileSizeChanged.HasValue && _FileSizeChanged.Value) || (_LastWriteTimeChanged.HasValue && _LastWriteTimeChanged.Value) || (_Moved.HasValue && _Moved.Value); - - public void Update(Property property) => _Property = property; - } \ No newline at end of file diff --git a/Shared/Models/MappingFromItem.cs b/Shared/Models/MappingFromItem.cs index 884d08e..54f97fd 100644 --- a/Shared/Models/MappingFromItem.cs +++ b/Shared/Models/MappingFromItem.cs @@ -1,16 +1,16 @@ +using System.Collections.ObjectModel; using System.Text.Json; using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; public record MappingFromItem(DateTime[] ContainerDateTimes, - DateTime? DateTimeDigitized, DateTime? DateTimeOriginal, int Id, bool? IsArchive, FilePath FilePath, bool? IsWrongYear, - string[] Keywords, + ReadOnlyCollection Keywords, DateTime MinimumDateTime, string? Model, string RelativePath, @@ -29,14 +29,18 @@ public record MappingFromItem(DateTime[] ContainerDateTimes, internal static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Item item, FileHolder? resizedFileHolder) { MappingFromItem result; - if (item.Property?.Id is null) + if (item.ExifDirectory?.FilePath.Id is null) throw new NotSupportedException(); if (resizedFileHolder is null) throw new NotSupportedException(); - List dateTimes = item.Property.GetDateTimes(); - DateTime minimumDateTime = Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); - (bool? isWrongYear, _) = Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes); - result = new(containerDateTimes, item.Property.DateTimeDigitized, item.Property.DateTimeOriginal, item.Property.Id.Value, item.IsArchive, item.FilePath, isWrongYear, item.Property.Keywords ?? [], minimumDateTime, item.Property.Model, item.RelativePath, resizedFileHolder); + ExifDirectory exifDirectory = item.ExifDirectory; + DateTime minimumDateTime = Stateless.Methods.IDate.GetMinimum(exifDirectory); + DateTime? dateTime = Stateless.Methods.IDate.GetDateTimeOriginal(exifDirectory); + string? model = Stateless.Methods.MetaBase.GetModel(exifDirectory.ExifBaseDirectories); + ReadOnlyCollection dateTimes = Stateless.Methods.IDate.GetDateTimes(exifDirectory); + (bool? isWrongYear, _) = Stateless.Methods.IProperty.IsWrongYear(item.FilePath, dateTime, dateTimes.ToList()); + ReadOnlyCollection keywords = Stateless.Methods.MetaBase.GetKeywords(exifDirectory.ExifBaseDirectories); + result = new(containerDateTimes, dateTime, exifDirectory.FilePath.Id.Value, item.IsArchive, item.FilePath, isWrongYear, keywords, minimumDateTime, model, item.RelativePath, resizedFileHolder); return result; } diff --git a/Shared/Models/Methods/IMetadataFile.cs b/Shared/Models/Methods/IMetadataFile.cs index 449e4ae..e69de29 100644 --- a/Shared/Models/Methods/IMetadataFile.cs +++ b/Shared/Models/Methods/IMetadataFile.cs @@ -1,8 +0,0 @@ -namespace View_by_Distance.Shared.Models.Methods; - -public interface IMetadataFile : Stateless.Methods.IMetadataFile -{ - - // ... - -} \ No newline at end of file diff --git a/Shared/Models/Properties/IItem.cs b/Shared/Models/Properties/IItem.cs index 0eff1e7..3d7ac9d 100644 --- a/Shared/Models/Properties/IItem.cs +++ b/Shared/Models/Properties/IItem.cs @@ -3,6 +3,7 @@ namespace View_by_Distance.Shared.Models.Properties; public interface IItem { + public ExifDirectory? ExifDirectory { get; } public bool? FileSizeChanged { get; } public List Faces { get; } public FilePath FilePath { get; } @@ -11,7 +12,6 @@ public interface IItem public bool IsUniqueFileName { get; } public bool IsValidImageFormatExtension { get; } public bool? Moved { get; } - public Property? Property { get; } public string RelativePath { get; } public FileHolder? ResizedFileHolder { get; } public FileHolder SourceDirectoryFileHolder { get; } diff --git a/Shared/Models/Stateless/Methods/FilePair.cs b/Shared/Models/Stateless/Methods/FilePair.cs new file mode 100644 index 0000000..49d15b8 --- /dev/null +++ b/Shared/Models/Stateless/Methods/FilePair.cs @@ -0,0 +1,151 @@ +using System.Collections.ObjectModel; +using View_by_Distance.Shared.Models.Properties; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal abstract class FilePair +{ + + internal static ReadOnlyCollection GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection) + { + ReadOnlyCollection results; + ReadOnlyDictionary> fileNamesToFiles = FilePath.GetFilesKeyValuePairs(filePathsCollection); + results = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles); + return results; + } + + internal static ReadOnlyCollection GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileNamesToFiles) + { + List? results = null; + int renamed; + const bool useCeilingAverage = true; + ReadOnlyCollection? jsonFilesCollection = null; + IReadOnlyDictionary>? compareFileNamesToFiles = null; + for (int i = 0; i < fileNamesToFiles.Count; i++) + { + renamed = 0; + jsonFilesCollection = XDirectory.GetFilesCollection(jsonGroupDirectory, directorySearchFilter, extension, useCeilingAverage); + renamed += LookForAbandoned(propertyConfiguration, jsonFilesCollection, fileNamesToFiles, extension); + if (renamed > 0) + continue; + compareFileNamesToFiles = GetKeyValuePairs(propertyConfiguration, jsonFilesCollection); + results = XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); + renamed += XDirectory.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.AsReadOnly(); + } + + private static int LookForAbandoned(IPropertyConfiguration propertyConfiguration, 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(propertyConfiguration, fileNamesToFiles, extension, renameCollection, files); + if (!moved && check) + moved = true; + } + if (renameCollection.Count > 0) + XDirectory.MoveFiles(renameCollection, "{}", "{abd}"); + result = renameCollection.Count; + if (moved && result == 0) + result = 1; + return result; + } + + private static bool AnyMoved(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary> fileNamesToFiles, string extension, List renameCollection, string[] files) + { + bool result = false; + string checkFile; + string directory; + FilePath filePath; + string fileNameWith; + string checkDirectory; + string directoryName; + List? collection; + Models.FileHolder fileHolder; + List directoryNames = []; + foreach (string file in files) + { + if (!file.EndsWith(extension)) + throw new Exception(); + fileHolder = IFileHolder.Get(file); + if (!fileHolder.Exists) + continue; + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + if (filePath.Id is null) + continue; + if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection)) + renameCollection.Add(file); + else + { + directoryNames.Clear(); + directoryName = Path.GetFileName(filePath.DirectoryFullPath) ?? throw new Exception(); + foreach (FilePath f in collection) + directoryNames.Add(Path.GetFileName(f.DirectoryFullPath) ?? throw new Exception()); + if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1) + continue; + if (directoryName != directoryNames[0]) + { + directory = Path.GetDirectoryName(filePath.DirectoryFullPath) ?? throw new Exception(); + checkDirectory = Path.Combine(directory, directoryNames[0]); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + fileNameWith = collection.Count > 1 ? filePath.Name : $"{collection[0].Name}{extension}"; + checkFile = Path.Combine(checkDirectory, fileNameWith); + 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); + } + } + } + } + return result; + } + + private static ReadOnlyDictionary> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) + { + Dictionary> results = []; + List? collection; + FilePath filePath; + Models.FileHolder fileHolder; + foreach (string[] files in filesCollection) + { + foreach (string file in files) + { + fileHolder = IFileHolder.Get(file); + if (!fileHolder.Exists) + continue; + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + if (filePath.Id is null) + continue; + if (!results.TryGetValue(filePath.Id.Value, out collection)) + { + results.Add(filePath.Id.Value, []); + if (!results.TryGetValue(filePath.Id.Value, out collection)) + throw new Exception(); + } + collection.Add(filePath); + } + } + return new(results); + } + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IDate.cs b/Shared/Models/Stateless/Methods/IDate.cs new file mode 100644 index 0000000..509186e --- /dev/null +++ b/Shared/Models/Stateless/Methods/IDate.cs @@ -0,0 +1,38 @@ +using System.Collections.ObjectModel; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface IDate +{ + + public static DateTime GetMinimum(ExifDirectory exifDirectory) => + XDate.GetMinimum(exifDirectory); + + public static (int Season, string seasonName) GetSeason(int dayOfYear) => + XDate.GetSeason(dayOfYear); + + public static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) => + XDate.GetDateTimeOriginal(exifDirectory); + + public static ReadOnlyCollection GetDateTimes(ExifDirectory exifDirectory) => + XDate.GetDateTimes(exifDirectory); + + public static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) => + XDate.IsWrongYear(directoryInfo, filePath, exifDirectory); + + internal DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) => + GetMinimum(exifDirectory); + + internal (int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) => + GetSeason(dayOfYear); + + internal DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) => + GetDateTimeOriginal(exifDirectory); + + internal ReadOnlyCollection TestStatic_GetDateTimes(ExifDirectory exifDirectory) => + XDate.GetDateTimes(exifDirectory); + + internal (bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) => + IsWrongYear(directoryInfo, filePath, exifDirectory); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IDirectory.cs b/Shared/Models/Stateless/Methods/IDirectory.cs index 0449347..753f1b2 100644 --- a/Shared/Models/Stateless/Methods/IDirectory.cs +++ b/Shared/Models/Stateless/Methods/IDirectory.cs @@ -11,32 +11,32 @@ public interface IDirectory public static char GetDirectory(string fileName) => fileName.Split('-').Length > 2 ? '-' : fileName.Split('.')[0][^1]; - 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); - 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); - 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 int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) => + XDirectory.MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension); - public static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) => - XDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage); + public static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection, bool useIgnoreExtensions) => + XDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions); - public static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) => + public static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useIgnoreExtensions, bool useCeilingAverage) => + XDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useIgnoreExtensions, 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); + public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, Action? tick) => + XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, null, tick); + + public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, Dictionary exifDirectoriesById, Action? tick) => + XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates: false, ifCanUseId: true, filePathsCollection, fileGroups, exifDirectoriesById, tick); internal int TestStatic_GetDirectory(char directory) => GetDirectory(directory); @@ -50,25 +50,25 @@ public interface IDirectory 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 int TestStatic_MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) => + MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension); - internal ReadOnlyCollection> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) => - GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage); + internal ReadOnlyCollection> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection, bool useIgnoreExtensions) => + GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions); - internal List TestStatic_GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) => + internal ReadOnlyCollection> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useIgnoreExtensions, bool useCeilingAverage) => + GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useIgnoreExtensions, 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) => + internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, Action? tick) => GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, tick); + internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, Dictionary exifDirectoriesById, Action? tick) => + GetToDoCollection(propertyConfiguration, filePathsCollection, fileGroups, exifDirectoriesById, tick); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IDlibDotNet.cs b/Shared/Models/Stateless/Methods/IDlibDotNet.cs index cbb593f..d317bd7 100644 --- a/Shared/Models/Stateless/Methods/IDlibDotNet.cs +++ b/Shared/Models/Stateless/Methods/IDlibDotNet.cs @@ -5,6 +5,7 @@ public interface IDlibDotNet void Tick(); (string, string) GetResultsFullGroupDirectories(); + void ConstructProgressBar(int maxTicks, string message); (string, string, string, string) GetResultsFullGroupDirectories(string outputResolution); } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IFilePair.cs b/Shared/Models/Stateless/Methods/IFilePair.cs new file mode 100644 index 0000000..0585149 --- /dev/null +++ b/Shared/Models/Stateless/Methods/IFilePair.cs @@ -0,0 +1,21 @@ +using System.Collections.ObjectModel; +using View_by_Distance.Shared.Models.Properties; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface IFilePair +{ + + public static ReadOnlyCollection GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection) => + FilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection); + + public static ReadOnlyCollection GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileNamesToFiles) => + FilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles); + + internal ReadOnlyCollection TestStatic_GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection) => + GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection); + + internal ReadOnlyCollection TestStatic_GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileNamesToFiles) => + GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IId.cs b/Shared/Models/Stateless/Methods/IId.cs index 92ec53b..44ecdf6 100644 --- a/Shared/Models/Stateless/Methods/IId.cs +++ b/Shared/Models/Stateless/Methods/IId.cs @@ -29,19 +29,19 @@ public interface IId 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); + fileNameFirstSegment.Length - 1 == propertyConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '0' or '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 string GetIntelligentId(IPropertyConfiguration propertyConfiguration, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) => + Id.GetIntelligentId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal); 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[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber); + public static string GetPaddedId(IPropertyConfiguration propertyConfiguration, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) => + Id.GetPaddedId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index); + internal int TestStatic_GetDeterministicHashCode(byte[] value) => GetDeterministicHashCode(value); @@ -63,13 +63,13 @@ public interface IId 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 string TestStatic_GetIntelligentId(IPropertyConfiguration propertyConfiguration, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) => + GetIntelligentId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal); internal bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) => NameWithoutExtensionIsPaddedIntelligentIdFormat(propertyConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment); + internal string TestStatic_GetPaddedId(IPropertyConfiguration propertyConfiguration, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) => + GetPaddedId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IMappingFromItem.cs b/Shared/Models/Stateless/Methods/IMappingFromItem.cs index 02ebeaf..1c775a3 100644 --- a/Shared/Models/Stateless/Methods/IMappingFromItem.cs +++ b/Shared/Models/Stateless/Methods/IMappingFromItem.cs @@ -1,16 +1,18 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IMappingFromItem -{ // ... +{ - MappingFromItem TestStatic_GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder) - => GetMappingFromItem(containerDateTimes, item, resizedFileHolder); - static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder) + public static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder) => MappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); - MappingFromItem TestStatic_GetMappingFromItem(Models.Item item) - => GetMappingFromItem(item); - static MappingFromItem GetMappingFromItem(Models.Item item) + public static MappingFromItem GetMappingFromItem(Models.Item item) => GetMappingFromItem(containerDateTimes: [], item, item.ResizedFileHolder); + internal MappingFromItem TestStatic_GetMappingFromItem(Models.Item item) + => GetMappingFromItem(item); + + internal MappingFromItem TestStatic_GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder) + => GetMappingFromItem(containerDateTimes, item, resizedFileHolder); + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IMetaBase.cs b/Shared/Models/Stateless/Methods/IMetaBase.cs new file mode 100644 index 0000000..19e5992 --- /dev/null +++ b/Shared/Models/Stateless/Methods/IMetaBase.cs @@ -0,0 +1,32 @@ +using System.Collections.ObjectModel; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +public interface IMetaBase +{ + + public static string? GetMaker(ExifDirectoryBase[]? exifBaseDirectories) => + MetaBase.GetMaker(exifBaseDirectories); + + public static string? GetModel(ExifDirectoryBase[]? exifBaseDirectories) => + MetaBase.GetModel(exifBaseDirectories); + + public static int? GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) => + MetaBase.GetOrientation(exifBaseDirectories); + + public static ReadOnlyCollection GetKeywords(ExifDirectoryBase[]? exifBaseDirectories) => + MetaBase.GetKeywords(exifBaseDirectories); + + internal static string? TestStatic_GetMaker(ExifDirectoryBase[]? exifBaseDirectories) => + GetMaker(exifBaseDirectories); + + internal static string? TestStatic_GetModel(ExifDirectoryBase[]? exifBaseDirectories) => + GetModel(exifBaseDirectories); + + internal static int? TestStatic_GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) => + GetOrientation(exifBaseDirectories); + + internal static ReadOnlyCollection TestStatic_GetKeywords(ExifDirectoryBase[]? exifBaseDirectories) => + GetKeywords(exifBaseDirectories); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IMetadataFile.cs b/Shared/Models/Stateless/Methods/IMetadataFile.cs index 6ff89c3..e69de29 100644 --- a/Shared/Models/Stateless/Methods/IMetadataFile.cs +++ b/Shared/Models/Stateless/Methods/IMetadataFile.cs @@ -1,8 +0,0 @@ -namespace View_by_Distance.Shared.Models.Stateless.Methods; - -public interface IMetadataFile -{ - - // ... - -} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IPath.cs b/Shared/Models/Stateless/Methods/IPath.cs index 4ab786d..8ecc5d5 100644 --- a/Shared/Models/Stateless/Methods/IPath.cs +++ b/Shared/Models/Stateless/Methods/IPath.cs @@ -24,6 +24,9 @@ public interface IPath public static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) => XPath.MakeHiddenIfAllItemsAreHidden(rootDirectory); + public static void CreateDirectories(ReadOnlyCollection directories) => + XPath.CreateDirectories(directories); + public static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) => XPath.ChangeDateForEmptyDirectories(rootDirectory, ticks); @@ -65,6 +68,9 @@ public interface IPath internal void TestStatic_MakeHiddenIfAllItemsAreHidden(string rootDirectory) => MakeHiddenIfAllItemsAreHidden(rootDirectory); + internal void TestStatic_CreateDirectories(ReadOnlyCollection directories) => + CreateDirectories(directories); + internal void TestStatic_ChangeDateForEmptyDirectories(string rootDirectory, long ticks) => ChangeDateForEmptyDirectories(rootDirectory, ticks); diff --git a/Shared/Models/Stateless/Methods/Id.cs b/Shared/Models/Stateless/Methods/Id.cs index ed3b348..191391b 100644 --- a/Shared/Models/Stateless/Methods/Id.cs +++ b/Shared/Models/Stateless/Methods/Id.cs @@ -37,7 +37,7 @@ internal abstract class Id _ = 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') + if (intelligentId[^1] is '0' or '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(); @@ -48,7 +48,7 @@ internal abstract class Id (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); + (byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 7 : 3 : filePath.Id > -1 ? 5 : 0); internal static bool NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameWithoutExtension) { @@ -63,25 +63,34 @@ internal abstract class Id return result; } - internal static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) + internal static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) { string result; StringBuilder stringBuilder = new(); if (propertyConfiguration.IntMinValueLength < (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2)) throw new NotSupportedException(); - if (hasDateTimeOriginal is null) - { } int key; string value; List resultAllInOneSubdirectoryChars = []; - if (id > -1) + if (hasDateTimeOriginal is null) { - key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal is not null && hasDateTimeOriginal.Value ? 9 : 7; + key = 0; + value = id.ToString().PadLeft(propertyConfiguration.IntMinValueLength, '0'); + } + else if (id > -1) + { + if (!propertyConfiguration.ValidVideoFormatExtensions.Contains(extensionLowered)) + key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal.Value ? 9 : 7; + else + key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 6 : 5; value = id.ToString().PadLeft(propertyConfiguration.IntMinValueLength, '0'); } else { - key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal is not null && hasDateTimeOriginal.Value ? 1 : 3; + if (!propertyConfiguration.ValidVideoFormatExtensions.Contains(extensionLowered)) + key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal.Value ? 1 : 3; + else + key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 4 : 0; value = id.ToString()[1..].PadLeft(propertyConfiguration.IntMinValueLength, '0'); } for (int i = value.Length - propertyConfiguration.ResultAllInOneSubdirectoryLength - 1; i > -1; i--) @@ -92,14 +101,14 @@ internal abstract class Id return result; } - internal static string GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) + internal static string GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) { string result; if (propertyConfiguration.Offset < 0) result = Guid.NewGuid().ToString(); else { - string intelligentId = GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal); + string intelligentId = GetIntelligentId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal); int check = GetId(propertyConfiguration, intelligentId); if (check != id) throw new NotSupportedException(); diff --git a/Shared/Models/Stateless/Methods/MetaBase.cs b/Shared/Models/Stateless/Methods/MetaBase.cs new file mode 100644 index 0000000..7a7e19c --- /dev/null +++ b/Shared/Models/Stateless/Methods/MetaBase.cs @@ -0,0 +1,83 @@ +using System.Collections.ObjectModel; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal static class MetaBase +{ + + internal static string? GetMaker(ExifDirectoryBase[]? exifBaseDirectories) + { + string? result = null; + if (exifBaseDirectories is not null) + { + string value; + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + value = exifDirectoryBase?.Make is null ? string.Empty : exifDirectoryBase.Make.ToString().Trim(); + if (string.IsNullOrEmpty(value)) + result = null; + else + { + result = $"{value[0].ToString().ToUpper()}{value[1..].ToLower()}"; + break; + } + } + } + return result; + } + + internal static string? GetModel(ExifDirectoryBase[]? exifBaseDirectories) + { + string? result = null; + if (exifBaseDirectories is not null) + { + string value; + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + value = exifDirectoryBase?.Model is null ? string.Empty : exifDirectoryBase.Model.ToString().Trim(); + if (string.IsNullOrEmpty(value)) + result = null; + else + { + result = value; + break; + } + } + } + return result; + } + + internal static int? GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) + { + int? result = null; + // public const int TagOrientation = 274; + if (exifBaseDirectories is not null) + { + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + result = exifDirectoryBase?.OrientationValue; + if (result is not null) + break; + } + } + return result; + } + + internal static ReadOnlyCollection GetKeywords(ExifDirectoryBase[]? exifBaseDirectories) + { + List results = []; + if (exifBaseDirectories is not null) + { + string value; + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + value = exifDirectoryBase?.WinKeywords is null ? string.Empty : exifDirectoryBase.WinKeywords.ToString().Trim(); + if (string.IsNullOrEmpty(value)) + continue; + results.Add(value); + } + } + return results.AsReadOnly(); + } + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/XDate.cs b/Shared/Models/Stateless/Methods/XDate.cs new file mode 100644 index 0000000..9bf6995 --- /dev/null +++ b/Shared/Models/Stateless/Methods/XDate.cs @@ -0,0 +1,252 @@ +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal abstract class XDate +{ + + internal static ReadOnlyCollection GetDateTimes(ExifDirectory exifDirectory) + { + List results = []; + foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) + { + if (exifDirectoryBase.DateTimeOriginal is not null) + results.Add(exifDirectoryBase.DateTimeOriginal.Value); + } + foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories) + { + if (aviDirectory.DateTimeOriginal is not null) + results.Add(aviDirectory.DateTimeOriginal.Value); + } + foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories) + { + if (quickTimeMovieHeaderDirectory.Created is not null) + { + if (quickTimeMovieHeaderDirectory.Created.Value.Year == 1904 && quickTimeMovieHeaderDirectory.Created.Value.Month == 1 && quickTimeMovieHeaderDirectory.Created.Value.Day == 1) + continue; + results.Add(quickTimeMovieHeaderDirectory.Created.Value); + } + } + foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories) + { + if (quickTimeTrackHeaderDirectory.Created is not null) + { + if ((quickTimeTrackHeaderDirectory.Created.Value.Year is 1904 or 1970) && quickTimeTrackHeaderDirectory.Created.Value.Month == 1 && quickTimeTrackHeaderDirectory.Created.Value.Day == 1) + continue; + results.Add(quickTimeTrackHeaderDirectory.Created.Value); + } + } + if (results.Count == 0) + { + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(exifDirectory.FilePath.Name); + DateTime? dateTime = GetDateTimeFromName(fileNameWithoutExtension); + if (dateTime is not null) + results.Add(dateTime.Value); + foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) + { + if (exifDirectoryBase.DateTime is not null) + results.Add(exifDirectoryBase.DateTime.Value); + if (exifDirectoryBase.DateTimeDigitized is not null) + results.Add(exifDirectoryBase.DateTimeDigitized.Value); + } + } + if (results.Count == 0) + { + foreach (FileMetadataDirectory fileMetadataDirectory in exifDirectory.FileMetadataDirectories) + { + if (fileMetadataDirectory.FileModifiedDate is not null) + results.Add(fileMetadataDirectory.FileModifiedDate.Value); + } + } + return results.AsReadOnly(); + } + + private static DateTime? GetDateTimeFromName(string fileNameWithoutExtension) + { + DateTime? result = null; + int length; + string format; + string fullFormat; + StringBuilder value = new(); + const string ticksExample = "##################"; + string[][] dateFormats = + [ + [string.Empty, "yyyyMMdd_HHmmss", string.Empty], + [string.Empty, "yyyyMMddHHmmssfff", string.Empty], + [string.Empty, "yyyyMMdd_", ticksExample], + [string.Empty, "yyyy-MM-dd_", ticksExample], + [string.Empty, "yyyy-MM-dd.", ticksExample], + // [string.Empty, "yyyy-MM-dd.", $"{ticksExample}.{fileHolder.Length}"], + [string.Empty, "yyyy-MM-dd HH.mm.ss", string.Empty], + [string.Empty, "yyyyMMdd_HHmmss", "_LLS"], + [string.Empty, "yyyyMMdd_HHmmss", "_HDR"], + ["WIN_", "yyyyMMdd_HH_mm_ss", "_Pro"], + ["IMG_", "yyyyMMdd_HHmmss", string.Empty], + ["IMG#####-", "yyyyMMdd-HHmm", string.Empty], + ["CameraZOOM-", "yyyyMMddHHmmss", string.Empty], + ["VideoCapture_", "yyyyMMdd-HHmmss ", string.Empty] + ]; + foreach (string[] dateFormat in dateFormats) + { + _ = value.Clear(); + if (dateFormat.Length != 3) + throw new Exception(); + fullFormat = string.Join(string.Empty, dateFormat); + if (fileNameWithoutExtension.Length != fullFormat.Length) + continue; + format = dateFormat[1]; + length = dateFormat[0].Length + dateFormat[1].Length; + for (int i = dateFormat[0].Length; i < length; i++) + _ = value.Append(fileNameWithoutExtension[i]); + if (value.Length != format.Length) + continue; + if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime)) + { + if (fileNameWithoutExtension.Length < ticksExample.Length || !long.TryParse(fileNameWithoutExtension[^ticksExample.Length..], out long ticks)) + result = checkDateTime; + else + result = new DateTime(ticks); + break; + } + } + return result; + } + + internal static DateTime GetMinimum(ExifDirectory exifDirectory) + { + DateTime result; + ReadOnlyCollection results = GetDateTimes(exifDirectory); + result = results.Count == 0 ? DateTime.MinValue : results.Min(); + return result; + } + + internal static (int Season, string seasonName) GetSeason(int dayOfYear) + { + (int Season, string seasonName) result = dayOfYear switch + { + < 78 => new(0, "Winter"), + < 124 => new(1, "Spring"), + < 171 => new(2, "Spring"), + < 217 => new(3, "Summer"), + < 264 => new(4, "Summer"), + < 309 => new(5, "Fall"), + < 354 => new(6, "Fall"), + _ => new(7, "Winter") + }; + return result; + } + + internal static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) + { + DateTime? result; + List results = []; + foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) + { + if (exifDirectoryBase.DateTimeOriginal is not null) + results.Add(exifDirectoryBase.DateTimeOriginal.Value); + } + foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories) + { + if (aviDirectory.DateTimeOriginal is not null) + results.Add(aviDirectory.DateTimeOriginal.Value); + } + foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories) + { + if (quickTimeMovieHeaderDirectory.Created is not null) + { + if (quickTimeMovieHeaderDirectory.Created.Value.Year == 1904 && quickTimeMovieHeaderDirectory.Created.Value.Month == 1 && quickTimeMovieHeaderDirectory.Created.Value.Day == 1) + continue; + results.Add(quickTimeMovieHeaderDirectory.Created.Value); + } + } + foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories) + { + if (quickTimeTrackHeaderDirectory.Created is not null) + { + if ((quickTimeTrackHeaderDirectory.Created.Value.Year is 1904 or 1970) && quickTimeTrackHeaderDirectory.Created.Value.Month == 1 && quickTimeTrackHeaderDirectory.Created.Value.Day == 1) + continue; + results.Add(quickTimeTrackHeaderDirectory.Created.Value); + } + } + result = results.Count == 0 ? null : results.Min(); + return result; + } + + internal static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) + { + string[] results = []; + bool? result = null; + string year; + string directoryName; + string[] directorySegments; + List collection = []; + string? check = Path.GetFullPath(filePath.FullName); + DateTime? dateTimeOriginal = GetDateTimeOriginal(exifDirectory); + if (dateTimeOriginal is not null) + collection.Add(dateTimeOriginal.Value); + else + { + ReadOnlyCollection dateTimes = GetDateTimes(exifDirectory); + foreach (DateTime dateTime in dateTimes) + collection.Add(dateTime); + } + foreach (DateTime dateTime in collection) + { + year = dateTime.ToString("yyyy"); + for (int i = 0; i < int.MaxValue; i++) + { + check = Path.GetDirectoryName(check); + if (string.IsNullOrEmpty(check)) + break; + directoryName = Path.GetFileName(check); + directorySegments = directoryName.Split(' '); + (result, results) = IsWrongYear(directorySegments, year); + if (result is not null) + break; + if (check == directoryInfo.FullName) + break; + } + if (result is not null && !result.Value) + break; + } + return new(result, results); + } + + private static Record IsWrongYear(string[] segments, string year) + { + Record result; + bool? check; + string[] results = ( + from l + in segments + where l?.Length > 2 + && ( + l[..2] is "18" or "19" or "20" + || (l.Length == 5 && l.Substring(1, 2) is "18" or "19" or "20" && (l[0] is '~' or '=' or '-' or '^' or '#')) + || (l.Length == 6 && l[..2] is "18" or "19" or "20" && l[4] == '.') + || (l.Length == 7 && l.Substring(1, 2) is "18" or "19" or "20" && l[5] == '.') + ) + select l + ).ToArray(); + string[] matches = ( + from l + in results + where l == year + || (l.Length == 5 && l.Substring(1, 4) == year && (l[0] is '~' or '=' or '-' or '^' or '#')) + || (l.Length == 6 && l[..4] == year && l[4] == '.') + || (l.Length == 7 && l.Substring(1, 4) == year && l[5] == '.') + select l + ).ToArray(); + if (results.Length == 0) + check = null; + else + check = matches.Length == 0; + result = new(check, results); + return result; + } + + private record Record(bool? IsWrongYear, string[] Years); + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/XDirectory.cs b/Shared/Models/Stateless/Methods/XDirectory.cs index b0859fc..6372eea 100644 --- a/Shared/Models/Stateless/Methods/XDirectory.cs +++ b/Shared/Models/Stateless/Methods/XDirectory.cs @@ -67,57 +67,6 @@ internal abstract partial class XDirectory 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 = []; @@ -173,7 +122,54 @@ internal abstract partial class XDirectory return results; } - internal static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) + internal static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List filePairs, string jsonGroupDirectory, string extension) + { + FileInfo? toFileInfo; + string checkDirectory; + List<(FilePath, string)> rename = []; + foreach (Models.FilePair filePair in filePairs) + { + if (filePair.IsUnique) + continue; + IsNotUniqueLoop(propertyConfiguration, jsonGroupDirectory, extension, filePair, rename); + } + foreach ((FilePath from, string to) in rename) + { + toFileInfo = null; + checkDirectory = to; + 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 (from.Length == toFileInfo.Length && from.LastWriteTicks == toFileInfo.LastWriteTime.Ticks) + checkDirectory = string.Concat(checkDirectory, ".del"); + else + checkDirectory = string.Concat(checkDirectory, ".j"); + } + File.Move(from.FullName, checkDirectory); + } + return rename.Count; + } + + private static void IsNotUniqueLoop(Properties.IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, Models.FilePair filePair, List<(FilePath, string)> rename) + { + int length = propertyConfiguration.RootDirectory.Length; + foreach (FilePath path in filePair.Collection) + { + if (filePair.Match is null || path != filePair.Match) + continue; + rename.Add(new(path, string.Concat(jsonGroupDirectory, filePair.FilePath.FullName[length..], extension))); + } + } + + internal static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection, bool useIgnoreExtensions) { List> results = []; FilePath filePath; @@ -185,7 +181,9 @@ internal abstract partial class XDirectory foreach (string file in files) { fileHolder = IFileHolder.Get(file); - if (propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) + if (!fileHolder.Exists) + continue; + if (useIgnoreExtensions && propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) continue; filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); filePaths.Add(filePath); @@ -195,38 +193,38 @@ internal abstract partial class XDirectory return results.AsReadOnly(); } - internal static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) + internal static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useIgnoreExtensions, bool useCeilingAverage) { ReadOnlyCollection> results; ReadOnlyCollection filesCollection = GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage); - results = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection); + results = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions); return results; } - internal static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) + internal static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) { - List results = []; - string? match; - FilePair filePair; + List results = []; + FilePath? match; bool uniqueFileName; - List? collection; + List? collection; + Models.FilePair filePair; bool? isNotUniqueAndNeedsReview; - string fileNameWithoutExtensionMinusOne; foreach (ReadOnlyCollection filePaths in filePathsCollection) { foreach (FilePath filePath in filePaths) { + if (filePath.Id is null) + continue; isNotUniqueAndNeedsReview = null; if (propertyConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered)) continue; - fileNameWithoutExtensionMinusOne = filePath.NameWithoutExtension[..^1]; - if (!fileNamesToFiles.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) + if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection)) throw new Exception(); uniqueFileName = collection.Count == 1; if (!uniqueFileName) - isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(filePath.FullName, collection); - if (!compareFileNamesToFiles.TryGetValue(fileNameWithoutExtensionMinusOne, out collection)) - filePair = new(Path: filePath.FullName, + isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(filePath, collection); + if (!compareFileNamesToFiles.TryGetValue(filePath.Id.Value, out collection)) + filePair = new(FilePath: filePath, IsUnique: uniqueFileName, IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, Collection: [], @@ -234,21 +232,21 @@ internal abstract partial class XDirectory else { if (collection.Count == 0) - filePair = new(Path: filePath.FullName, + filePair = new(FilePath: filePath, IsUnique: uniqueFileName, IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, Collection: collection, Match: null); else if (uniqueFileName && collection.Count == 1) - filePair = new(Path: filePath.FullName, + filePair = new(FilePath: filePath, IsUnique: uniqueFileName, IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, Collection: collection, Match: collection.First()); else { - match = GetMatch(filePath.FullName, collection); - filePair = new(Path: filePath.FullName, + match = GetMatch(filePath, collection); + filePair = new(FilePath: filePath, IsUnique: uniqueFileName, IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, Collection: collection, @@ -261,19 +259,20 @@ internal abstract partial class XDirectory return results; } - private static bool GetIsNotUniqueAndNeedsReview(string file, List collection) + private static bool GetIsNotUniqueAndNeedsReview(FilePath filePath, List collection) { bool result = false; - FileInfo possibleFileInfo; - FileInfo fileInfo = new(file); - foreach (string possible in collection) + long max; + foreach (FilePath possible in collection) { - if (possible == file) + if (possible.FullName == filePath.FullName) 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) + if (possible.LastWriteTicks != filePath.LastWriteTicks) + { + max = new long[] { possible.LastWriteTicks, filePath.LastWriteTicks }.Max(); + File.SetLastWriteTime(filePath.FullName, new DateTime(max)); + } + if (possible.LastWriteTicks == filePath.LastWriteTicks && possible.Length == filePath.Length) continue; if (!result) result = true; @@ -281,50 +280,48 @@ internal abstract partial class XDirectory return result; } - private static string? GetMatch(string file, List collection) + private static FilePath? GetMatch(FilePath filePath, List collection) { - string? result = null; - FileInfo possibleFileInfo; + FilePath? result = null; List lengths = []; - List matches = []; - FileInfo fileInfo = new(file); - List creationTimes = []; - foreach (string possible in collection) + List matches = []; + List lastWriteTicks = []; + foreach (FilePath possible in collection) { - possibleFileInfo = new(possible); - lengths.Add(possibleFileInfo.Length); - creationTimes.Add(possibleFileInfo.CreationTime); - if (possibleFileInfo.CreationTime != fileInfo.LastWriteTime) + lengths.Add(possible.Length); + lastWriteTicks.Add(possible.LastWriteTicks); + if (possible.LastWriteTicks != filePath.LastWriteTicks) continue; matches.Add(possible); } - if (matches.Count == 1 || (matches.Count > 0 && lengths.Distinct().Count() == 1 && creationTimes.Distinct().Count() == 1)) + if (matches.Count == 1 || (matches.Count > 0 && lengths.Distinct().Count() == 1 && lastWriteTicks.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) + internal static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, Dictionary? exifDirectoriesById, Action? tick) { List<(FilePath, string)> results = []; string paddedId; string checkFile; string directory; - FileInfo fileInfo; FilePath filePath; + DateTime? dateTime; string paddedIdFile; bool wrapped = false; string intelligentId; - CombinedEnumAndIndex cei; + bool? hasIgnoreKeyword; bool paddedCheck = false; + CombinedEnumAndIndex cei; string fileDirectoryName; + bool? hasDateTimeOriginal; List distinctIds = []; List distinct = []; + ExifDirectory? exifDirectory; Models.FileHolder fileHolder; + ReadOnlyCollection keywords; 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++) { @@ -338,23 +335,45 @@ internal abstract partial class XDirectory { if (wrapped) continue; - directory = keyValuePairs[cei.Enum][cei.Index]; + if (cei.Enum == 0) + continue; + directory = fileGroups[cei.Enum][cei.Index]; } else { if (!wrapped) wrapped = true; - directory = Path.Combine(keyValuePairs[cei.Enum][cei.Index], fileDirectoryName); + directory = Path.Combine(fileGroups[cei.Enum][cei.Index], fileDirectoryName); } if (ifCanUseId && filePath.IsIntelligentIdFormat && filePath.Id is not null && filePath.DirectoryFullPath is not null) { - paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i); + if (filePath.Id == -748161839 || filePath.FileNameFirstSegment == "740318810015") + { + if (filePath.ExtensionLowered == ".mov") // -748161839) + { } + } + if (exifDirectoriesById is null || !exifDirectoriesById.TryGetValue(filePath.Id.Value, out exifDirectory)) + { + hasIgnoreKeyword = filePath.HasIgnoreKeyword; + hasDateTimeOriginal = filePath.HasDateTimeOriginal; + } + else + { + dateTime = IDate.GetDateTimeOriginal(exifDirectory); + hasDateTimeOriginal = dateTime is not null; + if (dateTime is null && propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered)) + continue; + keywords = MetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories); + hasIgnoreKeyword = propertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains); + } + paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, i); paddedIdFile = Path.Combine(filePath.DirectoryFullPath, $"{paddedId}{filePath.ExtensionLowered}"); if (!File.Exists(paddedIdFile)) { File.Move(filePath.FullName, paddedIdFile); - fileInfo = new(paddedIdFile); - fileHolder = Models.FileHolder.Get(fileInfo); + fileHolder = IFileHolder.Get(paddedIdFile); + if (!fileHolder.Exists) + continue; filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); if (!paddedCheck) paddedCheck = true; @@ -366,14 +385,14 @@ internal abstract partial class XDirectory { if (filePath.Id is null) throw new NullReferenceException(nameof(filePath.Id)); - intelligentId = IId.GetIntelligentId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal); + intelligentId = IId.GetIntelligentId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal); if (!isOffsetDeterministicHashCode) checkFile = Path.Combine(directory, $"{intelligentId}{filePath.ExtensionLowered}"); else { if (filePath.DirectoryFullPath is null) continue; - paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i); + paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i); paddedIdFile = Path.Combine(filePath.DirectoryFullPath, $"{paddedId}{filePath.ExtensionLowered}"); if (File.Exists(paddedIdFile)) continue; @@ -391,8 +410,8 @@ internal abstract partial class XDirectory continue; for (int j = 1; j < int.MaxValue; j++) { - fileInfo = new(checkFile); - if (!fileInfo.Exists || filePath.Length == fileInfo.Length && filePath.LastWriteTicks == fileInfo.LastWriteTime.Ticks) + fileHolder = IFileHolder.Get(checkFile); + if (!fileHolder.Exists || fileHolder.LastWriteTime is null || filePath.Length == fileHolder.Length && filePath.LastWriteTicks == fileHolder.LastWriteTime.Value.Ticks) checkFile = Path.Combine(directory, $"{filePath.NameWithoutExtension}.{j}dup{filePath.ExtensionLowered}"); else checkFile = Path.Combine(directory, $"{filePath.NameWithoutExtension}.{j}why{filePath.ExtensionLowered}"); diff --git a/Shared/Models/Stateless/Methods/XPath.cs b/Shared/Models/Stateless/Methods/XPath.cs index 76835fa..d86b3b1 100644 --- a/Shared/Models/Stateless/Methods/XPath.cs +++ b/Shared/Models/Stateless/Methods/XPath.cs @@ -64,15 +64,20 @@ internal abstract class XPath byte @enum; int converted; string combined; - if (filePath.HasIgnoreKeyword is null || filePath.HasDateTimeOriginal is null) - throw new NotImplementedException(); byte missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePath); - if (filePath.HasIgnoreKeyword.Value) - @enum = IId.GetHasIgnoreKeyword(filePath); - else if (!filePath.HasDateTimeOriginal.Value) + if (!filePath.IsIntelligentIdFormat) @enum = missingDateTimeOriginal; else - @enum = IId.GetHasDateTimeOriginal(propertyConfiguration, filePath); + { + if (filePath.HasIgnoreKeyword is null || filePath.HasDateTimeOriginal is null) + throw new NotImplementedException("Chicken and Egg!"); + if (filePath.HasIgnoreKeyword.Value) + @enum = IId.GetHasIgnoreKeyword(filePath); + else if (!filePath.HasDateTimeOriginal.Value) + @enum = missingDateTimeOriginal; + else + @enum = IId.GetHasDateTimeOriginal(propertyConfiguration, filePath); + } string fileNameBeforeFirst = fileNameWithoutExtension.Split('.')[0]; string check = fileNameBeforeFirst.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength ? new('-', propertyConfiguration.ResultAllInOneSubdirectoryLength) : @@ -228,6 +233,20 @@ internal abstract class XPath } } + internal static void CreateDirectories(ReadOnlyCollection directories) + { + string checkDirectory; + foreach (string directory in directories) + { + for (int i = 0; i < 101; i++) + { + checkDirectory = Path.Combine(directory, i.ToString("000")); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + } + } + } + internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) { DateTime dateTime = new(ticks); @@ -411,6 +430,7 @@ internal abstract class XPath private static byte[] GetBytes() => [ + 0, 1, 2, 3, diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 48b4913..4a99f80 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -39,7 +39,6 @@ - diff --git a/Tests/UnitTestHardCoded.cs b/Tests/UnitTestHardCoded.cs index 1a0b65d..d4de3dc 100644 --- a/Tests/UnitTestHardCoded.cs +++ b/Tests/UnitTestHardCoded.cs @@ -54,6 +54,55 @@ public partial class UnitTestHardCoded _PropertyConfiguration = propertyConfiguration; } + [TestMethod] + public void TestMethodId() + { + int id; + id = 748161839; + string imageTrueTruePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: true, hasDateTimeOriginal: true, index: null); + Assert.AreEqual("816184700398", imageTrueTruePositive); + string imageTrueFalsePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: true, hasDateTimeOriginal: false, index: null); + Assert.AreEqual("816184700398", imageTrueFalsePositive); + string imageFalseFalsePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: false, hasDateTimeOriginal: false, index: null); + Assert.AreEqual("816184700397", imageFalseFalsePositive); + string imageFalseTruePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: false, hasDateTimeOriginal: true, index: null); + Assert.AreEqual("816184700399", imageFalseTruePositive); + try + { string videoTrueTruePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: true, hasDateTimeOriginal: true, index: null); } + catch (Exception) + { } + try + { string videoTrueFalsePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: true, hasDateTimeOriginal: false, index: null); } + catch (Exception) + { } + string videoFalseFalsePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: false, hasDateTimeOriginal: false, index: null); + Assert.AreEqual("816184700395", videoFalseFalsePositive); + string videoFalseTruePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: false, hasDateTimeOriginal: true, index: null); + Assert.AreEqual("816184700396", videoFalseTruePositive); + id = -748161839; + string imageTrueTrueNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: true, hasDateTimeOriginal: true, index: null); + Assert.AreEqual("816184700392", imageTrueTrueNegative); + string imageTrueFalseNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: true, hasDateTimeOriginal: false, index: null); + Assert.AreEqual("816184700392", imageTrueFalseNegative); + string imageFalseFalseNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: false, hasDateTimeOriginal: false, index: null); + Assert.AreEqual("816184700393", imageFalseFalseNegative); + string imageFalseTrueNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: false, hasDateTimeOriginal: true, index: null); + Assert.AreEqual("816184700391", imageFalseTrueNegative); + try + { string videoTrueTrueNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: true, hasDateTimeOriginal: true, index: null); } + catch (Exception) + { } + try + { string videoTrueFalseNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: true, hasDateTimeOriginal: false, index: null); } + catch (Exception) + { } + string videoFalseFalseNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: false, hasDateTimeOriginal: false, index: null); + Assert.AreEqual("816184700390", videoFalseFalseNegative); + string videoFalseTrueNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: false, hasDateTimeOriginal: true, index: null); + Assert.AreEqual("816184700394", videoFalseTrueNegative); + NonThrowTryCatch(); + } + private static void NonThrowTryCatch() { try @@ -61,18 +110,6 @@ public partial class UnitTestHardCoded catch (Exception) { } } - [TestMethod] - public void TestMethodNull() - { - Assert.IsFalse(_AppSettings is null); - Assert.IsFalse(_Configuration is null); - Assert.IsFalse(_IsEnvironment is null); - Assert.IsFalse(_WorkingDirectory is null); - Assert.IsFalse(_ConfigurationRoot is null); - Assert.IsFalse(_PropertyConfiguration is null); - NonThrowTryCatch(); - } - [TestMethod] public void TestMethodDel() { @@ -86,30 +123,6 @@ public partial class UnitTestHardCoded NonThrowTryCatch(); } - [TestMethod] - public void TestMethodGetApproximateYears() - { - string personDisplayDirectory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/^/Sydney Dupray^9"); - if (Directory.Exists(Directory.GetDirectoryRoot(personDisplayDirectory)) && Directory.Exists(personDisplayDirectory)) - { - char numberSign = '#'; - string? minusOne = null; - char[] personCharacters = ['^']; - string personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory); - string personBirthdayFormat = _Configuration.PropertyConfiguration.PersonBirthdayFormat; - string[] personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly); - List<(string, PersonBirthday)> collection = IPersonBirthday.GetPersonBirthdays(personBirthdayFormat, personKeyDirectories, personDisplayDirectoryName); - int? approximateYears = IAge.GetApproximateYears(personCharacters, personDisplayDirectoryName); - if (approximateYears is null) - throw new NullReferenceException(nameof(approximateYears)); - Assert.IsNotNull(approximateYears); - Assert.IsTrue(approximateYears.Value == 9); - string? change = IPersonContainer.VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, collection); - Assert.IsNull(change); - } - NonThrowTryCatch(); - } - [TestMethod] public void TestMethodDel2() { @@ -123,14 +136,35 @@ public partial class UnitTestHardCoded NonThrowTryCatch(); } - private static (string?, string, string) Get(string[] segments) + [TestMethod] + public void TestMethodNull() { - (string?, string, string) result; - if ((!segments[0].Contains('#') && (segments[3].Contains('~') || segments[3].Contains('#'))) || (segments[0].Contains('#') && !segments[3].Contains('#'))) - result = new(null, segments[3], segments[4]); - else - result = new(segments[0], segments[3], segments[4]); - return result; + Assert.IsNotNull(_AppSettings); + Assert.IsNotNull(_Configuration); + Assert.IsNotNull(_IsEnvironment); + Assert.IsNotNull(_WorkingDirectory); + Assert.IsNotNull(_ConfigurationRoot); + Assert.IsNotNull(_PropertyConfiguration); + NonThrowTryCatch(); + } + + [TestMethod] + public void TestMethodRename() + { + // string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}"; + string directory = $"D:/1-Images-A/Images-{_Git}"; + // string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}"; + if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) + { + string[] directories = Directory.GetDirectories(directory, "*;*", SearchOption.AllDirectories); + foreach (string subDirectory in directories.OrderByDescending(l => l.Length - l.Replace(@"\", string.Empty).Length)) + { + if (!subDirectory.EndsWith(";9")) + continue; + Directory.Move(subDirectory, $"{subDirectory[..^2]} !9"); + } + } + NonThrowTryCatch(); } [TestMethod] @@ -189,64 +223,14 @@ public partial class UnitTestHardCoded NonThrowTryCatch(); } - [TestMethod] - public void TestMethodRenameAbandoned() + private static (string?, string, string) Get(string[] segments) { - string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/!/Abandoned"); - if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) - { - string checkFile; - string[] files = Directory.GetFiles(directory, "*.abd", SearchOption.TopDirectoryOnly); - foreach (string file in files) - { - checkFile = file[..^4]; - if (File.Exists(checkFile)) - continue; - File.Move(file, checkFile); - } - Assert.IsTrue(true); - } - NonThrowTryCatch(); - } - - [TestMethod] - public void TestMethodRenameDelete() - { - string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A)Property/{_Git}", "/{}"); - if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) - { - string checkFile; - string[] files = Directory.GetFiles(directory, "*.del", SearchOption.AllDirectories); - foreach (string file in files) - { - checkFile = file[..^4]; - if (File.Exists(checkFile)) - continue; - File.Move(file, checkFile); - } - Assert.IsTrue(true); - } - NonThrowTryCatch(); - } - - [TestMethod] - public void TestMethodRenameOld() - { - string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/()"; - if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) - { - string checkFile; - string[] files = Directory.GetFiles(directory, "*.old", SearchOption.AllDirectories); - foreach (string file in files) - { - checkFile = file[..^4]; - if (File.Exists(checkFile)) - continue; - File.Move(file, checkFile); - } - Assert.IsTrue(true); - } - NonThrowTryCatch(); + (string?, string, string) result; + if ((!segments[0].Contains('#') && (segments[3].Contains('~') || segments[3].Contains('#'))) || (segments[0].Contains('#') && !segments[3].Contains('#'))) + result = new(null, segments[3], segments[4]); + else + result = new(segments[0], segments[3], segments[4]); + return result; } [TestMethod] @@ -285,33 +269,59 @@ public partial class UnitTestHardCoded } [TestMethod] - public void TestMethodRename() + public void TestMethodRenameOld() { - // string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}"; - string directory = $"D:/1-Images-A/Images-{_Git}"; - // string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}"; + string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/()"; if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { - string[] directories = Directory.GetDirectories(directory, "*;*", SearchOption.AllDirectories); - foreach (string subDirectory in directories.OrderByDescending(l => l.Length - l.Replace(@"\", string.Empty).Length)) + string checkFile; + string[] files = Directory.GetFiles(directory, "*.old", SearchOption.AllDirectories); + foreach (string file in files) { - if (!subDirectory.EndsWith(";9")) + checkFile = file[..^4]; + if (File.Exists(checkFile)) continue; - Directory.Move(subDirectory, $"{subDirectory[..^2]} !9"); + File.Move(file, checkFile); } + Assert.IsTrue(true); } NonThrowTryCatch(); } [TestMethod] - public void TestMethodRenameForUnknown() + public void TestMethodImmichAsset() { - string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/(RectInt-2023-06-19-less-0.99)"; + if (!string.IsNullOrEmpty(_Configuration.ImmichAssetsFile) && File.Exists(_Configuration.ImmichAssetsFile)) + { + Dictionary keyValuePairs = []; + string json = File.ReadAllText(_Configuration.ImmichAssetsFile); + ImmichAsset[]? immichAssets = JsonSerializer.Deserialize(json, ImmichAssetCollectionSourceGenerationContext.Default.ImmichAssetArray); + if (immichAssets is not null) + { + foreach (ImmichAsset immichAsset in immichAssets) + keyValuePairs.Add(immichAsset.OriginalPath, immichAsset); + } + Assert.IsTrue(keyValuePairs.Count > 0); + } + NonThrowTryCatch(); + } + + [TestMethod] + public void TestMethodRenameDelete() + { + string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A)Property/{_Git}", "/{}"); if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { - string[] files = Directory.GetFiles(directory, "*.unk", SearchOption.AllDirectories); + string checkFile; + string[] files = Directory.GetFiles(directory, "*.del", SearchOption.AllDirectories); foreach (string file in files) - File.Move(file, file[..^4]); + { + checkFile = file[..^4]; + if (File.Exists(checkFile)) + continue; + File.Move(file, checkFile); + } + Assert.IsTrue(true); } NonThrowTryCatch(); } @@ -347,19 +357,58 @@ public partial class UnitTestHardCoded } [TestMethod] - public void TestMethodImmichAsset() + public void TestMethodRenameAbandoned() { - if (!string.IsNullOrEmpty(_Configuration.ImmichAssetsFile) && File.Exists(_Configuration.ImmichAssetsFile)) + string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/!/Abandoned"); + if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { - Dictionary keyValuePairs = []; - string json = File.ReadAllText(_Configuration.ImmichAssetsFile); - ImmichAsset[]? immichAssets = JsonSerializer.Deserialize(json, ImmichAssetCollectionSourceGenerationContext.Default.ImmichAssetArray); - if (immichAssets is not null) + string checkFile; + string[] files = Directory.GetFiles(directory, "*.abd", SearchOption.TopDirectoryOnly); + foreach (string file in files) { - foreach (ImmichAsset immichAsset in immichAssets) - keyValuePairs.Add(immichAsset.OriginalPath, immichAsset); + checkFile = file[..^4]; + if (File.Exists(checkFile)) + continue; + File.Move(file, checkFile); } - Assert.IsTrue(keyValuePairs.Count > 0); + Assert.IsTrue(true); + } + NonThrowTryCatch(); + } + + [TestMethod] + public void TestMethodRenameForUnknown() + { + string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/(RectInt-2023-06-19-less-0.99)"; + if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) + { + string[] files = Directory.GetFiles(directory, "*.unk", SearchOption.AllDirectories); + foreach (string file in files) + File.Move(file, file[..^4]); + } + NonThrowTryCatch(); + } + + [TestMethod] + public void TestMethodGetApproximateYears() + { + string personDisplayDirectory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/^/Sydney Dupray^9"); + if (Directory.Exists(Directory.GetDirectoryRoot(personDisplayDirectory)) && Directory.Exists(personDisplayDirectory)) + { + char numberSign = '#'; + string? minusOne = null; + char[] personCharacters = ['^']; + string personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory); + string personBirthdayFormat = _Configuration.PropertyConfiguration.PersonBirthdayFormat; + string[] personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly); + List<(string, PersonBirthday)> collection = IPersonBirthday.GetPersonBirthdays(personBirthdayFormat, personKeyDirectories, personDisplayDirectoryName); + int? approximateYears = IAge.GetApproximateYears(personCharacters, personDisplayDirectoryName); + if (approximateYears is null) + throw new NullReferenceException(nameof(approximateYears)); + Assert.IsNotNull(approximateYears); + Assert.AreEqual(9, approximateYears.Value); + string? change = IPersonContainer.VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, collection); + Assert.IsNull(change); } NonThrowTryCatch(); } diff --git a/Tests/UnitTestResize.cs b/Tests/UnitTestResize.cs index a938053..2f762d5 100644 --- a/Tests/UnitTestResize.cs +++ b/Tests/UnitTestResize.cs @@ -56,13 +56,6 @@ public class UnitTestResize _PropertyConfiguration = propertyConfiguration; } - private static void NonThrowTryCatch() - { - try - { throw new Exception(); } - catch (Exception) { } - } - [TestMethod] public void TestMethodNull() { @@ -75,13 +68,80 @@ public class UnitTestResize NonThrowTryCatch(); } - private A_Property GetPropertyLogic(bool reverse, string aResultsFullGroupDirectory) + private static void NonThrowTryCatch() { - A_Property result; - if (_Configuration?.PropertyConfiguration is null) - throw new NullReferenceException(nameof(_PropertyConfiguration)); - result = new(_AppSettings.MaxDegreeOfParallelism, _PropertyConfiguration, _Configuration.OutputExtension, reverse, aResultsFullGroupDirectory); - return result; + try + { throw new Exception(); } + catch (Exception) { } + } + + [TestMethod] + public void TestMethodResize() + { + if (_PropertyConfiguration.NumberOfJitters is null) + throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); + if (_PropertyConfiguration.NumberOfTimesToUpsample is null) + throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample)); + // string sourceFileName = "100000507001158650387.jpg"; + // string sourceDirectoryName = "Facebook/2023.2 Summer Facebook"; + string sourceFileName = "105131603001106320328.jpg"; + string sourceDirectoryName = "Mike iCloud Have Date Taken 2022 !9"; + Item item; + bool reverse = false; + bool isArchive = false; + FileHolder resizedFileHolder; + long ticks = DateTime.Now.Ticks; + List parseExceptions = []; + const bool isValidImageFormatExtension = true; + List> subFileTuples = []; + int length = _PropertyConfiguration.RootDirectory.Length; + string[] changesFrom = [nameof(A_Property)]; + string outputResolution = _Configuration.OutputResolutions[0]; + bool outputResolutionHasNumber = outputResolution.Any(char.IsNumber); + (string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution); + (string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories(); + Shared.Models.Methods.IBlurHasher blurHasher = new BlurHash.Models.C2_BlurHasher(_PropertyConfiguration); + A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory); + string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}"); + if (!Directory.Exists(aPropertySingletonDirectory)) + _ = Directory.CreateDirectory(aPropertySingletonDirectory); + (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(_Configuration.OutputExtension, _Configuration.OutputQuality); + B_Metadata metadata = new(null, _PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, ticks, bResultsFullGroupDirectory); + _ = metadata.ToString(); + C_Resize resize = new(_PropertyConfiguration, _Configuration.ForceResizeLastWriteTimeToCreationTime, _Configuration.OverrideForResizeImages, _Configuration.PropertiesChangedForResize, _Configuration.ValidResolutions, imageCodecInfo, encoderParameters, filenameExtension); + _ = resize.ToString(); + bool isUniqueFileName = false; + bool? isNotUniqueAndNeedsReview = null; + FileHolder sourceDirectoryFileHolder = IFileHolder.Get(".json"); + string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName)); + FileHolder fileHolder = IFileHolder.Get(Path.Combine(sourceDirectory, sourceFileName)); + FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null); + Assert.IsNotNull(filePath.Id); + string relativePath = IPath.GetRelativePath(fileHolder.FullName, length); + ExifDirectory? exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath); + string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName)); + propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory); + if (outputResolutionHasNumber) + resize.SetAngleBracketCollection(cResultsFullGroupDirectory, sourceDirectory); + resize.Update(cResultsFullGroupDirectory); + blurHasher.Update(cResultsFullGroupDirectory); + item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, exifDirectory, false, false, false); + if (item.ExifDirectory is null) + throw new NullReferenceException(nameof(item.ExifDirectory)); + resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value); + item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder); + MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item); + Dictionary outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem); + Assert.IsNotNull(mappingFromItem.ResizedFileHolder); + resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize); + string blurHash = blurHasher.Encode(resizedFileHolder); + Assert.IsNotNull(blurHash); + exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); + string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase); + File.WriteAllText("../../../.json", json); + MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory); + double? distance = geoLocation is null ? null : Metadata.Models.Stateless.Methods.IMetadata.GetDistance(1, 1, geoLocation.Latitude, geoLocation.Longitude); + NonThrowTryCatch(); } private (string, string) GetResultsFullGroupDirectories() @@ -117,77 +177,13 @@ public class UnitTestResize return new(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); } - [TestMethod] - public void TestMethodResize() + private A_Property GetPropertyLogic(bool reverse, string aResultsFullGroupDirectory) { - if (_PropertyConfiguration.NumberOfJitters is null) - throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); - if (_PropertyConfiguration.NumberOfTimesToUpsample is null) - throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample)); - // string sourceFileName = "100000507001158650387.jpg"; - // string sourceDirectoryName = "Facebook/2023.2 Summer Facebook"; - string sourceFileName = "105131603001106320328.jpg"; - string sourceDirectoryName = "Mike iCloud Have Date Taken 2022 !9"; - Item item; - bool reverse = false; - bool isArchive = false; - FileHolder resizedFileHolder; - List parseExceptions = []; - Shared.Models.Property? property = null; - const bool isValidImageFormatExtension = true; - List> subFileTuples = []; - int length = _PropertyConfiguration.RootDirectory.Length; - string[] changesFrom = [nameof(A_Property)]; - string outputResolution = _Configuration.OutputResolutions[0]; - bool outputResolutionHasNumber = outputResolution.Any(char.IsNumber); - (string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution); - (string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories(); - Shared.Models.Methods.IBlurHasher blurHasher = new BlurHash.Models.C2_BlurHasher(_PropertyConfiguration); - A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory); - string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}"); - if (!Directory.Exists(aPropertySingletonDirectory)) - _ = Directory.CreateDirectory(aPropertySingletonDirectory); - (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(_Configuration.OutputExtension, _Configuration.OutputQuality); - B_Metadata metadata = new(_PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory); - _ = metadata.ToString(); - C_Resize resize = new(_PropertyConfiguration, _Configuration.ForceResizeLastWriteTimeToCreationTime, _Configuration.OverrideForResizeImages, _Configuration.PropertiesChangedForResize, _Configuration.ValidResolutions, imageCodecInfo, encoderParameters, filenameExtension); - _ = resize.ToString(); - bool isUniqueFileName = false; - bool? isNotUniqueAndNeedsReview = null; - FileHolder sourceDirectoryFileHolder = IFileHolder.Get(".json"); - string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName)); - FileHolder fileHolder = IFileHolder.Get(Path.Combine(sourceDirectory, sourceFileName)); - FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null); - Assert.IsNotNull(filePath.Id); - string relativePath = IPath.GetRelativePath(fileHolder.FullName, length); - string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName)); - propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory); - if (outputResolutionHasNumber) - resize.SetAngleBracketCollection(cResultsFullGroupDirectory, sourceDirectory); - resize.Update(cResultsFullGroupDirectory); - blurHasher.Update(cResultsFullGroupDirectory); - item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, property, false, false, false); - if (item.Property is null) - { - property = propertyLogic.GetProperty(metadata, item, subFileTuples, parseExceptions); - item.Update(property); - } - if (property is null || item.Property is null) - throw new NullReferenceException(nameof(property)); - resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value); - item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder); - MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item); - Dictionary outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.Property, mappingFromItem); - Assert.IsNotNull(mappingFromItem.ResizedFileHolder); - resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize); - string blurHash = blurHasher.Encode(resizedFileHolder); - Assert.IsNotNull(blurHash); - ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); - string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase); - File.WriteAllText("../../../.json", json); - MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory); - double? distance = geoLocation is null ? null : Metadata.Models.Stateless.Methods.IMetadata.GetDistance(1, 1, geoLocation.Latitude, geoLocation.Longitude); - NonThrowTryCatch(); + A_Property result; + if (_Configuration?.PropertyConfiguration is null) + throw new NullReferenceException(nameof(_PropertyConfiguration)); + result = new(_AppSettings.MaxDegreeOfParallelism, _PropertyConfiguration, _Configuration.OutputExtension, reverse, aResultsFullGroupDirectory); + return result; } } \ No newline at end of file diff --git a/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj b/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj index 7846d00..8229637 100644 --- a/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj +++ b/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj @@ -5,8 +5,8 @@ enable win-x64 net9.0 - ecbdc76d-6037-4046-86a4-1a7626a3d342 - + ecbdc76d-6037-4046-86a4-1a7626a3d342 + trx XPlat Code Coverage @@ -39,7 +39,6 @@ - diff --git a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs index 26d7743..dc468c7 100644 --- a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs +++ b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs @@ -58,33 +58,113 @@ public class UnitTestFace _PropertyConfiguration = propertyConfiguration; } - private static void NonThrowTryCatch() - { - try - { throw new Exception(); } - catch (Exception) { } - } - [TestMethod] - public void TestConfiguration() + public void TestMethodFace() { - 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 (_PropertyConfiguration.NumberOfJitters is null) + throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); + if (_PropertyConfiguration.NumberOfTimesToUpsample is null) + throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample)); + string sourceFileName = "100000507001158650387.jpg"; + string sourceDirectoryName = "Facebook/2023.2 Summer Facebook"; + Item item; + bool reverse = false; + bool isArchive = false; + FileHolder resizedFileHolder; + long ticks = DateTime.Now.Ticks; + List parseExceptions = []; + const bool isValidImageFormatExtension = true; + List> subFileTuples = []; + int length = _PropertyConfiguration.RootDirectory.Length; + string[] changesFrom = [nameof(A_Property)]; + string outputResolution = _Configuration.OutputResolutions[0]; + bool outputResolutionHasNumber = outputResolution.Any(char.IsNumber); + (string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution); + (string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories(); + Shared.Models.Methods.IBlurHasher blurHasher = new BlurHash.Models.C2_BlurHasher(_PropertyConfiguration); + A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory); + string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}"); + if (!Directory.Exists(aPropertySingletonDirectory)) + _ = Directory.CreateDirectory(aPropertySingletonDirectory); + (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(_Configuration.OutputExtension, _Configuration.OutputQuality); + B_Metadata metadata = new(null, _PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, ticks, bResultsFullGroupDirectory); + _ = metadata.ToString(); + C_Resize resize = new(_PropertyConfiguration, _Configuration.ForceResizeLastWriteTimeToCreationTime, _Configuration.OverrideForResizeImages, _Configuration.PropertiesChangedForResize, _Configuration.ValidResolutions, imageCodecInfo, encoderParameters, filenameExtension); + _ = resize.ToString(); + bool isUniqueFileName = false; + bool? isNotUniqueAndNeedsReview = null; + FileHolder sourceDirectoryFileHolder = IFileHolder.Get(".json"); + string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName)); + FileHolder fileHolder = IFileHolder.Get(Path.Combine(sourceDirectory, sourceFileName)); + FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null); + Assert.IsNotNull(filePath.Id); + string relativePath = IPath.GetRelativePath(fileHolder.FullName, length); + ExifDirectory? exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath); + string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName)); + propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory); + if (outputResolutionHasNumber) + resize.SetAngleBracketCollection(cResultsFullGroupDirectory, sourceDirectory); + resize.Update(cResultsFullGroupDirectory); + blurHasher.Update(cResultsFullGroupDirectory); + item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, exifDirectory, false, false, false); + if (item.ExifDirectory is null) + throw new NullReferenceException(nameof(item.ExifDirectory)); + resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value); + item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder); + MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item); + Dictionary outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem); + Assert.IsNotNull(mappingFromItem.ResizedFileHolder); + resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize); + string blurHash = blurHasher.Encode(resizedFileHolder); + Assert.IsNotNull(blurHash); + exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); + string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase); + File.WriteAllText("../../../.json", json); + Image image = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName); + Assert.IsNotNull(image); + (Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(_Configuration); + FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, model, modelParameter, predictorModel); + List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; + collection = faceRecognition.GetCollection(image, locations: [], includeFaceEncoding: true, includeFaceParts: true); + Assert.IsTrue(collection.Count == 2); + List faceDistanceEncodings = (from l in collection where l.FaceEncoding is not null select new FaceDistance(l.FaceEncoding)).ToList(); + List faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncodings[0]); + Assert.IsTrue(faceDistanceLengths.Count == 2); + Assert.IsNotNull(sourceFileName); NonThrowTryCatch(); } - [TestMethod] - public void TestMethodNull() + private (string, string) GetResultsFullGroupDirectories() { - Assert.IsFalse(_AppSettings is null); - Assert.IsFalse(_Configuration is null); - Assert.IsFalse(_IsEnvironment is null); - Assert.IsFalse(_WorkingDirectory is null); - Assert.IsFalse(_ConfigurationRoot is null); - Assert.IsFalse(_PropertyConfiguration is null); - NonThrowTryCatch(); + string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( + _PropertyConfiguration, + nameof(A_Property), + string.Empty, + includeResizeGroup: false, + includeModel: false, + includePredictorModel: false); + string bResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( + _PropertyConfiguration, + nameof(B_Metadata), + string.Empty, + includeResizeGroup: false, + includeModel: false, + includePredictorModel: false); + return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory); + } + + private (string, string, string) GetResultsFullGroupDirectories(string outputResolution) + { + string cResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( + _PropertyConfiguration, + nameof(C_Resize), + outputResolution, + includeResizeGroup: true, + includeModel: false, + includePredictorModel: false); + string dResultsFullGroupDirectory = string.Empty; + string d2ResultsFullGroupDirectory = string.Empty; + return new(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); } private A_Property GetPropertyLogic(bool reverse, string aResultsFullGroupDirectory) @@ -137,6 +217,25 @@ public class UnitTestFace return result; } + private static void NonThrowTryCatch() + { + try + { throw new Exception(); } + catch (Exception) { } + } + + [TestMethod] + public void TestMethodNull() + { + Assert.IsFalse(_AppSettings is null); + Assert.IsFalse(_Configuration is null); + Assert.IsFalse(_IsEnvironment is null); + Assert.IsFalse(_WorkingDirectory is null); + Assert.IsFalse(_ConfigurationRoot is null); + Assert.IsFalse(_PropertyConfiguration is null); + NonThrowTryCatch(); + } + [TestMethod] public void TestMethodRoundB() { @@ -159,116 +258,13 @@ public class UnitTestFace NonThrowTryCatch(); } - private (string, string) GetResultsFullGroupDirectories() - { - string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( - _PropertyConfiguration, - nameof(A_Property), - string.Empty, - includeResizeGroup: false, - includeModel: false, - includePredictorModel: false); - string bResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( - _PropertyConfiguration, - nameof(B_Metadata), - string.Empty, - includeResizeGroup: false, - includeModel: false, - includePredictorModel: false); - return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory); - } - - private (string, string, string) GetResultsFullGroupDirectories(string outputResolution) - { - string cResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( - _PropertyConfiguration, - nameof(C_Resize), - outputResolution, - includeResizeGroup: true, - includeModel: false, - includePredictorModel: false); - string dResultsFullGroupDirectory = string.Empty; - string d2ResultsFullGroupDirectory = string.Empty; - return new(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); - } - [TestMethod] - public void TestMethodFace() + public void TestConfiguration() { - if (_PropertyConfiguration.NumberOfJitters is null) - throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); - if (_PropertyConfiguration.NumberOfTimesToUpsample is null) - throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample)); - string sourceFileName = "100000507001158650387.jpg"; - string sourceDirectoryName = "Facebook/2023.2 Summer Facebook"; - Item item; - bool reverse = false; - bool isArchive = false; - FileHolder resizedFileHolder; - List parseExceptions = []; - Shared.Models.Property? property = null; - const bool isValidImageFormatExtension = true; - List> subFileTuples = []; - int length = _PropertyConfiguration.RootDirectory.Length; - string[] changesFrom = [nameof(A_Property)]; - string outputResolution = _Configuration.OutputResolutions[0]; - bool outputResolutionHasNumber = outputResolution.Any(char.IsNumber); - (string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution); - (string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories(); - Shared.Models.Methods.IBlurHasher blurHasher = new BlurHash.Models.C2_BlurHasher(_PropertyConfiguration); - A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory); - string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}"); - if (!Directory.Exists(aPropertySingletonDirectory)) - _ = Directory.CreateDirectory(aPropertySingletonDirectory); - (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(_Configuration.OutputExtension, _Configuration.OutputQuality); - B_Metadata metadata = new(_PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory); - _ = metadata.ToString(); - C_Resize resize = new(_PropertyConfiguration, _Configuration.ForceResizeLastWriteTimeToCreationTime, _Configuration.OverrideForResizeImages, _Configuration.PropertiesChangedForResize, _Configuration.ValidResolutions, imageCodecInfo, encoderParameters, filenameExtension); - _ = resize.ToString(); - bool isUniqueFileName = false; - bool? isNotUniqueAndNeedsReview = null; - FileHolder sourceDirectoryFileHolder = IFileHolder.Get(".json"); - string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName)); - FileHolder fileHolder = IFileHolder.Get(Path.Combine(sourceDirectory, sourceFileName)); - FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null); - Assert.IsNotNull(filePath.Id); - string relativePath = IPath.GetRelativePath(fileHolder.FullName, length); - string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName)); - propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory); - if (outputResolutionHasNumber) - resize.SetAngleBracketCollection(cResultsFullGroupDirectory, sourceDirectory); - resize.Update(cResultsFullGroupDirectory); - blurHasher.Update(cResultsFullGroupDirectory); - item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, property, false, false, false); - if (item.Property is null) - { - property = propertyLogic.GetProperty(metadata, item, subFileTuples, parseExceptions); - item.Update(property); - } - if (property is null || item.Property is null) - throw new NullReferenceException(nameof(property)); - resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value); - item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder); - MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item); - Dictionary outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.Property, mappingFromItem); - Assert.IsNotNull(mappingFromItem.ResizedFileHolder); - resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize); - string blurHash = blurHasher.Encode(resizedFileHolder); - Assert.IsNotNull(blurHash); - ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); - string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase); - File.WriteAllText("../../../.json", json); - Image image = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName); - Assert.IsNotNull(image); - (Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(_Configuration); - FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, model, modelParameter, predictorModel); - List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; - collection = faceRecognition.GetCollection(image, locations: [], includeFaceEncoding: true, includeFaceParts: true); - Assert.IsTrue(collection.Count == 2); - List faceDistanceEncodings = (from l in collection where l.FaceEncoding is not null select new FaceDistance(l.FaceEncoding)).ToList(); - List faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncodings[0]); - Assert.IsTrue(faceDistanceLengths.Count == 2); - Assert.IsNotNull(sourceFileName); + 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!"); NonThrowTryCatch(); } diff --git a/View-by-Distance-MKLink-Console.sln b/View-by-Distance-MKLink-Console.sln index 108c9e6..913b79f 100644 --- a/View-by-Distance-MKLink-Console.sln +++ b/View-by-Distance-MKLink-Console.sln @@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlurHash", "BlurHash\BlurHa EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Copy-Distinct", "Copy-Distinct\Copy-Distinct.csproj", "{E08CB662-FF25-48DF-A378-A770E1EFBA56}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Date-Group", "Date-Group\Date-Group.csproj", "{DFEDB5F9-AFFC-40A2-9FEC-9B84C83B63D9}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delete-By-Distinct", "Delete-By-Distinct\Delete-By-Distinct.csproj", "{3F00BDD5-75F8-470C-ACED-1A26FDC8D7B3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delete-By-Relative", "Delete-By-Relative\Delete-By-Relative.csproj", "{9DFCA595-80AA-4E78-A9AF-5B4AB4D737C4}" @@ -23,8 +21,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Search", "Drag-Dr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Set-Property-Item", "Drag-Drop-Set-Property-Item\Drag-Drop-Set-Property-Item.csproj", "{BFF75D8C-48E6-4B84-B480-3E5A4F9B2DD8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicate-Search", "Duplicate-Search\Duplicate-Search.csproj", "{48E87D9B-B802-467A-BDC7-E86F7FD01D5C}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Face", "Face\Face.csproj", "{A12E19E5-59C0-40D4-B807-DF1334D4906D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FaceParts", "FaceParts\FaceParts.csproj", "{919525B1-60BA-40C6-BA66-6F7F4C526E01}" @@ -51,14 +47,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrepareForOld", "PrepareFor EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Property-Compare", "Property-Compare\Property-Compare.csproj", "{692AA058-F142-44B0-88BC-F22AB5BE5EDF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Property", "Property\Property.csproj", "{964B969A-719C-48AF-86C0-F97AF1397347}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rename", "Rename\Rename.csproj", "{83FD089F-8034-4597-B87F-87D343C0486B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resize", "Resize\Resize.csproj", "{27D0D869-394D-4B07-83DF-2095B16026FC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Set-Created-Date", "Set-Created-Date\Set-Created-Date.csproj", "{B067643E-9F59-46A1-A001-ACF4661F059C}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{B4FB6B43-36EC-404D-B934-5C695C6E32CC}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsWithFaceRecognitionDotNet", "TestsWithFaceRecognitionDotNet\TestsWithFaceRecognitionDotNet.csproj", "{A67D73C7-A1A1-4443-B681-776339CFA08A}"