From bd03bba63eb0222115d98b0a2dc9602a779fb242 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sun, 6 Apr 2025 15:51:08 -0700 Subject: [PATCH] Not tested --- .gitignore | 3 + .vscode/mklink.md | 11 + .vscode/tasks.json | 36 ++ .../Models/Stateless/Methods/Container.cs | 594 +++++++----------- .../Models/Stateless/Methods/IContainer.cs | 8 +- Copy-Distinct/CopyDistinct.cs | 140 +++-- Instance/DlibDotNet.cs | 428 ++++++++----- Metadata/Models/B_Metadata.cs | 137 ++-- Property/Models/Stateless/Property.cs | 4 +- Shared/Models/C_Resize.cs | 34 + Shared/Models/D2_FaceParts.cs | 24 + Shared/Models/D_Face.cs | 24 + Shared/Models/FilePair.cs | 10 +- Shared/Models/FilePath.cs | 34 +- Shared/Models/Stateless/Methods/FilePair.cs | 151 +++++ Shared/Models/Stateless/Methods/IDate.cs | 30 + 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 +- Shared/Models/Stateless/Methods/IPath.cs | 6 + Shared/Models/Stateless/Methods/Id.cs | 29 +- Shared/Models/Stateless/Methods/MetaBase.cs | 67 ++ Shared/Models/Stateless/Methods/XDate.cs | 252 ++++++++ Shared/Models/Stateless/Methods/XDirectory.cs | 231 +++---- Shared/Models/Stateless/Methods/XPath.cs | 32 +- Tests/UnitTestHardCoded.cs | 291 +++++---- .../TestsWithFaceRecognitionDotNet.csproj | 4 +- 28 files changed, 1727 insertions(+), 949 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/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..83024f0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -100,6 +100,42 @@ ], "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": "File-Folder-Helper AOT s X Day-Helper-2025-03-20", "type": "shell", diff --git a/Container/Models/Stateless/Methods/Container.cs b/Container/Models/Stateless/Methods/Container.cs index 8fae951..8f6109f 100644 --- a/Container/Models/Stateless/Methods/Container.cs +++ b/Container/Models/Stateless/Methods/Container.cs @@ -10,7 +10,7 @@ internal abstract class Container { private record FilePair(bool IsUnique, - List Collection, + List Collection, FilePath FilePath, Item Item); @@ -62,21 +62,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 +106,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,415 +127,95 @@ 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? __, 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? fileSizeChanged = property is not null ? property.FileSize != filePair.FilePath.Length : null; + bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePair.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) + bool? isArchive = filePair.FilePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePair.FilePath.Id.Value, out Identifier? identifier); + if (property 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]; + 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) { - 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 = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != 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, property, 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) @@ -543,7 +225,7 @@ internal abstract class Container result = null; else { - string json = File.ReadAllText(filePair.Match); + string json = File.ReadAllText(filePair.Match.FullName); if (string.IsNullOrEmpty(json)) result = null; else @@ -552,36 +234,18 @@ internal abstract class Container 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) { @@ -664,4 +328,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..61f1e02 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,72 +95,6 @@ public class CopyDistinct return (move, new(filesCollection), anyLenFiles, moveBack); } - private static (string[], List<(FilePath, string)>) GetMoveBackToDoCollection(Property.Models.Configuration propertyConfiguration, ReadOnlyCollection filesCollection) - { - List<(FilePath, string)> results = []; - string key; - string? value; - string fileName; - FilePath filePath; - string? directory; - FileHolder fileHolder; - string destinationFile; - List distinctFound = []; - List distinctNeeded = []; - List distinctDirectories = []; - Dictionary nameToPath = []; - for (int i = 1; i < 3; i++) - { - foreach (string[] files in filesCollection) - { - foreach (string file in files.Reverse()) - { - fileName = Path.GetFileName(file); - if (fileName.EndsWith("len")) - { - key = fileName[..^3]; - destinationFile = file[..^3]; - if (nameToPath.ContainsKey(key)) - continue; - nameToPath.Add(key, destinationFile); - } - else - { - if (!distinctNeeded.Contains(file)) - distinctNeeded.Add(file); - if (!nameToPath.ContainsKey(fileName)) - continue; - if (distinctFound.Contains(file)) - continue; - distinctFound.Add(file); - } - } - } - } - foreach (string[] files in filesCollection) - { - foreach (string file in files) - { - // if (distinctNeeded.Count != distinctFound.Count) - // continue; - fileName = Path.GetFileName(file); - if (fileName.EndsWith("len")) - continue; - if (!nameToPath.TryGetValue(fileName, out value)) - continue; - directory = Path.GetDirectoryName(value); - if (string.IsNullOrEmpty(directory)) - continue; - fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(file); - filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); - results.Add(new(filePath, value)); - if (!distinctDirectories.Contains(directory)) - distinctDirectories.Add(directory); - } - } - return (distinctDirectories.ToArray(), results); - } - private List CopyDistinctFilesInDirectories(ILogger? logger, bool move, ReadOnlyCollection filesCollection, bool anyLenFiles, bool moveBack) { List results = []; @@ -229,4 +165,70 @@ public class CopyDistinct return results; } + private static (string[], List<(FilePath, string)>) GetMoveBackToDoCollection(Property.Models.Configuration propertyConfiguration, ReadOnlyCollection filesCollection) + { + List<(FilePath, string)> results = []; + string key; + string? value; + string fileName; + FilePath filePath; + string? directory; + FileHolder fileHolder; + string destinationFile; + List distinctFound = []; + List distinctNeeded = []; + List distinctDirectories = []; + Dictionary nameToPath = []; + for (int i = 1; i < 3; i++) + { + foreach (string[] files in filesCollection) + { + foreach (string file in files.Reverse()) + { + fileName = Path.GetFileName(file); + if (fileName.EndsWith("len")) + { + key = fileName[..^3]; + destinationFile = file[..^3]; + if (nameToPath.ContainsKey(key)) + continue; + nameToPath.Add(key, destinationFile); + } + else + { + if (!distinctNeeded.Contains(file)) + distinctNeeded.Add(file); + if (!nameToPath.ContainsKey(fileName)) + continue; + if (distinctFound.Contains(file)) + continue; + distinctFound.Add(file); + } + } + } + } + foreach (string[] files in filesCollection) + { + foreach (string file in files) + { + // if (distinctNeeded.Count != distinctFound.Count) + // continue; + fileName = Path.GetFileName(file); + if (fileName.EndsWith("len")) + continue; + if (!nameToPath.TryGetValue(fileName, out value)) + continue; + directory = Path.GetDirectoryName(value); + if (string.IsNullOrEmpty(directory)) + continue; + fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(file); + filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); + results.Add(new(filePath, value)); + if (!distinctDirectories.Contains(directory)) + distinctDirectories.Add(directory); + } + } + return (distinctDirectories.ToArray(), results); + } + } \ No newline at end of file diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index b3d030b..64b6b5e 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -25,6 +25,11 @@ namespace View_by_Distance.Instance; public partial class DlibDotNet : IDlibDotNet, IDisposable { + public record Record(string FilesCollectionRootDirectory, + bool FilesCollectionCountIsOne, + ReadOnlyCollection> FilePathsCollection, + ReadOnlyDictionary ExifDirectoriesById); + private readonly D_Face _Faces; private ProgressBar? _ProgressBar; private readonly C_Resize _Resize; @@ -40,6 +45,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; @@ -65,6 +71,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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 +126,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(); @@ -174,6 +180,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory, List> sourceDirectoryChanges, Dictionary> fileNameToCollection, + ReadOnlyDictionary exifDirectoriesById, Container.Models.Container container, Item item, DateTime[] containerDateTimes, @@ -245,7 +252,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable 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); + if (item.FilePath.Id is null || !exifDirectoriesById.TryGetValue(item.FilePath.Id.Value, out ExifDirectory? exifDirectory)) + exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); if (_AppSettings.Places.Count > 0) { float latitude; @@ -328,6 +336,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory, List> sourceDirectoryChanges, Dictionary> fileNameToCollection, + ReadOnlyDictionary exifDirectoriesById, Container.Models.Container container, ReadOnlyCollection filteredItems, string message) @@ -336,11 +345,10 @@ 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 @@ -354,6 +362,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable d2ResultsFullGroupDirectory, sourceDirectoryChanges, fileNameToCollection, + exifDirectoriesById, container, filteredItems[i], containerDateTimes, @@ -371,6 +380,102 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return (result, exceptionsCount > 0); } + private void FullDoWork(string argZero, + string propertyRoot, + long ticks, + string aResultsFullGroupDirectory, + string fPhotoPrismSingletonDirectory, + int count, + B_Metadata metadata, + ReadOnlyDictionary exifDirectoriesById, + 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, + exifDirectoriesById, + 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 +504,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 +577,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 +624,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 +681,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; @@ -867,7 +994,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 +1046,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 +1078,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 +1095,26 @@ 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; + (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); ReadOnlyDictionary? splatNineIdentifiers = null; ReadOnlyDictionary>? keyValuePairs = null; - ReadOnlyCollection>? filePathsCollection = null; - bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks); + 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 +1126,18 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable configurationOutputResolutionsHas = true; if (!runToDoCollectionFirst) break; - (filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne) = GetFilesCollectionThenCopyOrMove(ticks, fileSearchFilter, directorySearchFilter, options, outputResolution); - keyValuePairs = FilePath.GetKeyValuePairs(filePathsCollection); + record = GetFilesCollectionThenCopyOrMove(dlibDotNet, ticks, fileSearchFilter, directorySearchFilter, bResultsFullGroupDirectory, outputResolution); + keyValuePairs = FilePath.GetKeyValuePairs(record.FilePathsCollection); splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, keyValuePairs); break; } fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent); fPhotoPrismSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultSingleton); propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory); - if (filesCollectionCountIsOne) + if (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 +1148,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 +1160,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)); 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, + keyValuePairs, + 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, + aResultsFullGroupDirectory, + fPhotoPrismSingletonDirectory, + count, + metadata, + record.ExifDirectoriesById, + readOnlyContainers, + propertyLogic, + mapLogic); ReadOnlyCollection distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); if (_Configuration.LookForAbandoned) { @@ -1044,9 +1216,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(); } } @@ -1096,19 +1268,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 +1306,17 @@ 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); + result = new(FilesCollectionRootDirectory: filesCollectionRootDirectory, + FilesCollectionCountIsOne: filesCollectionCountIsOne, + FilePathsCollection: filePathsCollection, + ExifDirectoriesById: new(exifDirectoriesById)); + 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 +1334,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 +1344,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 +1373,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 +1389,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 = []; diff --git a/Metadata/Models/B_Metadata.cs b/Metadata/Models/B_Metadata.cs index dae1ad5..cb0fb22 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/Stateless/Property.cs b/Property/Models/Stateless/Property.cs index c7ca681..7544deb 100644 --- a/Property/Models/Stateless/Property.cs +++ b/Property/Models/Stateless/Property.cs @@ -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); 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/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/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..1b1dd73 --- /dev/null +++ b/Shared/Models/Stateless/Methods/IDate.cs @@ -0,0 +1,30 @@ +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 (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 (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/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..4cc80ea --- /dev/null +++ b/Shared/Models/Stateless/Methods/MetaBase.cs @@ -0,0 +1,67 @@ +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 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..979fa87 --- /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 DateTime GetMinimum(ExifDirectory exifDirectory) + { + DateTime result; + ReadOnlyCollection results = GetDateTimes(exifDirectory); + result = results.Count == 0 ? DateTime.MinValue : results.Min(); + return result; + } + + private 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 (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/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/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj b/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj index 7846d00..30c885a 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