diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0b85cfb..b882ce1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -148,6 +148,18 @@ ], "problemMatcher": "$msCompile" }, + { + "label": "buildDuplicateSearch", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Duplicate-Search/Duplicate-Search.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 e3a2e6d..4151aa8 100644 --- a/Container/Models/Stateless/Methods/Container.cs +++ b/Container/Models/Stateless/Methods/Container.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless.Methods; @@ -8,8 +9,10 @@ namespace View_by_Distance.Container.Models.Stateless.Methods; internal abstract class Container { + private record Record(string From, string To, char A); + private record FilePair(bool IsUnique, - List Collection, + ReadOnlyCollection Collection, FilePath FilePath, Item Item); @@ -47,7 +50,7 @@ internal abstract class Container continue; foreach (Item item in filteredItems) { - if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath?.Id is null || item.ResizedFileHolder is null) continue; if (results.Contains(item.ExifDirectory.FilePath.Id.Value)) continue; @@ -57,7 +60,7 @@ internal abstract class Container return results; } - internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string _) + internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers) { int count; Models.Container[] results; @@ -66,18 +69,18 @@ internal abstract class Container const string fileSearchFilter = "*"; IDlibDotNet dlibDotNet = GetDlibDotNet(); const string directorySearchFilter = "*"; - ReadOnlyDictionary? exifDirectoriesById = null; - ReadOnlyDictionary>? keyValuePairs = null; + ReadOnlyDictionary exifDirectoriesById = new(new Dictionary()); ReadOnlyCollection filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); ReadOnlyCollection> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions); - (count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter); + ReadOnlyDictionary> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection); + (count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, idToFilePaths, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter); return (count, results); } private static IDlibDotNet GetDlibDotNet() => - throw new NotImplementedException(); + throw new NotImplementedException(nameof(IDlibDotNet)); - private static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary? exifDirectoriesById, string directorySearchFilter) + private static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary> idToFilePaths, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary exifDirectoriesById, string directorySearchFilter) { List results = []; string directory; @@ -101,11 +104,7 @@ internal abstract class Container throw new Exception(); } } - (string aResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories(); - string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); - if (!Directory.Exists(aPropertySingletonDirectory)) - _ = Directory.CreateDirectory(aPropertySingletonDirectory); - List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter); + ReadOnlyCollection filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, filesCollectionDirectory, idToFilePaths, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter); foreach (FilePair filePair in filePairs) { if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) @@ -120,104 +119,159 @@ internal abstract class Container { if (keyValuePair.Value.Count == 0) continue; - container = new(keyValuePair.Key, new(keyValuePair.Value)); + container = new(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); results.Add(container); } return (filePairs.Count, results.ToArray()); } - private static List GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary? exifDirectoriesById, string directorySearchFilter) + private static ReadOnlyCollection GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary> idToFilePaths, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary exifDirectoriesById, string directorySearchFilter) { - List results = []; + ReadOnlyCollection results; const string extension = ".json"; + Dictionary keyValuePairs = []; ReadOnlyCollection filePairs; - string jsonGroupDirectory = aPropertySingletonDirectory; int maxDegreeOfParallelism = Environment.ProcessorCount; int filesCollectionDirectoryLength = filesCollectionDirectory.Length; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; + (_, string bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); + string jsonGroupDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(jsonGroupDirectory)) + _ = Directory.CreateDirectory(jsonGroupDirectory); filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection); + string message; + message = $") {nameof(Container)} - Preloading ExifDirectory Dictionary - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - dlibDotNet.Ticks).TotalSeconds)} total second(s)"; + dlibDotNet.ConstructProgressBar(filePairs.Count, message); _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => - ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, exifDirectoriesById, filePairs[i], results)); + ParallelFor(exifDirectoriesById, filePairs[i], keyValuePairs, dlibDotNet.Tick)); + results = GetFilePairs(propertyConfiguration, idToFilePaths, splatNineIdentifiers, exifDirectoriesById, extension, keyValuePairs, filePairs, jsonGroupDirectory, filesCollectionDirectoryLength); return results; } - private static void ParallelFor(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, int rootDirectoryLength, ReadOnlyDictionary? exifDirectoriesById, Shared.Models.FilePair filePair, List results) + private static ReadOnlyCollection GetFilePairs(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary> idToFilePaths, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyDictionary exifDirectoriesById, string extension, Dictionary keyValuePairs, ReadOnlyCollection filePairs, string jsonGroupDirectory, int rootDirectoryLength) { - dlibDotNet?.Tick(); - bool abandoned = false; + List results = []; + Item item; + string json; + char? change; + bool abandoned; + bool? isArchive; + string fileName; + string directory; + FileInfo fileInfo; + bool? shouldIgnore; + DateTime? dateTime; + string fullFileName; + string jsonFileName; + string relativePath; + bool? fileSizeChanged; + char hasIgnoreKeyword; + char hasDateTimeOriginal; + CombinedEnumAndIndex cei; + List records = []; + bool? lastWriteTimeChanged; + char missingDateTimeOriginal; + ExifDirectory? exifDirectory; + bool isValidImageFormatExtension; + ReadOnlyCollection keywords; FileHolder sourceDirectoryFileHolder; - if (exifDirectoriesById is null || filePair.FilePath.Id is null || !exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out ExifDirectory? exifDirectory)) - exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); - ReadOnlyCollection keywords = IMetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories); - bool? shouldIgnore = propertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains); - bool? fileSizeChanged = exifDirectory is not null ? exifDirectory.FilePath.Length != filePair.FilePath.Length : null; - bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePair.FilePath.ExtensionLowered); - bool? isArchive = filePair.FilePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePair.FilePath.Id.Value, out Identifier? identifier); - if (exifDirectory is not null && filePair.FilePath.Id is not null && filePair.FilePath.HasIgnoreKeyword is not null && filePair.FilePath.HasDateTimeOriginal is not null) + ReadOnlyCollection? filePaths; + foreach (Shared.Models.FilePair filePair in filePairs) { - char? change; - ReadOnlyCollection? filePaths = null; - DateTime? dateTime = IDate.GetDateTimeOriginal(exifDirectory); - char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePair.FilePath).ToString()[0]; - char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0]; - char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0]; - if (shouldIgnore is not null && shouldIgnore.Value) + abandoned = false; + if (filePair.FilePath.Id is null || (!exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out exifDirectory) && !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out exifDirectory))) + exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); + keywords = IMetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories); + shouldIgnore = propertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains); + fileSizeChanged = exifDirectory?.FilePath is not null ? exifDirectory.FilePath.Length != filePair.FilePath.Length : null; + isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePair.FilePath.ExtensionLowered); + isArchive = filePair.FilePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePair.FilePath.Id.Value, out Identifier? identifier); + if (exifDirectory is not null && filePair.FilePath.Id is not null && filePair.FilePath.HasIgnoreKeyword is not null && filePair.FilePath.HasDateTimeOriginal is not null) { - if (filePair.FilePath.FileNameFirstSegment[^1] == hasIgnoreKeyword) - change = null; - else + filePaths = null; + dateTime = IDate.GetDateTimeOriginal(exifDirectory); + hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePair.FilePath).ToString()[0]; + hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0]; + missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0]; + if (shouldIgnore is not null && shouldIgnore.Value) { - change = hasIgnoreKeyword; - if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null) + if (filePair.FilePath.FileNameFirstSegment[^1] == hasIgnoreKeyword) + change = null; + else + { + change = hasIgnoreKeyword; + if (!idToFilePaths.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) && dateTime is null) + { + if (filePair.FilePath.FileNameFirstSegment[^1] == missingDateTimeOriginal) + change = null; + else + { + change = missingDateTimeOriginal; + if (!idToFilePaths.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null) + throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>"); + } + } + else if (filePair.FilePath.FileNameFirstSegment[^1] != hasDateTimeOriginal) + { + change = hasDateTimeOriginal; + if (!idToFilePaths.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) && dateTime is null) - { - if (filePair.FilePath.FileNameFirstSegment[^1] == missingDateTimeOriginal) - change = null; else - { - change = missingDateTimeOriginal; - 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 (filePair.FilePath.FileNameFirstSegment[^1] != hasDateTimeOriginal) - { - change = hasDateTimeOriginal; - if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null) - throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>"); + change = null; + if (filePaths is not null && change is not null) + RenameFile(filePair, filePair.FilePath, change.Value, filePaths); } + relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.FilePath.FullName, rootDirectoryLength, forceExtensionToLower: true); + lastWriteTimeChanged = exifDirectory?.FilePath is not null ? propertyConfiguration.PropertiesChangedForProperty || exifDirectory.FilePath.LastWriteTicks != filePair.FilePath.LastWriteTicks : null; + if (filePair.Match is not null) + sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match); + else if (!filePair.IsUnique) + sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(jsonGroupDirectory, relativePath, extension))); else - change = null; - if (filePaths is not null && change is not null) - RenameFile(filePair, filePair.FilePath, change.Value, filePaths); - } - string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.FilePath.FullName, rootDirectoryLength, forceExtensionToLower: true); - bool? lastWriteTimeChanged = exifDirectory is not null ? propertyConfiguration.PropertiesChangedForProperty || exifDirectory.FilePath.LastWriteTicks != filePair.FilePath.LastWriteTicks : null; - if (filePair.Match is not null) - sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match); - else if (!filePair.IsUnique) - sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(jsonGroupDirectory, relativePath, extension))); - else - { - string fileName = Path.GetFileName(filePair.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 && filePair.FilePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) - { - File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePair.FilePath.LastWriteTicks)); - File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); - } - Item item = Item.Get(filePair.FilePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, exifDirectory, abandoned, fileSizeChanged, lastWriteTimeChanged); - lock (results) + { + fileName = Path.GetFileName(filePair.FilePath.FullName); + cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePair.FilePath); + directory = Path.Combine(jsonGroupDirectory, cei.Combined); + jsonFileName = $"{fileName}{extension}"; + fullFileName = Path.Combine(directory, jsonFileName); + MoveIf(jsonFileName, cei, directory, fullFileName); + fileInfo = new(fullFileName); + if (exifDirectory is not null && !fileInfo.Exists) + { + json = JsonSerializer.Serialize(exifDirectory, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + fileInfo.Refresh(); + } + sourceDirectoryFileHolder = IFileHolder.Get(fileInfo); + } + if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePair.FilePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) + { + File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePair.FilePath.LastWriteTicks)); + File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); + } + item = Item.Get(filePair.FilePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, exifDirectory, abandoned, fileSizeChanged, lastWriteTimeChanged); results.Add(new(filePair.IsUnique, filePair.Collection, filePair.FilePath, item)); + } + return results.AsReadOnly(); + } + + private static void ParallelFor(ReadOnlyDictionary exifDirectoriesById, Shared.Models.FilePair filePair, Dictionary keyValuePairs, Action? tick) + { + tick?.Invoke(); + if (filePair.FilePath.Id is not null && (!exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out ExifDirectory? exifDirectory) || exifDirectory.FilePath?.Id is null)) + { + exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); + lock (keyValuePairs) + { + if (!keyValuePairs.ContainsKey(filePair.FilePath.Id.Value)) + keyValuePairs.Add(filePair.FilePath.Id.Value, exifDirectory); + } + } } private static void RenameFile(Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection filePaths) @@ -230,13 +284,13 @@ internal abstract class Container 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)) + if (!File.Exists(checkFile) && File.Exists(filePair.Match.FullName)) File.Move(filePair.Match.FullName, checkFile); } foreach (FilePath f in filePaths) { checkFile = Path.Combine(f.DirectoryFullPath, $"{f.NameWithoutExtension[..^1]}{change}{f.ExtensionLowered}"); - if (File.Exists(checkFile)) + if (File.Exists(checkFile) || !File.Exists(f.FullName)) continue; File.Move(f.FullName, checkFile); } @@ -271,7 +325,7 @@ internal abstract class Container continue; foreach (Item item in filteredItems) { - if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath?.Id is null || item.ResizedFileHolder is null) continue; if (results.Contains(item.FilePath.FileNameFirstSegment)) continue; @@ -300,7 +354,7 @@ internal abstract class Container } foreach (Item item in filteredItems) { - if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath?.Id is null || item.ResizedFileHolder is null) continue; if (distinctItems) { @@ -314,42 +368,41 @@ 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) + internal static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, 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); + ReadOnlyDictionary> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection); + (_, results) = GetContainers(dlibDotNet, propertyConfiguration, filesCollectionDirectory, idToFilePaths, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter); + AnyMoved(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filePathsCollection, idToFilePaths, directorySearchFilter); return results.AsReadOnly(); } - private static void DoGetFilePairsForRemaining(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, ReadOnlyCollection> filePathsCollection, string directorySearchFilter) + private static void AnyMoved(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> idToFilePaths, 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); + _ = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, bMetaSingletonDirectory, filePathsCollection); (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); + _ = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, cResizeSingletonDirectory, filePathsCollection); string dFaceCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultCollection); if (!Directory.Exists(dFaceCollectionDirectory)) _ = Directory.CreateDirectory(dFaceCollectionDirectory); - _ = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, dFaceCollectionDirectory, filePathsCollection, fileNamesToFiles); + _ = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, dFaceCollectionDirectory, filePathsCollection); 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); + AnyMovedFace(propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, idToFilePaths, dFaceContentDirectory); + AnyMovedDistance(propertyConfiguration, facesFileNameExtension, idToFilePaths, eDistanceContentDirectory); } - private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, string extension, string hiddenExtension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) + private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, string extension, string hiddenExtension, ReadOnlyDictionary> idToFilePaths, string jsonGroupDirectory) { string directory; string checkFile; @@ -382,26 +435,26 @@ internal abstract class Container } } if (directories.Count > 0) - AnyMovedFace(propertyConfiguration, fileNamesToFiles, directories); + AnyMovedFace(propertyConfiguration, idToFilePaths, directories); } - private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary> fileNamesToFiles, List directories) + private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary> fileNamesToFiles, List directories) { bool result = false; string checkFile; - FilePath filePath; + FilePath? filePath; string subDirectory; string directoryName; string checkDirectory; FileHolder fileHolder; string directoryNameWith; - List? collection; List directoryNames = []; + ReadOnlyCollection? collection; foreach (string directory in directories) { fileHolder = IFileHolder.Get(Path.GetFileName(directory)); - filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); - if (filePath.Id is null) + filePath = FilePath.GetNullSafe(propertyConfiguration, fileHolder, index: null); + if (filePath?.Id is null) continue; if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection)) throw new Exception(); @@ -437,7 +490,7 @@ internal abstract class Container } } - private static void AnyMovedDistance(IPropertyConfiguration propertyConfiguration, string extension, IReadOnlyDictionary> fileNamesToFiles, string jsonGroupDirectory) + private static void AnyMovedDistance(IPropertyConfiguration propertyConfiguration, string extension, ReadOnlyDictionary> idToFilePaths, string jsonGroupDirectory) { bool result = false; string checkFile; @@ -445,8 +498,8 @@ internal abstract class Container FilePath filePath; FileHolder fileHolder; string[] fileNameSegments; - List? collection; List fileNames = []; + ReadOnlyCollection? collection; string[] files = Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories); foreach (string file in files) { @@ -457,7 +510,7 @@ internal abstract class Container if (filePath.Id is null) continue; fileNameSegments = filePath.Name.Split('.'); - if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection)) + if (!idToFilePaths.TryGetValue(filePath.Id.Value, out collection)) continue; fileNames.Clear(); foreach (FilePath f in collection) diff --git a/Container/Models/Stateless/Methods/IContainer.cs b/Container/Models/Stateless/Methods/IContainer.cs index d5169ac..a39cf7d 100644 --- a/Container/Models/Stateless/Methods/IContainer.cs +++ b/Container/Models/Stateless/Methods/IContainer.cs @@ -8,17 +8,14 @@ namespace View_by_Distance.Container.Models.Stateless.Methods; public interface IContainer { - public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => - Container.GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); - public static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) => Container.GetContainerDateTimes(items); public static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => Container.GetValidImageItems(propertyConfiguration, container); - public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, null, aPropertySingletonDirectory); + public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration) => + Container.GetContainers(propertyConfiguration, splatNineIdentifiers: null); public static List GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); @@ -29,8 +26,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, ReadOnlyDictionary exifDirectoriesById) => - Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById); + public static ReadOnlyCollection GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary exifDirectoriesById) => + Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection, exifDirectoriesById); internal DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection items) => GetContainerDateTimes(items); @@ -38,8 +35,8 @@ public interface IContainer internal ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => GetValidImageItems(propertyConfiguration, container); - internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, aPropertySingletonDirectory); + internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration) => + GetContainers(propertyConfiguration); internal List TestStatic_GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); @@ -50,10 +47,10 @@ public interface IContainer internal ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); - internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); + internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers) => + Container.GetContainers(propertyConfiguration, splatNineIdentifiers); - 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); + internal ReadOnlyCollection TestStatic_GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary exifDirectoriesById) => + GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection, exifDirectoriesById); } \ No newline at end of file diff --git a/Date-Group/DateGroup.cs b/Date-Group/DateGroup.cs index 9460b51..b345ce8 100644 --- a/Date-Group/DateGroup.cs +++ b/Date-Group/DateGroup.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Phares.Shared; using System.Globalization; diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index 634c03c..82233c1 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -356,7 +356,7 @@ public partial class E_Distance : IDistance locationContainer = LocationContainer.Get(keyValue.Value, encoding, keepExifDirectory: false); keyValuePairs.Add(keyValue.Key, locationContainer); } - results.Add(keyValuePair.Key, new(keyValuePairs)); + results.Add(keyValuePair.Key, keyValuePairs.AsReadOnly()); } return results.AsReadOnly(); } @@ -394,7 +394,7 @@ public partial class E_Distance : IDistance continue; if (!string.IsNullOrEmpty(focusModel)) { - model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(locationContainer.ExifDirectory); + model = IMetaBase.GetModel(locationContainer.ExifDirectory); if (string.IsNullOrEmpty(model) || !model.Contains(focusModel)) continue; } @@ -571,14 +571,14 @@ public partial class E_Distance : IDistance return results.AsReadOnly(); } - public static ReadOnlyCollection GetMatrixLocationContainers(IDlibDotNet dlibDotNet, Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary> mappedWithEncoding, List preFiltered, DistanceLimits distanceLimits, List postFiltered) + public static ReadOnlyCollection GetMatrixLocationContainers(Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary> mappedWithEncoding, List preFiltered, DistanceLimits distanceLimits, List postFiltered, Action? tick) { List results = []; ReadOnlyCollection locationContainers; ReadOnlyCollection readOnlyLocationContainers = GetReadOnlyLocationContainer(mappedWithEncoding, postFiltered); foreach (LocationContainer locationContainer in postFiltered) { - dlibDotNet.Tick(); + tick?.Invoke(); locationContainers = FaceRecognition.GetLocationContainers(mapConfiguration.FaceDistancePermyriad, readOnlyLocationContainers, locationContainer); foreach (LocationContainer item in locationContainers) { @@ -634,9 +634,9 @@ public partial class E_Distance : IDistance FileHolder fileHolder; int distancePermyriad; List files = []; + List mappedRelations; long ticks = DateTime.Now.Ticks; FaceDistance? faceDistanceEncoding; - List mappedRelations; List faceDistanceLengths; List faceDistanceEncodings = []; foreach (Record record in records) @@ -670,7 +670,7 @@ public partial class E_Distance : IDistance } } mappedRelations = (from l in mappedRelations orderby l.DistancePermyriad select l).Take(locationContainerDistanceTake).ToList(); - results.Add(new(fileHolder, new(mappedRelations))); + results.Add(new(fileHolder, mappedRelations.AsReadOnly())); } return results.AsReadOnly(); } diff --git a/Drag-Drop-Search/DragDropSearch.cs b/Drag-Drop-Search/DragDropSearch.cs index abf2426..fde6f11 100644 --- a/Drag-Drop-Search/DragDropSearch.cs +++ b/Drag-Drop-Search/DragDropSearch.cs @@ -3,7 +3,6 @@ using Phares.Shared; using System.Diagnostics; using System.Reflection; using View_by_Distance.Drag_Drop.Models; -using View_by_Distance.Property.Models; using View_by_Distance.Resize.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; @@ -20,7 +19,7 @@ public partial class DragDropSearch : Form private readonly IsEnvironment _IsEnvironment; private readonly Dictionary _IdToItem; private readonly string _ResizeFileNameExtension; - private readonly Models.Configuration _Configuration; + private readonly Configuration _Configuration; private readonly IConfigurationRoot _ConfigurationRoot; private readonly Property.Models.Configuration _PropertyConfiguration; @@ -30,8 +29,8 @@ public partial class DragDropSearch : Form _IdToItem = []; AppSettings appSettings; string workingDirectory; + Configuration configuration; IsEnvironment isEnvironment; - Models.Configuration configuration; IConfigurationRoot configurationRoot; Property.Models.Configuration propertyConfiguration; Assembly assembly = Assembly.GetExecutingAssembly(); @@ -93,12 +92,11 @@ public partial class DragDropSearch : Form private void LoadData() { Container.Models.Container[] containers; - string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}"); - (_, containers) = View_by_Distance.Container.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory); + (_, containers) = View_by_Distance.Container.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration); List collection = Program.GetItemCollection(_Configuration, containers); foreach (Item item in collection) { - if (item.ExifDirectory?.FilePath.Id is null) + if (item.ExifDirectory?.FilePath?.Id is null) continue; if (_IdToItem.ContainsKey(item.ExifDirectory.FilePath.Id.Value)) continue; diff --git a/Duplicate-Search/DuplicateSearch.cs b/Duplicate-Search/DuplicateSearch.cs index a897783..c0baa5e 100644 --- a/Duplicate-Search/DuplicateSearch.cs +++ b/Duplicate-Search/DuplicateSearch.cs @@ -9,6 +9,7 @@ using View_by_Distance.Duplicate.Search.Models; using View_by_Distance.Property.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; +using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Duplicate.Search; @@ -71,8 +72,7 @@ public class DuplicateSearch using (ProgressBar progressBar = new(1, message, options)) { progressBar.Tick(); - string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}"); - (f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory); + (f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration); } return containers; } @@ -101,12 +101,15 @@ public class DuplicateSearch private static Dictionary> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, Container.Models.Container[] containers, string destinationRoot, List preloadIds) { Dictionary> results = []; + string? model; string directory; const int zero = 0; + DateTime? dateTime; FileHolder resizedFileHolder; DateTime[] containerDateTimes; MappingFromItem? mappingFromItem; List? collection; + ReadOnlyCollection keywords; ReadOnlyCollection validImageItems; const string duplicates = "-Duplicate(s)"; if (containers.Length != 0) @@ -126,7 +129,7 @@ public class DuplicateSearch containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(validImageItems); foreach (Item item in validImageItems) { - if (item.ExifDirectory?.FilePath.Id is null) + if (item.ExifDirectory?.FilePath?.Id is null) { if (int.TryParse(item.FilePath.NameWithoutExtension, out int id)) continue; @@ -145,12 +148,15 @@ public class DuplicateSearch mappingFromItem = collection[zero]; if (mappingFromItem is not null) { + model = IMetaBase.GetModel(item.ExifDirectory); + dateTime = IDate.GetDateTimeOriginal(item.ExifDirectory); + keywords = IMetaBase.GetKeywords(item.ExifDirectory?.ExifBaseDirectories); resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(mappingFromItem.ResizedFileHolder.FullName.Replace($"0{duplicates}", $"1{duplicates}")); - collection[0] = new(mappingFromItem.ContainerDateTimes, item.Property.DateTimeDigitized, item.Property.DateTimeOriginal, mappingFromItem.Id, mappingFromItem.IsArchive, mappingFromItem.FilePath, mappingFromItem.IsWrongYear, item.Property.Keywords ?? [], mappingFromItem.MinimumDateTime, item.Property.Model, mappingFromItem.RelativePath, resizedFileHolder); + collection[0] = new(mappingFromItem.ContainerDateTimes, dateTime, mappingFromItem.Id, mappingFromItem.IsArchive, mappingFromItem.FilePath, mappingFromItem.IsWrongYear, keywords, mappingFromItem.MinimumDateTime, model, mappingFromItem.RelativePath, resizedFileHolder); } } resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(string.Concat(Path.Combine(destinationRoot, directory), item.RelativePath)); - mappingFromItem = Shared.Models.Stateless.Methods.IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); + mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); collection.Add(mappingFromItem); } } diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index 94fb512..232e5cf 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -116,8 +116,8 @@ public class D_Face : IFaceD } else { - maker = IMetadata.GetMaker(exifDirectory); - model = IMetadata.GetModel(exifDirectory); + maker = IMetaBase.GetMaker(exifDirectory); + model = IMetaBase.GetModel(exifDirectory); ExifDirectory? faceExifDirectory = IMetadata.GetExifDirectory(filePath); json = IMetadata.GetOutputResolution(faceExifDirectory); if (json is not null && json.Contains(nameof(DateTime))) @@ -264,7 +264,7 @@ public class D_Face : IFaceD } } - public List GetFaces(string outputResolution, string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List? mappingFromPhotoPrismCollection) + public List GetFaces(string cResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, List? mappingFromPhotoPrismCollection) { List? results; string? json; @@ -314,7 +314,7 @@ public class D_Face : IFaceD locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum); if (results is null || locations.Count > 0) { - results = GetFaces(outputResolution, cResultsFullGroupDirectory, exifDirectory, mappingFromItem, outputResolutionToResize, locations); + results = GetFaces(cResultsFullGroupDirectory, exifDirectory, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, locations); if (results.Count == 0) File.Move(mappingFromItem.ResizedFileHolder.FullName, $"{mappingFromItem.ResizedFileHolder.FullName}.err"); else @@ -338,7 +338,7 @@ public class D_Face : IFaceD return results; } - private List GetFaces(string outputResolution, string cResultsFullGroupDirectory, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, List locations) + private List GetFaces(string cResultsFullGroupDirectory, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, List locations) { if (_PropertyConfiguration.NumberOfJitters is null) throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters)); @@ -369,7 +369,6 @@ public class D_Face : IFaceD { unknownImage = null; } if (unknownImage is not null) { - (int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize); List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary? FaceParts)> collection; FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, _Model, _ModelParameter, _PredictorModel); collection = faceRecognition.GetCollection(unknownImage, locations, includeFaceEncoding: true, includeFaceParts: true); @@ -454,8 +453,8 @@ public class D_Face : IFaceD string faceFileJson; string faceEncodingJson; PropertyItem? propertyItem; - string? maker = IMetadata.GetMaker(exifDirectory); - string? model = IMetadata.GetModel(exifDirectory); + string? maker = IMetaBase.GetMaker(exifDirectory); + string? model = IMetaBase.GetModel(exifDirectory); #pragma warning disable CA1416 using Bitmap source = new(resizedFileHolder.FullName); MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); diff --git a/FaceParts/Models/_D2_FaceParts.cs b/FaceParts/Models/_D2_FaceParts.cs index 482d5e4..755f29f 100644 --- a/FaceParts/Models/_D2_FaceParts.cs +++ b/FaceParts/Models/_D2_FaceParts.cs @@ -92,8 +92,8 @@ public class D2_FaceParts List faceFiles = []; StringBuilder stringBuilder = new(); MappingFromPerson? mappingFromPerson; - string? maker = IMetadata.GetMaker(exifDirectory); - string? model = IMetadata.GetModel(exifDirectory); + string? maker = Shared.Models.Stateless.Methods.IMetaBase.GetMaker(exifDirectory); + string? model = Shared.Models.Stateless.Methods.IMetaBase.GetModel(exifDirectory); MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); #pragma warning disable CA1416 using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); @@ -468,8 +468,8 @@ public class D2_FaceParts int pointSize; FaceFile faceFile; MappingFromPerson? mappingFromPerson; - string? maker = IMetadata.GetMaker(exifDirectory); - string? model = IMetadata.GetModel(exifDirectory); + string? maker = Shared.Models.Stateless.Methods.IMetaBase.GetMaker(exifDirectory); + string? model = Shared.Models.Stateless.Methods.IMetaBase.GetModel(exifDirectory); MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); foreach ((Shared.Models.Face face, string fileName, string _) in collection) { diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 26d9e84..bfc4fa5 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -29,9 +29,10 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string FilesCollectionRootDirectory, bool FilesCollectionCountIsOne, ReadOnlyCollection> FilePathsCollection, - ReadOnlyDictionary>? IdToFilePaths, ReadOnlyDictionary? SplatNineIdentifiers); + public long Ticks { get; init; } + private readonly D_Face _Faces; private ProgressBar? _ProgressBar; private readonly C_Resize _Resize; @@ -67,11 +68,12 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable _Exceptions = []; _Console = console; _AppSettings = appSettings; + IDlibDotNet dlibDotNet = this; _IsEnvironment = isEnvironment; - long ticks = DateTime.Now.Ticks; + Ticks = DateTime.Now.Ticks; _JLinkResolvedDirectories = []; - if (ticks.ToString().Last() == '0') - ticks += 1; + 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); @@ -126,10 +128,10 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable personContainers = new([]); else { - int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - dlibDotNet.Ticks).TotalSeconds); message = $") Building People Collection - {totalSeconds} total second(s)"; - using ProgressBar progressBar = new(1, message, _ProgressBarOptions); - progressBar.Tick(); + dlibDotNet.ConstructProgressBar(1, message); + dlibDotNet.Tick(); string peopleRootDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(A2_People)); string? rootResultsDirectory = Path.GetDirectoryName(Path.GetDirectoryName(peopleRootDirectory)) ?? throw new Exception(); _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(peopleRootDirectory, propertyConfiguration.ResultSingleton)); @@ -158,12 +160,12 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable filenameExtension); } if (!configuration.SkipSearch) - Search(ticks, personContainers, argZero, propertyRoot); + Search(personContainers, argZero, propertyRoot); if (!_PropertyRootExistedBefore && !_IsEnvironment.Development && _Exceptions.Count == 0 && _ArgZeroIsConfigurationRootDirectory) { string d2FacePartsRootDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(D2_FaceParts)); _Logger?.LogInformation(string.Concat("Cleaning <", d2FacePartsRootDirectory, ">")); - Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(d2FacePartsRootDirectory, ticks); + Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(d2FacePartsRootDirectory, dlibDotNet.Ticks); } message = $"There were {_Exceptions.Count} exception(s) thrown! {Environment.NewLine}{string.Join(Environment.NewLine, _Exceptions)}"; _Logger?.LogInformation(message); @@ -196,7 +198,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string[] changesFrom = [nameof(A_Property)]; List> subFileTuples = []; FileHolder resizedFileHolder = _Resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber); - if (item.ExifDirectory is null || item.ExifDirectory.FilePath.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) + if (item.ExifDirectory is null || item.ExifDirectory.FilePath?.Id is null) + throw new Exception(); + if (!item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) throw new Exception(); if (_Configuration.PropertyConfiguration.ForcePropertyLastWriteTimeToCreationTime && item.SourceDirectoryFileHolder.LastWriteTime.Value != item.SourceDirectoryFileHolder.CreationTime.Value) { @@ -220,7 +224,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } else { - ReadOnlyCollection keywords = IMetaBase.GetKeywords(exifDirectory.ExifBaseDirectories); + ReadOnlyCollection keywords = IMetaBase.GetKeywords(exifDirectory); shouldIgnore = _Configuration.PropertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains); if (shouldIgnore.Value) { @@ -261,47 +265,54 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable distance += 1; } } - Dictionary outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem); - if (_Configuration.SaveResizedSubfiles) - { - if (shouldIgnore is not null && item.FilePath.HasIgnoreKeyword is not null && item.FilePath.HasIgnoreKeyword.Value != shouldIgnore.Value) - faces = []; - else - _Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize); - } - if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) - faces = []; - else if (!mappingFromItem.ResizedFileHolder.Exists && !File.Exists(mappingFromItem.ResizedFileHolder.FullName)) + int? orientation = IMetaBase.GetOrientation(exifDirectory); + if (exifDirectory is null || orientation is null || exifDirectory.Width is null || exifDirectory.Height is null) faces = []; else { - List? mappingFromPhotoPrismCollection; - if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection)) - mappingFromPhotoPrismCollection = null; - bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution); - faces = _Faces.GetFaces(outputResolution, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection); - result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces); - List<(Shared.Models.Face, FileHolder?, string, bool Saved)> faceCollection = _Faces.SaveFaces(item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces); - if (move && faceCollection.All(l => !l.Saved)) + Dictionary outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem); + (int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize); + if (_Configuration.SaveResizedSubfiles) { - ReadOnlyCollection locationContainers = mapLogic.GetLocationContainers(item); - if (locationContainers.Count > 0) + if (shouldIgnore is not null && item.FilePath.HasIgnoreKeyword is not null && item.FilePath.HasIgnoreKeyword.Value != shouldIgnore.Value) + faces = []; + else + _Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize); + } + if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) + faces = []; + else if (!mappingFromItem.ResizedFileHolder.Exists && !File.Exists(mappingFromItem.ResizedFileHolder.FullName)) + faces = []; + else + { + List? mappingFromPhotoPrismCollection; + if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection)) + mappingFromPhotoPrismCollection = null; + bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution); + faces = _Faces.GetFaces(cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, mappingFromPhotoPrismCollection); + result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces); + List<(Shared.Models.Face, FileHolder?, string, bool Saved)> faceCollection = _Faces.SaveFaces(item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces); + if (move && faceCollection.All(l => !l.Saved)) { - Map.Models.Stateless.Methods.IMapLogic.SetCreationTime(mappingFromItem, locationContainers); - if (_Configuration.LocationContainerDistanceTolerance is null && _Configuration.MoveToDecade) - Map.Models.Stateless.Methods.IMapLogic.MoveToDecade(_Configuration.PropertyConfiguration, mappingFromItem, locationContainers); - _Distance.LookForMatchFacesAndPossiblyRename(_Configuration.OverrideForFaceImages, _DistanceLimits, _Faces, item.FilePath, mappingFromItem, exifDirectory, faces, locationContainers); + ReadOnlyCollection locationContainers = mapLogic.GetLocationContainers(item); + if (locationContainers.Count > 0) + { + Map.Models.Stateless.Methods.IMapLogic.SetCreationTime(mappingFromItem, locationContainers); + if (_Configuration.LocationContainerDistanceTolerance is null && _Configuration.MoveToDecade) + Map.Models.Stateless.Methods.IMapLogic.MoveToDecade(_Configuration.PropertyConfiguration, mappingFromItem, locationContainers); + _Distance.LookForMatchFacesAndPossiblyRename(_Configuration.OverrideForFaceImages, _DistanceLimits, _Faces, item.FilePath, mappingFromItem, exifDirectory, faces, locationContainers); + } } + (bool review, int[] eyesCollection) = Shared.Models.Stateless.Methods.IFace.GetEyeCollection(_Configuration.EyeThreshold, faces); + if (review || _Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution)) + { + bool saveRotated = false; + string sourceDirectorySegment = Property.Models.Stateless.IResult.GetRelativePath(_Configuration.PropertyConfiguration, container.SourceDirectory); + _FaceParts.SaveFaceLandmarkImages(_Configuration.PropertyConfiguration, d2ResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces, saveRotated); + } + if (_Configuration.SaveFaceLandmarkForOutputResolutionsV2.Contains(outputResolution)) + _FaceParts.SaveFaceLandmarkImages(d2ResultsFullGroupDirectory, mappingFromItem, exifDirectory, faces); } - (bool review, int[] eyesCollection) = Shared.Models.Stateless.Methods.IFace.GetEyeCollection(_Configuration.EyeThreshold, faces); - if (review || _Configuration.SaveFaceLandmarkForOutputResolutions.Contains(outputResolution)) - { - bool saveRotated = false; - string sourceDirectorySegment = Property.Models.Stateless.IResult.GetRelativePath(_Configuration.PropertyConfiguration, container.SourceDirectory); - _FaceParts.SaveFaceLandmarkImages(_Configuration.PropertyConfiguration, d2ResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces, saveRotated); - } - if (_Configuration.SaveFaceLandmarkForOutputResolutionsV2.Contains(outputResolution)) - _FaceParts.SaveFaceLandmarkImages(d2ResultsFullGroupDirectory, mappingFromItem, exifDirectory, faces); } lock (sourceDirectoryChanges) { @@ -327,14 +338,18 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { int result = 0; int exceptionsCount = 0; + IDlibDotNet dlibDotNet = this; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; DateTime[] containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); 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, _ProgressBarOptions); + dlibDotNet.ConstructProgressBar(filteredItems.Count, message); _ = Parallel.For(0, filteredItems.Count, parallelOptions, (i, state) => { + Item item = filteredItems[i]; + if (!item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) + return; try { result += FullParallelForWork(metadata, @@ -347,11 +362,11 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable fileNameToCollection, record, container, - filteredItems[i], + item, containerDateTimes, isFocusRelativePath); if (!anyPropertiesChangedForX && (i == 0 || sourceDirectoryChanges.Count > 0)) - progressBar.Tick(); + dlibDotNet.Tick(); } catch (Exception) { @@ -365,7 +380,6 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable private void FullDoWork(string argZero, string propertyRoot, - long ticks, string fPhotoPrismSingletonDirectory, int count, B_Metadata metadata, @@ -413,7 +427,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable continue; sourceDirectoryChanges.Clear(); anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName); - totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - dlibDotNet.Ticks).TotalSeconds); message = $"{totalSeconds} total second(s) - {outputResolution} - {i + 1:000} / {readOnlyContainers.Count:000} - {total} / {count} total - <{container.SourceDirectory}> [{filteredItems.Count:000}] - total not mapped {totalNotMapped:000000}"; if (outputResolutionHasNumber) _Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory); @@ -448,10 +462,10 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } total += container.Items.Count; } - totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); + totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - dlibDotNet.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(); + dlibDotNet.ConstructProgressBar(1, message); + dlibDotNet.Tick(); } } @@ -607,14 +621,36 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { ExifDirectory? result; if (filePair.Match is null) - result = null; + { + try + { result = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); } + catch (Exception) { result = null; } + } else { string json = File.ReadAllText(filePair.Match.FullName); if (string.IsNullOrEmpty(json)) result = null; else + { result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + if (result?.FilePath?.Id is null) + { + try + { + result = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); + json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(filePair.Match.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + } + catch (Exception) { result = null; } + } + } + } + if (result is not null && result.FilePath?.Id is null) + { + try + { result = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); } + catch (Exception) { result = null; } } return result; } @@ -715,7 +751,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable List<(string, string, string)> collection = []; foreach (Item item in distinctValidImageItems) { - if (item.ExifDirectory?.FilePath.Id is null) + if (item.ExifDirectory?.FilePath?.Id is null) continue; if (item.IsNotUniqueAndNeedsReview is null || !item.IsNotUniqueAndNeedsReview.Value) continue; @@ -728,7 +764,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } foreach (Item item in distinctValidImageItems) { - if (item.ExifDirectory?.FilePath.Id is null) + if (item.ExifDirectory?.FilePath?.Id is null) continue; dateTime = IDate.GetDateTimeOriginal(item.ExifDirectory); if (dateTime is null) @@ -809,7 +845,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return result; } - private static ReadOnlyCollection HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(Property.Models.Configuration propertyConfiguration, ReadOnlyDictionary> keyValuePairs) + private static ReadOnlyCollection HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(Property.Models.Configuration propertyConfiguration, ReadOnlyDictionary> idToFilePaths) { List results = []; string checkFile; @@ -817,7 +853,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable FilePath? filePath; string checkDirectory; List collection = []; - foreach (KeyValuePair> keyValuePair in keyValuePairs) + foreach (KeyValuePair> keyValuePair in idToFilePaths) { collection.Clear(); for (int i = 0; i < keyValuePair.Value.Count; i++) @@ -851,7 +887,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return results.AsReadOnly(); } - private static ReadOnlyDictionary GetSplatNineIdentifiers(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> keyValuePairs) + private static ReadOnlyDictionary GetSplatNineIdentifiers(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> idToFilePaths) { Dictionary results = []; string json; @@ -862,7 +898,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string bMetadataCollectionDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultCollection); if (!Directory.Exists(bMetadataCollectionDirectory)) _ = Directory.CreateDirectory(bMetadataCollectionDirectory); - foreach (KeyValuePair> keyValuePair in keyValuePairs) + foreach (KeyValuePair> keyValuePair in idToFilePaths) { filePath = null; for (int i = 0; i < keyValuePair.Value.Count; i++) @@ -894,17 +930,18 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return results.AsReadOnly(); } - private static ReadOnlyDictionary GetSplatNineIdentifiersAndHideSplatNine(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary> keyValuePairs) + private static ReadOnlyDictionary GetSplatNineIdentifiersAndHideSplatNine(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection> filePathsCollection) { ReadOnlyDictionary results; - if (keyValuePairs.Count == 0) + if (filePathsCollection.Count == 0) results = new(new Dictionary()); else { - ReadOnlyCollection moved = HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(propertyConfiguration, keyValuePairs); + ReadOnlyDictionary> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection); + ReadOnlyCollection moved = HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(propertyConfiguration, idToFilePaths); if (moved.Count > 0) throw new Exception($"House cleaning needed!{Environment.NewLine}{string.Join(Environment.NewLine, moved.Select(l => l.Id))}"); - results = GetSplatNineIdentifiers(propertyConfiguration, bResultsFullGroupDirectory, keyValuePairs); + results = GetSplatNineIdentifiers(propertyConfiguration, bResultsFullGroupDirectory, idToFilePaths); } return results; } @@ -983,13 +1020,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return result; } - private ReadOnlyCollection GetFilePath(long ticks, string dFacesContentDirectory) + private ReadOnlyCollection GetFilePath(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; + 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); @@ -1003,19 +1040,22 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return results.AsReadOnly(); } - private void ParallelFor(IDlibDotNet dlibDotNet, FilePair filePair, Dictionary results) + private void ParallelFor(FilePair filePair, Dictionary results, Action? tick) { - dlibDotNet?.Tick(); + tick?.Invoke(); 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); + { + if (!results.ContainsKey(filePair.FilePath.Id.Value)) + results.Add(filePair.FilePath.Id.Value, exifDirectory); + } } - private bool GetRunToDoCollectionFirst(Models.Configuration configuration, long ticks, string[] checkDirectories) + private bool GetRunToDoCollectionFirst(Models.Configuration configuration, string[] checkDirectories) { bool result = configuration.SaveSortingWithoutPerson; if (!result) @@ -1030,7 +1070,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { string seasonDirectory; DirectoryInfo directoryInfo; - DateTime dateTime = new(ticks); + DateTime dateTime = new(Ticks); string rootDirectory = _Configuration.PropertyConfiguration.RootDirectory; string eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent); (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); @@ -1067,15 +1107,14 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return result; } - private void Search(long ticks, ReadOnlyCollection personContainers, string argZero, string propertyRoot) + private void Search(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); + DateTime dateTime = new(Ticks); string eDistanceContentDirectory; string? a2PeopleContentDirectory; string aResultsFullGroupDirectory; @@ -1087,25 +1126,25 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable const string directorySearchFilter = "*"; bool configurationOutputResolutionsHas = false; ReadOnlyDictionary> personKeyToIds; - (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); string[] checkDirectories = [ Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Ancestry"), Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Facebook"), Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "LinkedIn") ]; - bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks, checkDirectories); + bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, checkDirectories); (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); - Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(_Configuration.PropertyConfiguration.RootDirectory, ticks); + (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); + Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(_Configuration.PropertyConfiguration.RootDirectory, dlibDotNet.Ticks); a2PeopleContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), "([])"); 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); + _ = Directory.CreateDirectory(Path.Combine(eDistanceContentDirectory, dlibDotNet.Ticks.ToString())); + B_Metadata metadata = new(dlibDotNet, _Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, dlibDotNet.Ticks, bResultsFullGroupDirectory); if (runToDoCollectionFirst) mapLogic = null; else - mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); + mapLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, dlibDotNet.Ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); foreach (string outputResolution in _Configuration.OutputResolutions) { if (outputResolution.Any(char.IsNumber)) @@ -1113,7 +1152,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable configurationOutputResolutionsHas = true; if (!runToDoCollectionFirst) break; - record = GetFilesCollectionThenCopyOrMove(dlibDotNet, ticks, fileSearchFilter, directorySearchFilter, bResultsFullGroupDirectory, outputResolution); + record = GetFilesCollectionThenCopyOrMove(dlibDotNet, metadata, fileSearchFilter, directorySearchFilter, bResultsFullGroupDirectory, outputResolution); break; } fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent); @@ -1145,30 +1184,21 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { if (outputResolution.Any(char.IsNumber)) continue; - Dictionary exifDirectoriesById = []; (cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); string? filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); ReadOnlyCollection>? filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: true); + ReadOnlyDictionary exifDirectoriesById = record?.ExifDirectoriesById ?? new(new Dictionary()); record = new(FilesCollectionRootDirectory: filesCollectionRootDirectory, FilesCollectionCountIsOne: false, FilePathsCollection: filePathsCollection, - ExifDirectoriesById: new(exifDirectoriesById), - IdToFilePaths: null, + ExifDirectoriesById: exifDirectoriesById, SplatNineIdentifiers: null); break; } } 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, _ProgressBarOptions); ReadOnlyCollection readOnlyContainers = Container.Models.Stateless.Methods.IContainer.GetContainers(dlibDotNet, _Configuration.PropertyConfiguration, @@ -1176,24 +1206,21 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable _Faces.HiddenFileNameExtension, eDistanceContentDirectory, record.FilesCollectionRootDirectory, - record.IdToFilePaths, record.SplatNineIdentifiers, record.FilePathsCollection, record.ExifDirectoriesById); - _ProgressBar.Dispose(); - mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); + Verify(argZero, readOnlyContainers); + foreach (string checkDirectory in checkDirectories) + { + seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}"); + if (!Directory.Exists(seasonDirectory)) + _ = Directory.CreateDirectory(seasonDirectory); + } + mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, dlibDotNet.Ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); DeleteContinueFiles(personContainers); if (!runToDoCollectionFirst) - MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory); - FullDoWork(argZero, - propertyRoot, - ticks, - fPhotoPrismSingletonDirectory, - count, - metadata, - record, - readOnlyContainers, - mapLogic); + MapFaceFileLogic(personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory); + FullDoWork(argZero, propertyRoot, fPhotoPrismSingletonDirectory, count, metadata, record, readOnlyContainers, mapLogic); ReadOnlyCollection distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); if (_Configuration.LookForAbandoned) { @@ -1201,10 +1228,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory; foreach (string outputResolution in _Configuration.OutputResolutions) { - _ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), _ProgressBarOptions); + dlibDotNet.ConstructProgressBar(5, nameof(mapLogic.LookForAbandoned)); (cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - mapLogic.LookForAbandoned(dlibDotNet, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); - _ProgressBar.Dispose(); + mapLogic.LookForAbandoned(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, dlibDotNet.Tick); } } _Distance.Clear(); @@ -1215,7 +1241,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable if (!Directory.Exists(eDistanceContentDirectory)) _ = Directory.CreateDirectory(eDistanceContentDirectory); string json = JsonSerializer.Serialize(distinctValidImageMappingCollection); - File.WriteAllText(Path.Combine(eDistanceContentDirectory, $"{ticks}.json"), json); + File.WriteAllText(Path.Combine(eDistanceContentDirectory, $"{Ticks}.json"), json); } foreach (string outputResolution in _Configuration.OutputResolutions) { @@ -1233,7 +1259,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable && outputResolution == _Configuration.OutputResolutions[0] && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution) && _Exceptions.Count == 0) - MapLogic(ticks, readOnlyContainers, fPhotoPrismContentDirectory, mapLogic, outputResolution, new(personKeyToIds), distinctValidImageFaces, distinctValidImageMappingCollection); + MapLogic(readOnlyContainers, fPhotoPrismContentDirectory, mapLogic, outputResolution, personKeyToIds.AsReadOnly(), distinctValidImageFaces, distinctValidImageMappingCollection); if (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && record?.SplatNineIdentifiers is not null && distinctValidImageMappingCollection.Count > 0) _Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.ImmichOwnerId, _Configuration.ImmichRoot, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, record.SplatNineIdentifiers, distinctValidImageMappingCollection); if (_IsEnvironment.Development) @@ -1253,59 +1279,96 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } } - private Record GetFilesCollectionThenCopyOrMove(IDlibDotNet dlibDotNet, long ticks, string fileSearchFilter, string directorySearchFilter, string bResultsFullGroupDirectory, string outputResolution) + private void Verify(string argZero, ReadOnlyCollection readOnlyContainers) + { + int count = 0; + List items = []; + Container.Models.Container container; + ReadOnlyCollection filteredItems; + foreach (string outputResolution in _Configuration.OutputResolutions) + { + 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; + foreach (Item item in filteredItems) + { + count++; + if (item.ExifDirectory is null || item.ExifDirectory.FilePath?.Id is null) + { + items.Add(item); + continue; + } + if (!item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) + { + items.Add(item); + continue; + } + } + } + } + if (items.Count > 0) + throw new Exception($"{items.Count} item(s) of {count} item(s) are not setup!"); + } + + private Record GetFilesCollectionThenCopyOrMove(IDlibDotNet dlibDotNet, B_Metadata metadata, string fileSearchFilter, string directorySearchFilter, string bResultsFullGroupDirectory, string outputResolution) { Record result; int count; string message; - ProgressBar progressBar; const string extension = ".json"; ReadOnlyCollection filePairs; + Dictionary results = []; int maxDegreeOfParallelism = Environment.ProcessorCount; - Dictionary exifDirectoriesById = []; string filesCollectionRootDirectory = _Configuration.PropertyConfiguration.RootDirectory; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; (string cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); string jsonGroupDirectory = Path.Combine(bResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton); if (!Directory.Exists(jsonGroupDirectory)) _ = Directory.CreateDirectory(jsonGroupDirectory); - IReadOnlyDictionary>> keyValuePairs = + ReadOnlyDictionary>> 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); + foreach (KeyValuePair keyValuePair in metadata.GetKeyValuePairsAndClear()) + results.Add(keyValuePair.Key, keyValuePair.Value); 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(); + message = $") {nameof(DlibDotNet)} - Preloading ExifDirectory Dictionary - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - dlibDotNet.Ticks).TotalSeconds)} total second(s)"; + dlibDotNet.ConstructProgressBar(count, message); + _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(filePairs[i], results, dlibDotNet.Tick)); + if (results.Count == 0) + throw new Exception("No exif directories were found!"); count = filePathsCollection.Select(l => l.Count).Sum(); + ReadOnlyDictionary exifDirectoriesById = results.AsReadOnly(); 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(); + message = $") Selecting for ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - dlibDotNet.Ticks).TotalSeconds)} total second(s)"; + dlibDotNet.ConstructProgressBar(count, message); + (string[] distinctDirectories, List<(FilePath, string)> toDoCollection) = IDirectory.GetToDoCollection(_Configuration.PropertyConfiguration, filePathsCollection, fileGroups, exifDirectoriesById, dlibDotNet.Tick); foreach (string distinctDirectory in distinctDirectories) { if (!Directory.Exists(distinctDirectory)) _ = 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, _ProgressBarOptions); - _ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, () => progressBar.Tick()); - progressBar.Dispose(); - ReadOnlyDictionary> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection); - ReadOnlyDictionary splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, idToFilePaths); - result = new(ExifDirectoriesById: new(exifDirectoriesById), + message = $") Copying to ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - dlibDotNet.Ticks).TotalSeconds)} total second(s)"; + dlibDotNet.ConstructProgressBar(count, message); + _ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, dlibDotNet.Tick); + ReadOnlyDictionary splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, filePathsCollection); + result = new(ExifDirectoriesById: exifDirectoriesById, FilesCollectionRootDirectory: filesCollectionRootDirectory, FilesCollectionCountIsOne: filesCollectionCountIsOne, FilePathsCollection: filePathsCollection, - IdToFilePaths: idToFilePaths, SplatNineIdentifiers: splatNineIdentifiers); return result; } - private void MapFaceFileLogic(long ticks, ReadOnlyCollection personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory) + private void MapFaceFileLogic(ReadOnlyCollection personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory) { foreach (string outputResolution in _Configuration.OutputResolutions) { @@ -1323,7 +1386,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable continue; if (!_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)) continue; - List saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, mapLogic, outputResolution); + List saveContainers = GetSaveContainers(personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, mapLogic, outputResolution); if (saveContainers.Count > 0) { int updated = 0; @@ -1333,24 +1396,24 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } } - private List GetSaveContainers(long ticks, ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, MapLogic mapLogic, string outputResolution) + private List GetSaveContainers(ReadOnlyCollection personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, MapLogic mapLogic, string outputResolution) { List results; IDlibDotNet dlibDotNet = this; long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); (string cResultsFullGroupDirectory, string _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); string dFacesContentDirectory = Path.Combine(dResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); - ReadOnlyDictionary> mapped = Map.Models.Stateless.Methods.IMapLogic.GetMapped(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); + ReadOnlyDictionary> mapped = Map.Models.Stateless.Methods.IMapLogic.GetMapped(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, dlibDotNet.Ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); if (mapped.Count == 0 && !_Configuration.SaveSortingWithoutPerson) throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!"); - ReadOnlyCollection filePaths = GetFilePath(ticks, dFacesContentDirectory); - List available = Map.Models.Stateless.Methods.IMapLogic.GetAvailable(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Faces, ticks, filePaths); + ReadOnlyCollection filePaths = GetFilePath(dFacesContentDirectory); + List available = Map.Models.Stateless.Methods.IMapLogic.GetAvailable(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Faces, dlibDotNet.Ticks, filePaths); if (!string.IsNullOrEmpty(_Configuration.FocusDirectory) && _Configuration.FocusDirectory.Length != 2) throw new NotSupportedException($"{nameof(_Configuration.FocusDirectory)} currently only works with output directory! Example 00."); ReadOnlyDictionary> mappedWithEncoding = E_Distance.GetMappedWithEncoding(mapped); if (mappedWithEncoding.Count == 0 && !_Configuration.SaveSortingWithoutPerson) throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!"); - List preFiltered = E_Distance.GetPreFilterLocationContainer(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Configuration.FocusDirectory, _Configuration.FocusModel, _Configuration.SkipPersonWithMoreThen, ticks, mapLogic, jLinkResolvedPersonKeys, mapped, available); + List preFiltered = E_Distance.GetPreFilterLocationContainer(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Configuration.FocusDirectory, _Configuration.FocusModel, _Configuration.SkipPersonWithMoreThen, dlibDotNet.Ticks, mapLogic, jLinkResolvedPersonKeys, mapped, available); if (preFiltered.Count == 0) results = []; else @@ -1361,10 +1424,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable results = []; else { - string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - _ProgressBar = new(postFiltered.Count, message, _ProgressBarOptions); - ReadOnlyCollection matrix = E_Distance.GetMatrixLocationContainers(dlibDotNet, _MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered); - _ProgressBar.Dispose(); + string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - dlibDotNet.Ticks).TotalSeconds)} total second(s)"; + dlibDotNet.ConstructProgressBar(postFiltered.Count, message); + ReadOnlyCollection matrix = E_Distance.GetMatrixLocationContainers(_MapConfiguration, dlibDotNet.Ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered, dlibDotNet.Tick); ReadOnlyDictionary onlyOne = GetOnlyOne(distanceLimits, matrix); if (onlyOne.Count == 0) results = []; @@ -1403,7 +1465,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath); foreach (Item item in filteredItems) { - if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath?.Id is null || item.ResizedFileHolder is null) continue; mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, item.ResizedFileHolder); if (distinctItems) @@ -1458,7 +1520,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return (result, notMapped); } - private void MapLogic(long ticks, ReadOnlyCollection containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageFaces, ReadOnlyCollection distinctValidImageMappingCollection) + private void MapLogic(ReadOnlyCollection containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection distinctValidImageFaces, ReadOnlyCollection distinctValidImageMappingCollection) { IDlibDotNet dlibDotNet = this; (_, _, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); @@ -1467,24 +1529,25 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2FacePartsContentCollectionDirectory = Path.Combine(d2ResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContentCollection); if (distinctValidImageMappingCollection.Count > 0) { - Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(d2FacePartsContentDirectory, ticks); + Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(d2FacePartsContentDirectory, dlibDotNet.Ticks); if (Directory.Exists(d2FacePartsContentCollectionDirectory)) Shared.Models.Stateless.Methods.IPath.MakeHiddenIfAllItemsAreHidden(d2FacePartsContentCollectionDirectory); } if (Directory.Exists(fPhotoPrismContentDirectory)) - F_PhotoPrism.WriteMatches(fPhotoPrismContentDirectory, _Configuration.PersonBirthdayFormat, _Configuration.RectangleIntersectMinimums, ticks, distinctValidImageFaces, mapLogic); + F_PhotoPrism.WriteMatches(fPhotoPrismContentDirectory, _Configuration.PersonBirthdayFormat, _Configuration.RectangleIntersectMinimums, dlibDotNet.Ticks, distinctValidImageFaces, mapLogic); if (_Configuration.SaveShortcutsForOutputResolutions.Contains(outputResolution)) mapLogic.SaveShortcutsForOutputResolutionsDuringMapLogic(containers, personKeyToIds, dFacesContentDirectory, distinctValidImageMappingCollection); ReadOnlyDictionary> idToWholePercentagesToMapping = Map.Models.Stateless.Methods.IMapLogic.GetIdToWholePercentagesToFace(distinctValidImageMappingCollection); if (_Configuration.SaveMappedForOutputResolutions.Contains(outputResolution)) mapLogic.SaveMapped(dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, personKeyToIds, distinctValidImageMappingCollection, idToWholePercentagesToMapping); if (_Configuration.SaveFaceDistancesForOutputResolutions.Contains(outputResolution)) - SaveFaceDistances(ticks, mapLogic, distinctValidImageFaces, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping); + SaveFaceDistances(mapLogic, distinctValidImageFaces, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping); } - private void SaveFaceDistances(long ticks, MapLogic mapLogic, ReadOnlyCollection distinctValidImageFaces, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping) + private void SaveFaceDistances(MapLogic mapLogic, ReadOnlyCollection distinctValidImageFaces, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping) { - E_Distance.PreFilterSetFaceDistances(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, distinctValidImageFaces); + IDlibDotNet dlibDotNet = this; + E_Distance.PreFilterSetFaceDistances(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, dlibDotNet.Ticks, distinctValidImageFaces); ReadOnlyCollection faceDistanceContainers = E_Distance.GetFaceDistanceContainers(distinctValidImageFaces); if (faceDistanceContainers.Count > 0) { @@ -1495,24 +1558,25 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable continue; faceDistanceEncodings.Add(faceDistanceContainer.FaceDistance); } - SaveFaceDistances(ticks, mapLogic, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, new(faceDistanceEncodings), faceDistanceContainers); + SaveFaceDistances(mapLogic, dFacesContentDirectory, d2FacePartsContentDirectory, d2FacePartsContentCollectionDirectory, idToWholePercentagesToMapping, faceDistanceEncodings.AsReadOnly(), faceDistanceContainers); } } - private void SaveFaceDistances(long ticks, MapLogic mapLogic, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping, ReadOnlyCollection faceDistanceEncodings, ReadOnlyCollection faceDistanceContainers) + private void SaveFaceDistances(MapLogic mapLogic, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary> idToWholePercentagesToMapping, ReadOnlyCollection faceDistanceEncodings, ReadOnlyCollection faceDistanceContainers) { int? useFiltersCounter = null; + IDlibDotNet dlibDotNet = this; DistanceLimits distanceLimits; ReadOnlyCollection sortingContainers; FaceDistanceContainer[] filteredFaceDistanceContainers; - long? skipOlderThan = _Configuration.SkipOlderThanDays is null ? null : new DateTime(ticks).AddDays(-_Configuration.SkipOlderThanDays.Value).Ticks; + long? skipOlderThan = _Configuration.SkipOlderThanDays is null ? null : new DateTime(Ticks).AddDays(-_Configuration.SkipOlderThanDays.Value).Ticks; distanceLimits = new(_Configuration.FaceAreaPermyriad, _Configuration.FaceConfidencePercent, _Configuration.FaceDistancePermyriad, _Configuration.RangeDaysDeltaTolerance, _Configuration.RangeDistanceTolerance, _Configuration.RangeFaceAreaTolerance, _Configuration.RangeFaceConfidence, _Configuration.SortingMaximumPerFaceShouldBeHigh); filteredFaceDistanceContainers = E_Distance.FilteredPostLoadFaceDistanceContainers(mapLogic, faceDistanceContainers, skipOlderThan, distanceLimits); if (filteredFaceDistanceContainers.Length == 0) _Logger?.LogInformation("All images have been filtered!"); else { - sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortedSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers); + sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortedSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, dlibDotNet.Ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers); if (sortingContainers.Count == 0) { for (useFiltersCounter = 1; useFiltersCounter < _Configuration.UseFilterTries; useFiltersCounter++) @@ -1523,7 +1587,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable _Logger?.LogInformation("All images have been filtered!"); else { - sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortedSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers); + sortingContainers = E_Distance.SetFaceMappingSortingCollectionThenGetSortedSortingContainers(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, dlibDotNet.Ticks, mapLogic, distanceLimits, faceDistanceEncodings, filteredFaceDistanceContainers); if (sortingContainers.Count == 0) break; } @@ -1599,11 +1663,11 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable MappingFromFilterPre mappingFromFilterPre; MappingFromFilterPost mappingFromFilterPost; bool? isFocusModel = GetIsFocusModel(item.ExifDirectory); - ReadOnlyDictionary>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.ExifDirectory?.FilePath.Id); + ReadOnlyDictionary>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.ExifDirectory?.FilePath?.Id); long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); foreach (Shared.Models.Face face in faces) { - if (item.ExifDirectory?.FilePath.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) + if (item.ExifDirectory?.FilePath?.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) { canReMap = null; isFocusPerson = null; diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 08ecd54..7aa7d70 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -82,8 +82,8 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic List personKeyFormattedCollection = []; Dictionary personKeyFormattedToNewestPersonKeyFormatted = []; Stateless.MapLogic.SetPersonCollectionsAfterSetSkipCollections(configuration, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection); - readOnlyPersonKeyFormattedCollection = new(personKeyFormattedCollection); - readOnlyPersonKeyFormattedToNewestPersonKeyFormatted = new(personKeyFormattedToNewestPersonKeyFormatted); + readOnlyPersonKeyFormattedCollection = personKeyFormattedCollection.AsReadOnly(); + readOnlyPersonKeyFormattedToNewestPersonKeyFormatted = personKeyFormattedToNewestPersonKeyFormatted.AsReadOnly(); } List records = Stateless.DistanceLogic.DeleteEmptyDirectoriesAndGetCollection(propertyConfiguration, configuration, ticks, eDistanceContentDirectory, readOnlyPersonKeyFormattedToNewestPersonKeyFormatted, readOnlyPersonKeyFormattedCollection); ReadOnlyCollection<(Stateless.MapLogic.PersonKeyFormattedIdThenWholePercentages, PersonContainer)> readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer; @@ -103,10 +103,10 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic personKeyFormattedToPersonContainer, personKeyToPersonContainerCollection, possiblyNewPersonDisplayDirectoryNamesAndPersonContainer); - readOnlyPersonKeyToCount = new(personKeyToCount); - readOnlyPersonKeyFormattedToPersonContainer = new(personKeyFormattedToPersonContainer); - readOnlyPersonKeyToPersonContainerCollection = new(personKeyToPersonContainerCollection); - readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer = new(possiblyNewPersonDisplayDirectoryNamesAndPersonContainer); + readOnlyPersonKeyToCount = personKeyToCount.AsReadOnly(); + readOnlyPersonKeyFormattedToPersonContainer = personKeyFormattedToPersonContainer.AsReadOnly(); + readOnlyPersonKeyToPersonContainerCollection = personKeyToPersonContainerCollection.AsReadOnly(); + readOnlyPossiblyNewPersonDisplayDirectoryNamesAndPersonContainer = possiblyNewPersonDisplayDirectoryNamesAndPersonContainer.AsReadOnly(); Stateless.MapLogic.SetPersonKeyToPersonContainer(configuration, personContainers, readOnlyPersonKeyToCount, @@ -320,7 +320,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic public ReadOnlyCollection GetLocationContainers(Item item) { LocationContainer[] results; - if (item.ExifDirectory?.FilePath.Id is null) + if (item.ExifDirectory?.FilePath?.Id is null) results = []; else { @@ -921,16 +921,16 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return result; } - public void LookForAbandoned(IDlibDotNet dlibDotNet, Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) + public void LookForAbandoned(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, Action? tick) { string[] directories; string? directoryName; List distinctFilteredIds = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); LookForAbandoned(propertyConfiguration, distinctFilteredIds); - dlibDotNet.Tick(); + tick?.Invoke(); List distinctFilteredFileNameFirstSegments = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, bResultsFullGroupDirectory, distinctFilteredFileNameFirstSegments); - dlibDotNet.Tick(); + tick?.Invoke(); directories = Directory.GetDirectories(cResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { @@ -939,7 +939,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic continue; Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } - dlibDotNet.Tick(); + tick?.Invoke(); directories = Directory.GetDirectories(dResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { @@ -948,7 +948,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic continue; Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } - dlibDotNet.Tick(); + tick?.Invoke(); directories = Directory.GetDirectories(d2ResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { @@ -957,7 +957,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic continue; Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } - dlibDotNet.Tick(); + tick?.Invoke(); } private void LookForAbandoned(Property.Models.Configuration propertyConfiguration, List distinctFilteredIds) diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index 13b5880..af68af6 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -53,9 +53,9 @@ internal abstract class MapLogic continue; personContainers.Add(personContainer); } - keyValuePairs.Add(wholePercentagesTo.Key, new(personContainers)); + keyValuePairs.Add(wholePercentagesTo.Key, personContainers.AsReadOnly()); } - results.Add(idTo.Key, new(keyValuePairs)); + results.Add(idTo.Key, keyValuePairs.AsReadOnly()); } return results.AsReadOnly(); } @@ -110,7 +110,7 @@ internal abstract class MapLogic List results = []; foreach (Item item in items) { - if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) + if (item.ExifDirectory?.FilePath?.Id is null || item.ResizedFileHolder is null) continue; foreach (Face face in item.Faces) { @@ -384,7 +384,7 @@ internal abstract class MapLogic { Dictionary> results = []; foreach (KeyValuePair> keyValuePair in keyValuePairs) - results.Add(keyValuePair.Key, new(keyValuePair.Value)); + results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); return results.AsReadOnly(); } @@ -602,7 +602,7 @@ internal abstract class MapLogic if (check) continue; personBirthday = IPersonBirthday.GetPersonBirthday(personKey + (oneHour * 2)); - personContainer = PersonContainer.Get(approximateYears, [personBirthday], new(personDisplayDirectoryAllFilePaths), configuration.MappingDefaultName, personKey); + personContainer = PersonContainer.Get(approximateYears, [personBirthday], personDisplayDirectoryAllFilePaths.AsReadOnly(), configuration.MappingDefaultName, personKey); results.Add(personContainer); if (results.Count > 99) break; @@ -1095,7 +1095,7 @@ internal abstract class MapLogic } if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) { - ReadOnlyCollection locationContainers = new(results.OrderBy(l => l.DirectoryNumber).ToArray()); + ReadOnlyCollection locationContainers = results.OrderBy(l => l.DirectoryNumber).ToArray().AsReadOnly(); LookForPossibleDuplicates(configuration, locationContainers); } return results; diff --git a/Metadata/Models/B_Metadata.cs b/Metadata/Models/B_Metadata.cs index 16ff588..c46c380 100644 --- a/Metadata/Models/B_Metadata.cs +++ b/Metadata/Models/B_Metadata.cs @@ -14,10 +14,7 @@ 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; } + private readonly Dictionary _ExifDirectoriesById; // First // Set DateGroupDirectory @@ -50,52 +47,49 @@ public class B_Metadata : IMetadata else throw new Exception(); } - 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 = []; + int maxDegreeOfParallelism = Environment.ProcessorCount; + Action? tick = dlibDotNet is null ? null : dlibDotNet.Tick; + filesCollectionRootDirectory = propertyConfiguration.RootDirectory; 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)"; + ReadOnlyCollection filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupSingletonDirectory, filePathsCollection); + string message = $") {nameof(B_Metadata)} - 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) - { - 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]; + _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(filePairs[i], exifDirectoriesById, tick)); + _ExifDirectoriesById = exifDirectoriesById; } - private void ParallelFor(IDlibDotNet? dlibDotNet, FilePair filePair, List results) + public ReadOnlyDictionary GetKeyValuePairsAndClear() { - dlibDotNet?.Tick(); + Dictionary results = []; + foreach (KeyValuePair keyValuePair in _ExifDirectoriesById) + results.Add(keyValuePair.Key, keyValuePair.Value); + _ExifDirectoriesById.Clear(); + return results.AsReadOnly(); + } + + private void ParallelFor(FilePair filePair, Dictionary results, Action? tick) + { + tick?.Invoke(); if (filePair.FilePath.Id is null) return; ExifDirectory? exifDirectory = GetExifDirectory(filePair); if (exifDirectory is null) return; lock (results) - results.Add(exifDirectory); + { + if (!results.ContainsKey(filePair.FilePath.Id.Value)) + results.Add(filePair.FilePath.Id.Value, exifDirectory); + } } private static ExifDirectory? GetExifDirectory(FilePair filePair) @@ -105,11 +99,24 @@ public class B_Metadata : IMetadata result = null; else { - string json = File.ReadAllText(filePair.Match.FullName); + string json; + json = File.ReadAllText(filePair.Match.FullName); if (string.IsNullOrEmpty(json)) result = null; else + { result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + if (result?.FilePath?.Id is null) + { + try + { + result = Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); + json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(filePair.Match.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + } + catch (Exception) { result = null; } + } + } } return result; } @@ -203,7 +210,7 @@ public class B_Metadata : IMetadata DateTime? result = null; DateTime? dateTime; DateTime checkDateTime; - string dateTimeFormat = Stateless.Methods.IMetadata.DateTimeFormat(); + string dateTimeFormat = IMetaBase.DateTimeFormat(); MetadataExtractor.Formats.Exif.ExifDirectoryBase? exifDirectoryBase = directories.OfType().FirstOrDefault(); results.Add(new DateTime(filePath.CreationTicks)); results.Add(new DateTime(filePath.LastWriteTicks)); @@ -213,7 +220,7 @@ public class B_Metadata : IMetadata results.Add(checkDateTime); else { - dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime)); + dateTime = IMetaBase.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime)); if (dateTime is not null) results.Add(dateTime.Value); } @@ -221,7 +228,7 @@ public class B_Metadata : IMetadata results.Add(checkDateTime); else { - dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized)); + dateTime = IMetaBase.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized)); if (dateTime is not null) results.Add(dateTime.Value); } @@ -232,7 +239,7 @@ public class B_Metadata : IMetadata } else { - dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal)); + dateTime = IMetaBase.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal)); if (dateTime is not null) { result ??= dateTime.Value; @@ -250,7 +257,7 @@ public class B_Metadata : IMetadata } else { - dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); + dateTime = IMetaBase.GetDateTime(dateTimeFormat, aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); if (dateTime is not null) { result ??= dateTime.Value; @@ -268,7 +275,7 @@ public class B_Metadata : IMetadata } else { - dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); + dateTime = IMetaBase.GetDateTime(dateTimeFormat, quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); if (dateTime is not null) { result ??= dateTime.Value; @@ -286,7 +293,7 @@ public class B_Metadata : IMetadata } else { - dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); + dateTime = IMetaBase.GetDateTime(dateTimeFormat, quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); if (dateTime is not null) { result ??= dateTime.Value; diff --git a/Metadata/Models/Stateless/Base.cs b/Metadata/Models/Stateless/Base.cs deleted file mode 100644 index 2575ec5..0000000 --- a/Metadata/Models/Stateless/Base.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Globalization; -using View_by_Distance.Shared.Models; - -namespace View_by_Distance.Metadata.Models.Stateless.Methods; - -internal static class Base -{ - - 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; - } - -#pragma warning restore CA1416 - - internal static DateTime? GetDateTime(string dateTimeFormat, string? value) - { - DateTime? result; - string alternateFormat = "ddd MMM dd HH:mm:ss yyyy"; - if (value is not null && DateTime.TryParse(value, out DateTime dateTime)) - result = dateTime; - else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) - result = dateTime; - else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) - result = dateTime; - else - result = null; - return result; - } - -#pragma warning disable CA1416 - -} \ No newline at end of file diff --git a/Metadata/Models/Stateless/Dimensions.cs b/Metadata/Models/Stateless/Dimensions.cs index fd53715..e156efb 100644 --- a/Metadata/Models/Stateless/Dimensions.cs +++ b/Metadata/Models/Stateless/Dimensions.cs @@ -8,18 +8,18 @@ internal static class Dimensions #pragma warning disable IDE0230 private static readonly Dictionary> _ImageFormatDecoders = new() { + { new byte[] { 0xff, 0xd8 }, DecodeJfif }, { new byte[] { 0x42, 0x4D }, DecodeBitmap }, + { new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP }, { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, - { new byte[] { 0xff, 0xd8 }, DecodeJfif }, - { new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP }, }; #pragma warning restore IDE0230 - private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) + private static bool StartsWith(List thisBytes, byte[] thatBytes) { - for (int i = 0; i < thatBytes.Length; i += 1) + for (int i = 0; i < thisBytes.Count && i < thatBytes.Length; i += 1) { if (thisBytes[i] == thatBytes[i]) continue; @@ -103,24 +103,41 @@ internal static class Dimensions internal static Size? GetDimensions(BinaryReader binaryReader) { - int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; - byte[] magicBytes = new byte[maxMagicBytesLength]; - for (int i = 0; i < maxMagicBytesLength; i += 1) + Size? result; + List magicBytes = []; + int[] magicBytesLengths = (from l in _ImageFormatDecoders.Keys where l.Length <= binaryReader.BaseStream.Length orderby l.Length descending select l.Length).ToArray(); + if (magicBytesLengths.Length == 0) + result = null; + else { - magicBytes[i] = binaryReader.ReadByte(); - foreach (KeyValuePair> kvPair in _ImageFormatDecoders) + result = null; + if (binaryReader.BaseStream.Length == binaryReader.BaseStream.Position) + _ = binaryReader.BaseStream.Seek(0, SeekOrigin.Begin); + for (int i = 0; i < magicBytesLengths[0]; i++) { - if (StartsWith(magicBytes, kvPair.Key)) - return kvPair.Value(binaryReader); + magicBytes.Add(binaryReader.ReadByte()); + foreach (KeyValuePair> kvPair in _ImageFormatDecoders) + { + if (StartsWith(magicBytes, kvPair.Key)) + { + result = kvPair.Value(binaryReader); + break; + } + } + if (result is not null) + break; } } - return null; + return result; } internal static Size? GetDimensions(string path) { - using BinaryReader binaryReader = new(File.OpenRead(path)); - return GetDimensions(binaryReader); + Size? result; + using FileStream fileStream = File.OpenRead(path); + using BinaryReader binaryReader = new(fileStream); + result = GetDimensions(binaryReader); + return result; } } \ No newline at end of file diff --git a/Metadata/Models/Stateless/Exif.cs b/Metadata/Models/Stateless/Exif.cs index 81309b4..1851114 100644 --- a/Metadata/Models/Stateless/Exif.cs +++ b/Metadata/Models/Stateless/Exif.cs @@ -75,9 +75,11 @@ internal abstract class Exif string? fileSource = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagFileSource); string? imageDescription = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageDescription); string? imageHeight = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageHeight); + int? imageHeightValue = imageHeight is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagImageHeight); string? imageNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageNumber); string? imageUniqueId = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageUniqueId); string? imageWidth = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageWidth); + int? imageWidthValue = imageWidth is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagImageWidth); string? isoSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagIsoSpeed); string? lensMake = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensMake); string? lensModel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensModel); @@ -164,52 +166,54 @@ internal abstract class Exif && xResolution is not null && yResolution is null) continue; - results.Add(new(aperture, - applicationNotes, - artist, - bitsPerSample, - bodySerialNumber, - cameraOwnerName, - compressedAverageBitsPerPixel, - compression, - copyright, - dateTime, - dateTimeDigitized, - dateTimeOriginal, - documentName, - exifVersion, - exposureTime, - fileSource, - imageDescription, - imageHeight, - imageNumber, - imageUniqueId, - imageWidth, - isoSpeed, - lensMake, - lensModel, - lensSerialNumber, - make, - makerNote, - model, - orientation, - orientationValue, - rating, - ratingPercent, - securityClassification, - shutterSpeed, - software, - timeZone, - timeZoneDigitized, - timeZoneOriginal, - userComment, - winAuthor, - winComment, - winKeywords, - winSubject, - winTitle, - xResolution, - yResolution)); + results.Add(new(Aperture: aperture, + ApplicationNotes: applicationNotes, + Artist: artist, + BitsPerSample: bitsPerSample, + BodySerialNumber: bodySerialNumber, + CameraOwnerName: cameraOwnerName, + CompressedAverageBitsPerPixel: compressedAverageBitsPerPixel, + Compression: compression, + Copyright: copyright, + DateTime: dateTime, + DateTimeDigitized: dateTimeDigitized, + DateTimeOriginal: dateTimeOriginal, + DocumentName: documentName, + ExifVersion: exifVersion, + ExposureTime: exposureTime, + FileSource: fileSource, + ImageDescription: imageDescription, + ImageHeight: imageHeight, + ImageHeightValue: imageHeightValue, + ImageNumber: imageNumber, + ImageUniqueId: imageUniqueId, + ImageWidth: imageWidth, + ImageWidthValue: imageWidthValue, + IsoSpeed: isoSpeed, + LensMake: lensMake, + LensModel: lensModel, + LensSerialNumber: lensSerialNumber, + Make: make, + MakerNote: makerNote, + Model: model, + Orientation: orientation, + OrientationValue: orientationValue, + Rating: rating, + RatingPercent: ratingPercent, + SecurityClassification: securityClassification, + ShutterSpeed: shutterSpeed, + Software: software, + TimeZone: timeZone, + TimeZoneDigitized: timeZoneDigitized, + TimeZoneOriginal: timeZoneOriginal, + UserComment: userComment, + WinAuthor: winAuthor, + WinComment: winComment, + WinKeywords: winKeywords, + WinSubject: winSubject, + WinTitle: winTitle, + XResolution: xResolution, + YResolution: yResolution)); } return results.ToArray(); } @@ -499,21 +503,21 @@ internal abstract class Exif Shared.Models.FileMetadataDirectory[] fileMetadataDirectories = GetFileMetadataDirectories(filePath.FullName, directories); Shared.Models.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories); Shared.Models.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories); - result = new(aviDirectories, - exifBaseDirectories, - fileMetadataDirectories, - filePath, - gifHeaderDirectories, - gpsDirectories, - size?.Height, - jpegDirectories, - makernoteDirectories, - photoshopDirectories, - pngDirectories, - quickTimeMovieHeaderDirectories, - quickTimeTrackHeaderDirectories, - webPDirectories, - size?.Width); + result = new(AviDirectories: aviDirectories, + ExifBaseDirectories: exifBaseDirectories, + FileMetadataDirectories: fileMetadataDirectories, + FilePath: filePath, + GifHeaderDirectories: gifHeaderDirectories, + GpsDirectories: gpsDirectories, + Height: size?.Height ?? Shared.Models.Stateless.Methods.IMetaBase.GetHeight(exifBaseDirectories), + JpegDirectories: jpegDirectories, + MakernoteDirectories: makernoteDirectories, + PhotoshopDirectories: photoshopDirectories, + PngDirectories: pngDirectories, + QuickTimeMovieHeaderDirectories: quickTimeMovieHeaderDirectories, + QuickTimeTrackHeaderDirectories: quickTimeTrackHeaderDirectories, + WebPDirectories: webPDirectories, + Width: size?.Width ?? Shared.Models.Stateless.Methods.IMetaBase.GetWidth(exifBaseDirectories)); return result; } diff --git a/Metadata/Models/Stateless/Methods/IMetadata.cs b/Metadata/Models/Stateless/Methods/IMetadata.cs index 352ae9a..0ae7b22 100644 --- a/Metadata/Models/Stateless/Methods/IMetadata.cs +++ b/Metadata/Models/Stateless/Methods/IMetadata.cs @@ -19,16 +19,6 @@ public interface IMetadata static ExifDirectory GetExifDirectory(FilePath filePath) => Exif.GetExifDirectory(filePath); - string? TestStatic_GetMaker(ExifDirectory? exifDirectory) => - GetMaker(exifDirectory); - static string? GetMaker(ExifDirectory? exifDirectory) => - Base.GetMaker(exifDirectory?.ExifBaseDirectories); - - string? TestStatic_GetModel(ExifDirectory? exifDirectory) => - GetModel(exifDirectory); - static string? GetModel(ExifDirectory? exifDirectory) => - Base.GetModel(exifDirectory?.ExifBaseDirectories); - string? TestStatic_GetOutputResolution(ExifDirectory? exifDirectory) => GetOutputResolution(exifDirectory); static string? GetOutputResolution(ExifDirectory? exifDirectory) => @@ -64,14 +54,4 @@ public interface IMetadata // static Dictionary GetMetadataCollection(FileInfo fileInfo, List> subFileTuples, List parseExceptions) => // Metadata.GetMetadataCollection(fileInfo, subFileTuples, parseExceptions); - string TestStatic_DateTimeFormat() => - DateTimeFormat(); - static string DateTimeFormat() => - "yyyy:MM:dd HH:mm:ss"; - - DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) => - GetDateTime(dateTimeFormat, value); - static DateTime? GetDateTime(string dateTimeFormat, string? value) => - Base.GetDateTime(dateTimeFormat, value); - } \ No newline at end of file diff --git a/Property/Models/A_Property.cs b/Property/Models/A_Property.cs index dd3826e..eb4094a 100644 --- a/Property/Models/A_Property.cs +++ b/Property/Models/A_Property.cs @@ -1,4 +1,3 @@ -using ShellProgressBar; using System.Collections.ObjectModel; using System.Text; using System.Text.Json; @@ -66,40 +65,6 @@ public class A_Property converted: false)); } - [Obsolete("Use ExifDirectory")] - public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata metadata, int t, Container.Models.Container[] containers) - { - int total = 0; - string message; - int totalSeconds; - bool anyNullOrNoIsUniqueFileName; - List exceptions = []; - Container.Models.Container container; - int containersLength = containers.Length; - const string outputResolution = "Original"; - List> sourceDirectoryChanges = []; - string propertyRoot = IResult.GetResultsGroupDirectory(_PropertyConfiguration, nameof(A_Property)); - for (int i = 0; i < containers.Length; i++) - { - container = containers[i]; - if (container.Items.Count == 0) - continue; - sourceDirectoryChanges.Clear(); - if (container.Items.Count == 0) - continue; - anyNullOrNoIsUniqueFileName = container.Items.Any(l => !l.IsUniqueFileName); - SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName); - totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; - SavePropertyParallelWork(_MaxDegreeOfParallelism, metadata, exceptions, sourceDirectoryChanges, container, container.Items, message); - if (exceptions.Count == container.Items.Count) - throw new Exception(string.Concat("All in [", container.SourceDirectory, "]failed!")); - if (exceptions.Count != 0) - _ExceptionsDirectories.Add(container.SourceDirectory); - total += container.Items.Count; - } - } - private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName) { _AngleBracketCollection.Clear(); @@ -112,180 +77,6 @@ public class A_Property SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); } - [Obsolete("Use ExifDirectory")] - private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata metadata, List exceptions, List> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection items, string message) - { - List> sourceDirectoryFileTuples = []; - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; - ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - using ProgressBar progressBar = new(items.Count, message, options); - _ = Parallel.For(0, items.Count, parallelOptions, (i, state) => - { - try - { - long ticks = DateTime.Now.Ticks; - DateTime dateTime = DateTime.Now; - List> collection; - SavePropertyParallelForWork(metadata, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]); - if (i == 0 || sourceDirectoryChanges.Count != 0) - progressBar.Tick(); - lock (sourceDirectoryFileTuples) - collection = (from l in sourceDirectoryFileTuples where l.Item2 > dateTime select l).ToList(); - lock (sourceDirectoryChanges) - sourceDirectoryChanges.AddRange(collection); - } - catch (Exception ex) - { - lock (exceptions) - exceptions.Add(ex); - } - }); - } - - [Obsolete("Use ExifDirectory")] - private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata metadata, string sourceDirectory, List> sourceDirectoryFileTuples, List> sourceDirectoryChanges, Item item) - { - Shared.Models.Property property; - List parseExceptions = []; - bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered); - string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}"); - if (item.IsValidImageFormatExtension && item.FilePath.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.FilePath.FullName != filteredSourceDirectoryFileExtensionLowered) - File.Move(item.FilePath.FullName, filteredSourceDirectoryFileExtensionLowered); - if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.ExifDirectory is null) - { - property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension); - lock (sourceDirectoryChanges) - sourceDirectoryChanges.Add(new Tuple(nameof(A_Property), DateTime.Now)); - lock (item) - item.Update(null); - } - } - - [Obsolete("Use ExifDirectory")] - private Shared.Models.Property GetImageProperty(Shared.Models.Methods.IMetadata metadata, Item item, List> sourceDirectoryFileTuples, List parseExceptions, bool isIgnoreExtension) - { - Shared.Models.Property? result; - int? id = null; - FileInfo fileInfo; - string? json = null; - bool hasWrongYearProperty = false; - string[] changesFrom = []; - string angleBracket = _AngleBracketCollection[0]; - bool populateId = _Configuration.PopulatePropertyId; - if (!item.IsUniqueFileName) - fileInfo = new(Path.Combine(angleBracket.Replace("<>", _PropertyConfiguration.ResultSingleton), $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json")); - else - { - string fileName = $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json"; - CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, item.FilePath); - string directory = _ResultSingletonFileGroups[0][cei.Enum][cei.Index]; - fileInfo = new(Path.Combine(directory, fileName)); - MoveIf(fileName, cei, directory, fileInfo); - } - List dateTimes = (from l in sourceDirectoryFileTuples where l is not null && changesFrom.Contains(l.Item1) select l.Item2).ToList(); - if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) - { - File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName); - fileInfo.Refresh(); - } - if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime) - { - File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); - fileInfo.Refresh(); - } - if (_Configuration.PropertiesChangedForProperty) - result = null; - else if (!fileInfo.Exists) - result = null; - else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old")) - throw new ArgumentException("must be a *.json file"); - else if (dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime) - result = null; - else - { - json = File.ReadAllText(fileInfo.FullName); - try - { - if (item.ExifDirectory is not null) - result = null; - else - result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); - if (result is not null && json.Contains("WrongYear")) - { - id = result.Id; - hasWrongYearProperty = true; - result = null; - } - if (!isIgnoreExtension && item.IsValidImageFormatExtension && ((populateId && result?.Id is null) || result?.Width is null || result.Height is null)) - { - id = result?.Id; - result = null; - } - if (!isIgnoreExtension && item.IsValidImageFormatExtension && populateId && result is not null && result.LastWriteTime.Ticks != item.FilePath.LastWriteTicks) - { - id = null; - result = null; - } - if (!isIgnoreExtension && item.IsValidImageFormatExtension && result?.Width is not null && result.Height is not null && result.Width.Value == result.Height.Value) - { - id = result.Id; - result = null; - if (result?.Width is not null && result.Height is not null && result.Width.Value != result.Height.Value) - throw new Exception("Was square!"); - } - if (!isIgnoreExtension && item.IsValidImageFormatExtension && result is not null && result.FileSize != item.FilePath.Length) - { - id = result.Id; - result = null; - } - if (result is not null) - { - sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), fileInfo.LastWriteTime)); - if (fileInfo.CreationTime != result.LastWriteTime) - { - File.SetCreationTime(fileInfo.FullName, result.LastWriteTime); - File.SetLastWriteTime(fileInfo.FullName, fileInfo.LastWriteTime); - } - } - } - catch (Exception) - { - result = null; - parseExceptions.Add(nameof(A_Property)); - } - } - if (result is null) - { - id ??= item.FilePath.Id; - (_, _, result) = Stateless.Property.GetProperty(populateId, metadata, item.FilePath, result, isIgnoreExtension, item.IsValidImageFormatExtension, id, _ASCIIEncoding); - json = JsonSerializer.Serialize(result, PropertyGenerationContext.Default.Property); - if (populateId && Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: true, compareBeforeWrite: true)) - { - File.SetCreationTime(fileInfo.FullName, result.LastWriteTime); - if (!_Configuration.ForcePropertyLastWriteTimeToCreationTime) - sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), DateTime.Now)); - else - { - File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); - fileInfo.Refresh(); - sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), fileInfo.CreationTime)); - } - } - } - else if (hasWrongYearProperty) - { - json = JsonSerializer.Serialize(result, PropertyGenerationContext.Default.Property); - if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: true, compareBeforeWrite: true)) - { - File.SetCreationTime(fileInfo.FullName, result.LastWriteTime); - File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); - fileInfo.Refresh(); - sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), fileInfo.CreationTime)); - } - } - return result; - } - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo) { string[] segments = directory.Split(cei.Combined); @@ -305,22 +96,4 @@ public class A_Property } } - [Obsolete("Use ExifDirectory")] - public Shared.Models.Property GetProperty(Shared.Models.Methods.IMetadata metadata, Item item, List> sourceDirectoryFileTuples, List parseExceptions) - { - Shared.Models.Property result; - bool angleBracketCollectionAny = _AngleBracketCollection.Count != 0; - if (!angleBracketCollectionAny) - { - if (item.FilePath.DirectoryFullPath is null) - throw new NullReferenceException(nameof(item.FilePath.DirectoryFullPath)); - SetAngleBracketCollection(item.FilePath.DirectoryFullPath, !item.IsUniqueFileName); - } - bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered); - result = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension); - if (!angleBracketCollectionAny) - _AngleBracketCollection.Clear(); - return result; - } - } \ No newline at end of file diff --git a/Resize/Models/_C_Resize.cs b/Resize/Models/_C_Resize.cs index 26145bd..b93d1ab 100644 --- a/Resize/Models/_C_Resize.cs +++ b/Resize/Models/_C_Resize.cs @@ -284,7 +284,7 @@ public class C_Resize int checkHeight; int desiredWidth; int desiredHeight; - int? orientation = Shared.Models.Stateless.Methods.IMetaBase.GetOrientation(exifDirectory.ExifBaseDirectories); + int? orientation = Shared.Models.Stateless.Methods.IMetaBase.GetOrientation(exifDirectory); if (exifDirectory is null || orientation is null || exifDirectory.Width is null || exifDirectory.Height is null) throw new NotSupportedException(); checkWidth = exifDirectory.Width.Value; diff --git a/Shared/Models/ExifDirectory.cs b/Shared/Models/ExifDirectory.cs index 70b9651..5bf0d6a 100644 --- a/Shared/Models/ExifDirectory.cs +++ b/Shared/Models/ExifDirectory.cs @@ -6,7 +6,7 @@ namespace View_by_Distance.Shared.Models; public record ExifDirectory(AviDirectory[] AviDirectories, ExifDirectoryBase[] ExifBaseDirectories, FileMetadataDirectory[] FileMetadataDirectories, - FilePath FilePath, + FilePath? FilePath, GifHeaderDirectory[] GifHeaderDirectories, GpsDirectory[] GpsDirectories, int? Height, diff --git a/Shared/Models/ExifDirectoryBase.cs b/Shared/Models/ExifDirectoryBase.cs index 8235471..186d936 100644 --- a/Shared/Models/ExifDirectoryBase.cs +++ b/Shared/Models/ExifDirectoryBase.cs @@ -21,9 +21,11 @@ public record ExifDirectoryBase(string? Aperture, string? FileSource, string? ImageDescription, string? ImageHeight, + int? ImageHeightValue, string? ImageNumber, string? ImageUniqueId, string? ImageWidth, + int? ImageWidthValue, string? IsoSpeed, string? LensMake, string? LensModel, diff --git a/Shared/Models/FaceFileSystem.cs b/Shared/Models/FaceFileSystem.cs index 0f529bc..776ab2e 100644 --- a/Shared/Models/FaceFileSystem.cs +++ b/Shared/Models/FaceFileSystem.cs @@ -76,7 +76,7 @@ public class FaceFileSystem : FileSystem, Properties.IFaceFileSystem } else { - System.Drawing.Size size = Stateless.Methods.ImageHelper.GetDimensions(fileInfo.FullName, faceRight, faceBottom); + System.Drawing.Size size = Stateless.Methods.Dimensions.GetDimensions(fileInfo.FullName, faceRight, faceBottom); imageWidth = size.Width; imageHeight = size.Height; } diff --git a/Shared/Models/FilePair.cs b/Shared/Models/FilePair.cs index 065114a..775dfa2 100644 --- a/Shared/Models/FilePair.cs +++ b/Shared/Models/FilePair.cs @@ -1,3 +1,4 @@ +using System.Collections.ObjectModel; using System.Text.Json; using System.Text.Json.Serialization; @@ -6,7 +7,7 @@ namespace View_by_Distance.Shared.Models; public record FilePair(FilePath FilePath, bool IsUnique, bool? IsNotUniqueAndNeedsReview, - List Collection, + ReadOnlyCollection Collection, FilePath? Match) { diff --git a/Shared/Models/FilePath.cs b/Shared/Models/FilePath.cs index 7c41812..920bd16 100644 --- a/Shared/Models/FilePath.cs +++ b/Shared/Models/FilePath.cs @@ -21,6 +21,20 @@ public record FilePath(long CreationTicks, int? SortOrder) { + public static FilePath? GetNullSafe(Properties.IPropertyConfiguration propertyConfiguration, FileHolder fileHolder, int? index) + { + FilePath? result; + if (fileHolder.CreationTime is null) + result = null; + else if (fileHolder.LastWriteTime is null) + result = null; + else if (fileHolder.Length is null) + result = null; + else + result = Get(propertyConfiguration, fileHolder, index); + return result; + } + public static FilePath Get(Properties.IPropertyConfiguration propertyConfiguration, FileHolder fileHolder, int? index) { if (fileHolder.CreationTime is null) @@ -78,28 +92,6 @@ 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 = []; @@ -123,7 +115,7 @@ public record FilePath(long CreationTicks, } } foreach (KeyValuePair> keyValuePair in keyValuePairs) - results.Add(keyValuePair.Key, new(keyValuePair.Value)); + results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); return results.AsReadOnly(); } diff --git a/Shared/Models/MappingFromItem.cs b/Shared/Models/MappingFromItem.cs index 54f97fd..aa39533 100644 --- a/Shared/Models/MappingFromItem.cs +++ b/Shared/Models/MappingFromItem.cs @@ -29,17 +29,17 @@ public record MappingFromItem(DateTime[] ContainerDateTimes, internal static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Item item, FileHolder? resizedFileHolder) { MappingFromItem result; - if (item.ExifDirectory?.FilePath.Id is null) + if (item.ExifDirectory?.FilePath?.Id is null) throw new NotSupportedException(); if (resizedFileHolder is null) throw new NotSupportedException(); ExifDirectory exifDirectory = item.ExifDirectory; + string? model = Stateless.Methods.IMetaBase.GetModel(exifDirectory); DateTime minimumDateTime = Stateless.Methods.IDate.GetMinimum(exifDirectory); DateTime? dateTime = Stateless.Methods.IDate.GetDateTimeOriginal(exifDirectory); - string? model = Stateless.Methods.MetaBase.GetModel(exifDirectory.ExifBaseDirectories); ReadOnlyCollection dateTimes = Stateless.Methods.IDate.GetDateTimes(exifDirectory); + ReadOnlyCollection keywords = Stateless.Methods.IMetaBase.GetKeywords(exifDirectory); (bool? isWrongYear, _) = Stateless.Methods.IProperty.IsWrongYear(item.FilePath, dateTime, dateTimes.ToList()); - ReadOnlyCollection keywords = Stateless.Methods.MetaBase.GetKeywords(exifDirectory.ExifBaseDirectories); result = new(containerDateTimes, dateTime, exifDirectory.FilePath.Id.Value, item.IsArchive, item.FilePath, isWrongYear, keywords, minimumDateTime, model, item.RelativePath, resizedFileHolder); return result; } diff --git a/Shared/Models/Stateless/Methods/Dimensions.cs b/Shared/Models/Stateless/Methods/Dimensions.cs new file mode 100644 index 0000000..05d3535 --- /dev/null +++ b/Shared/Models/Stateless/Methods/Dimensions.cs @@ -0,0 +1,149 @@ +using System.Drawing; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal static class Dimensions +{ + +#pragma warning disable IDE0230 + private static readonly Dictionary> _ImageFormatDecoders = new() + { + { new byte[] { 0xff, 0xd8 }, DecodeJfif }, + { new byte[] { 0x42, 0x4D }, DecodeBitmap }, + { new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP }, + { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, + { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, + { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, + }; +#pragma warning restore IDE0230 + + private static bool StartsWith(List thisBytes, byte[] thatBytes) + { + for (int i = 0; i < thisBytes.Count && i < thatBytes.Length; i += 1) + { + if (thisBytes[i] == thatBytes[i]) + continue; + return false; + } + return true; + } + + private static short ReadLittleEndianInt16(BinaryReader binaryReader) + { + byte[] bytes = new byte[sizeof(short)]; + for (int i = 0; i < sizeof(short); i += 1) + bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); + return BitConverter.ToInt16(bytes, 0); + } + + private static int ReadLittleEndianInt32(BinaryReader binaryReader) + { + byte[] bytes = new byte[sizeof(int)]; + for (int i = 0; i < sizeof(int); i += 1) + bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); + return BitConverter.ToInt32(bytes, 0); + } + + private static Size? DecodeBitmap(BinaryReader binaryReader) + { + _ = binaryReader.ReadBytes(16); + int width = binaryReader.ReadInt32(); + int height = binaryReader.ReadInt32(); + return new Size(width, height); + } + + private static Size? DecodeGif(BinaryReader binaryReader) + { + int width = binaryReader.ReadInt16(); + int height = binaryReader.ReadInt16(); + return new Size(width, height); + } + + private static Size? DecodePng(BinaryReader binaryReader) + { + _ = binaryReader.ReadBytes(8); + int width = ReadLittleEndianInt32(binaryReader); + int height = ReadLittleEndianInt32(binaryReader); + return new Size(width, height); + } + + private static Size? DecodeJfif(BinaryReader binaryReader) + { + while (binaryReader.ReadByte() == 0xff) + { + byte marker = binaryReader.ReadByte(); + short chunkLength = ReadLittleEndianInt16(binaryReader); + if (marker == 0xc0) + { + _ = binaryReader.ReadByte(); + int height = ReadLittleEndianInt16(binaryReader); + int width = ReadLittleEndianInt16(binaryReader); + return new Size(width, height); + } + if (chunkLength >= 0) + _ = binaryReader.ReadBytes(chunkLength - 2); + else + { + ushort uChunkLength = (ushort)chunkLength; + _ = binaryReader.ReadBytes(uChunkLength - 2); + } + } + return null; + } + + private static Size? DecodeWebP(BinaryReader binaryReader) + { + _ = binaryReader.ReadUInt32(); // Size + _ = binaryReader.ReadBytes(15); // WEBP, VP8 + more + _ = binaryReader.ReadBytes(3); // SYNC + int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width + int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height + return new Size(width, height); + } + + internal static Size GetDimensions(BinaryReader binaryReader, int? faceRight, int? faceBottom) + { + Size? result; + List magicBytes = []; + int[] magicBytesLengths = (from l in _ImageFormatDecoders.Keys where l.Length <= binaryReader.BaseStream.Length orderby l.Length descending select l.Length).ToArray(); + if (magicBytesLengths.Length == 0) + result = null; + else + { + result = null; + if (binaryReader.BaseStream.Length == binaryReader.BaseStream.Position) + _ = binaryReader.BaseStream.Seek(0, SeekOrigin.Begin); + for (int i = 0; i < magicBytesLengths[0]; i++) + { + magicBytes.Add(binaryReader.ReadByte()); + foreach (KeyValuePair> kvPair in _ImageFormatDecoders) + { + if (StartsWith(magicBytes, kvPair.Key)) + { + result = kvPair.Value(binaryReader); + break; + } + } + if (result is not null) + break; + } + } + if (result is null) + { + if (faceRight is null || faceBottom is null) + throw new Exception("face is null!"); + result = new(faceRight.Value, faceBottom.Value); + } + return result.Value; + } + + internal static Size GetDimensions(string path, int? faceRight, int? faceBottom) + { + Size result; + using FileStream fileStream = File.OpenRead(path); + using BinaryReader binaryReader = new(fileStream); + result = GetDimensions(binaryReader, faceRight, faceBottom); + return result; + } + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/FilePair.cs b/Shared/Models/Stateless/Methods/FilePair.cs index 49d15b8..c5317d6 100644 --- a/Shared/Models/Stateless/Methods/FilePair.cs +++ b/Shared/Models/Stateless/Methods/FilePair.cs @@ -7,29 +7,22 @@ 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++) + ReadOnlyDictionary> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection); + IReadOnlyDictionary>? compareFileNamesToFiles = null; + for (int i = 0; i < idToFilePaths.Count; i++) { renamed = 0; jsonFilesCollection = XDirectory.GetFilesCollection(jsonGroupDirectory, directorySearchFilter, extension, useCeilingAverage); - renamed += LookForAbandoned(propertyConfiguration, jsonFilesCollection, fileNamesToFiles, extension); + renamed += LookForAbandoned(propertyConfiguration, jsonFilesCollection, idToFilePaths, extension); if (renamed > 0) continue; compareFileNamesToFiles = GetKeyValuePairs(propertyConfiguration, jsonFilesCollection); - results = XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles); + results = XDirectory.GetFiles(propertyConfiguration, filePathsCollection, idToFilePaths, compareFileNamesToFiles); renamed += XDirectory.MaybeMove(propertyConfiguration, results, jsonGroupDirectory, extension); if (renamed == 0) break; @@ -41,7 +34,7 @@ internal abstract class FilePair return results.AsReadOnly(); } - private static int LookForAbandoned(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection jsonFilesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension) + private static int LookForAbandoned(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection jsonFilesCollection, IReadOnlyDictionary> fileNamesToFiles, string extension) { int result; bool check; @@ -63,7 +56,7 @@ internal abstract class FilePair return result; } - private static bool AnyMoved(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary> fileNamesToFiles, string extension, List renameCollection, string[] files) + private static bool AnyMoved(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary> fileNamesToFiles, string extension, List renameCollection, string[] files) { bool result = false; string checkFile; @@ -72,11 +65,13 @@ internal abstract class FilePair string fileNameWith; string checkDirectory; string directoryName; - List? collection; Models.FileHolder fileHolder; List directoryNames = []; + ReadOnlyCollection? collection; foreach (string file in files) { + if (files.Any(l => l.StartsWith(propertyConfiguration.RootDirectory))) + continue; if (!file.EndsWith(extension)) throw new Exception(); fileHolder = IFileHolder.Get(file); @@ -120,12 +115,13 @@ internal abstract class FilePair return result; } - private static ReadOnlyDictionary> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) + private static ReadOnlyDictionary> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection) { - Dictionary> results = []; - List? collection; + Dictionary> results = []; FilePath filePath; + List? collection; Models.FileHolder fileHolder; + Dictionary> keyValuePairs = []; foreach (string[] files in filesCollection) { foreach (string file in files) @@ -136,15 +132,17 @@ internal abstract class FilePair filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); if (filePath.Id is null) continue; - if (!results.TryGetValue(filePath.Id.Value, out collection)) + if (!keyValuePairs.TryGetValue(filePath.Id.Value, out collection)) { - results.Add(filePath.Id.Value, []); - if (!results.TryGetValue(filePath.Id.Value, out collection)) + keyValuePairs.Add(filePath.Id.Value, []); + if (!keyValuePairs.TryGetValue(filePath.Id.Value, out collection)) throw new Exception(); } collection.Add(filePath); } } + foreach (KeyValuePair> keyValuePair in keyValuePairs) + results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); return new(results); } diff --git a/Shared/Models/Stateless/Methods/IDirectory.cs b/Shared/Models/Stateless/Methods/IDirectory.cs index 753f1b2..449b1d4 100644 --- a/Shared/Models/Stateless/Methods/IDirectory.cs +++ b/Shared/Models/Stateless/Methods/IDirectory.cs @@ -29,13 +29,13 @@ public interface IDirectory 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) => + 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, 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) => + public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, ReadOnlyDictionary exifDirectoriesById, Action? tick) => XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates: false, ifCanUseId: true, filePathsCollection, fileGroups, exifDirectoriesById, tick); internal int TestStatic_GetDirectory(char directory) => @@ -62,13 +62,13 @@ public interface IDirectory 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) => + 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, 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) => + internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, ReadOnlyDictionary 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 d317bd7..a1fe46c 100644 --- a/Shared/Models/Stateless/Methods/IDlibDotNet.cs +++ b/Shared/Models/Stateless/Methods/IDlibDotNet.cs @@ -4,6 +4,7 @@ public interface IDlibDotNet { void Tick(); + long Ticks { get; } (string, string) GetResultsFullGroupDirectories(); void ConstructProgressBar(int maxTicks, string message); (string, string, string, string) GetResultsFullGroupDirectories(string outputResolution); diff --git a/Shared/Models/Stateless/Methods/IFilePair.cs b/Shared/Models/Stateless/Methods/IFilePair.cs index 0585149..7394edd 100644 --- a/Shared/Models/Stateless/Methods/IFilePair.cs +++ b/Shared/Models/Stateless/Methods/IFilePair.cs @@ -9,13 +9,7 @@ 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/IMetaBase.cs b/Shared/Models/Stateless/Methods/IMetaBase.cs index 19e5992..0d3c917 100644 --- a/Shared/Models/Stateless/Methods/IMetaBase.cs +++ b/Shared/Models/Stateless/Methods/IMetaBase.cs @@ -5,24 +5,66 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IMetaBase { + public static string DateTimeFormat() => + "yyyy:MM:dd HH:mm:ss"; + + public static string? GetMaker(ExifDirectory? exifDirectory) => + MetaBase.GetMaker(exifDirectory?.ExifBaseDirectories); + + public static string? GetModel(ExifDirectory? exifDirectory) => + MetaBase.GetModel(exifDirectory?.ExifBaseDirectories); + + public static int? GetOrientation(ExifDirectory? exifDirectory) => + MetaBase.GetOrientation(exifDirectory?.ExifBaseDirectories); + + public static int? GetWidth(ExifDirectoryBase[]? exifBaseDirectories) => + MetaBase.GetWidth(exifBaseDirectories); + + public static int? GetHeight(ExifDirectoryBase[]? exifBaseDirectories) => + MetaBase.GetHeight(exifBaseDirectories); + public static string? GetMaker(ExifDirectoryBase[]? exifBaseDirectories) => MetaBase.GetMaker(exifBaseDirectories); public static string? GetModel(ExifDirectoryBase[]? exifBaseDirectories) => MetaBase.GetModel(exifBaseDirectories); + public static DateTime? GetDateTime(string dateTimeFormat, string? value) => + MetaBase.GetDateTime(dateTimeFormat, value); + public static int? GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) => MetaBase.GetOrientation(exifBaseDirectories); + public static ReadOnlyCollection GetKeywords(ExifDirectory? exifDirectory) => + MetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories); + public static ReadOnlyCollection GetKeywords(ExifDirectoryBase[]? exifBaseDirectories) => MetaBase.GetKeywords(exifBaseDirectories); + internal string TestStatic_DateTimeFormat() => + DateTimeFormat(); + + internal string? TestStatic_GetMaker(ExifDirectory? exifDirectory) => + GetMaker(exifDirectory); + + internal string? TestStatic_GetModel(ExifDirectory? exifDirectory) => + GetModel(exifDirectory); + + internal static int? TestStatic_GetWidth(ExifDirectoryBase[]? exifBaseDirectories) => + GetWidth(exifBaseDirectories); + + internal static int? TestStatic_GetHeight(ExifDirectoryBase[]? exifBaseDirectories) => + GetHeight(exifBaseDirectories); + internal static string? TestStatic_GetMaker(ExifDirectoryBase[]? exifBaseDirectories) => GetMaker(exifBaseDirectories); internal static string? TestStatic_GetModel(ExifDirectoryBase[]? exifBaseDirectories) => GetModel(exifBaseDirectories); + internal static DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) => + GetDateTime(dateTimeFormat, value); + internal static int? TestStatic_GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) => GetOrientation(exifBaseDirectories); diff --git a/Shared/Models/Stateless/Methods/Id.cs b/Shared/Models/Stateless/Methods/Id.cs index 191391b..3c75879 100644 --- a/Shared/Models/Stateless/Methods/Id.cs +++ b/Shared/Models/Stateless/Methods/Id.cs @@ -45,10 +45,10 @@ internal abstract class Id } internal static byte GetHasDateTimeOriginal(Properties.IPropertyConfiguration propertyConfiguration, FilePath filePath) => - (byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 9 : 1 : filePath.Id > -1 ? 6 : 4); + (byte)(propertyConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered) || propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 6 : 4 : filePath.Id > -1 ? 9 : 1); internal static byte GetMissingDateTimeOriginal(Properties.IPropertyConfiguration propertyConfiguration, FilePath filePath) => - (byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 7 : 3 : filePath.Id > -1 ? 5 : 0); + (byte)(propertyConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered) || propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 5 : 0 : filePath.Id > -1 ? 7 : 3); internal static bool NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameWithoutExtension) { diff --git a/Shared/Models/Stateless/Methods/ImageHelper.cs b/Shared/Models/Stateless/Methods/ImageHelper.cs deleted file mode 100644 index e4493f2..0000000 --- a/Shared/Models/Stateless/Methods/ImageHelper.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System.Drawing; - -namespace View_by_Distance.Shared.Models.Stateless.Methods; - -internal abstract class ImageHelper -{ - - private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) - { - for (int i = 0; i < thatBytes.Length; i += 1) - { - if (thisBytes[i] != thatBytes[i]) - return false; - } - return true; - } - - private static short ReadLittleEndianInt16(BinaryReader binaryReader) - { - byte[] bytes = new byte[sizeof(short)]; - for (int i = 0; i < sizeof(short); i += 1) - bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); - return BitConverter.ToInt16(bytes, 0); - } - - private static int ReadLittleEndianInt32(BinaryReader binaryReader) - { - byte[] bytes = new byte[sizeof(int)]; - for (int i = 0; i < sizeof(int); i += 1) - bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); - return BitConverter.ToInt32(bytes, 0); - } - - private static Size DecodeBitmap(BinaryReader binaryReader) - { - _ = binaryReader.ReadBytes(16); - int width = binaryReader.ReadInt32(); - int height = binaryReader.ReadInt32(); - return new Size(width, height); - } - - private static Size DecodeGif(BinaryReader binaryReader) - { - int width = binaryReader.ReadInt16(); - int height = binaryReader.ReadInt16(); - return new Size(width, height); - } - - private static Size DecodePng(BinaryReader binaryReader) - { - _ = binaryReader.ReadBytes(8); - int width = ReadLittleEndianInt32(binaryReader); - int height = ReadLittleEndianInt32(binaryReader); - return new Size(width, height); - } - - private static Size DecodeJfif(BinaryReader binaryReader) - { - while (binaryReader.ReadByte() == 0xff) - { - byte marker = binaryReader.ReadByte(); - short chunkLength = ReadLittleEndianInt16(binaryReader); - if (marker == 0xc0) - { - _ = binaryReader.ReadByte(); - int height = ReadLittleEndianInt16(binaryReader); - int width = ReadLittleEndianInt16(binaryReader); - return new Size(width, height); - } - _ = binaryReader.ReadBytes(chunkLength - 2); - } - throw new ArgumentException("Could not recognize image format."); - } - - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognized format. - internal static Size GetDimensions(BinaryReader binaryReader, int? faceRight, int? faceBottom) - { - Size? result = null; -#pragma warning disable IDE0230 - Dictionary> _ImageFormatDecoders = new() - { - { new byte[] { 0x42, 0x4D }, DecodeBitmap }, - { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, - { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, - { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, - { new byte[] { 0xff, 0xd8 }, DecodeJfif }, - }; -#pragma warning restore IDE0230 - int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; - byte[] magicBytes = new byte[maxMagicBytesLength]; - for (int i = 0; i < maxMagicBytesLength; i += 1) - { - magicBytes[i] = binaryReader.ReadByte(); - foreach (KeyValuePair> kvPair in _ImageFormatDecoders) - { - if (!StartsWith(magicBytes, kvPair.Key)) - continue; - result = kvPair.Value(binaryReader); - break; - } - if (result is not null) - break; - } - if (result is null) - { - if (faceRight is null || faceBottom is null) - throw new Exception("face is null!"); - result = new(faceRight.Value, faceBottom.Value); - } - return result.Value; - } - - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognized format. - internal static Size GetDimensions(string path, int? faceRight, int? faceBottom) - { - Size result; - using BinaryReader binaryReader = new(File.OpenRead(path)); - result = GetDimensions(binaryReader, faceRight, faceBottom); - return result; - } - -} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/MetaBase.cs b/Shared/Models/Stateless/Methods/MetaBase.cs index 7a7e19c..d0a192b 100644 --- a/Shared/Models/Stateless/Methods/MetaBase.cs +++ b/Shared/Models/Stateless/Methods/MetaBase.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using System.Globalization; namespace View_by_Distance.Shared.Models.Stateless.Methods; @@ -80,4 +81,55 @@ internal static class MetaBase return results.AsReadOnly(); } + internal static int? GetWidth(ExifDirectoryBase[]? exifBaseDirectories) + { + int? result = null; + // public const int TagImageWidth = 256; + if (exifBaseDirectories is not null) + { + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + result = exifDirectoryBase?.ImageWidthValue; + if (result is not null) + break; + } + } + return result; + } + + internal static int? GetHeight(ExifDirectoryBase[]? exifBaseDirectories) + { + int? result = null; + // public const int TagImageHeight = 257; + if (exifBaseDirectories is not null) + { + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + result = exifDirectoryBase?.ImageHeightValue; + if (result is not null) + break; + } + } + return result; + } + +#pragma warning disable CA1416 + + internal static DateTime? GetDateTime(string dateTimeFormat, string? value) + { + DateTime? result; + string alternateFormat = "ddd MMM dd HH:mm:ss yyyy"; + if (value is not null && DateTime.TryParse(value, out DateTime dateTime)) + result = dateTime; + else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + result = dateTime; + else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + result = dateTime; + else + result = null; + return result; + } + +#pragma warning restore CA1416 + } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/XDate.cs b/Shared/Models/Stateless/Methods/XDate.cs index 9bf6995..6de5bb5 100644 --- a/Shared/Models/Stateless/Methods/XDate.cs +++ b/Shared/Models/Stateless/Methods/XDate.cs @@ -40,8 +40,8 @@ internal abstract class XDate } if (results.Count == 0) { - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(exifDirectory.FilePath.Name); - DateTime? dateTime = GetDateTimeFromName(fileNameWithoutExtension); + string? fileNameWithoutExtension = exifDirectory.FilePath is null ? null : Path.GetFileNameWithoutExtension(exifDirectory.FilePath.Name); + DateTime? dateTime = fileNameWithoutExtension is null ? null : GetDateTimeFromName(fileNameWithoutExtension); if (dateTime is not null) results.Add(dateTime.Value); foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) diff --git a/Shared/Models/Stateless/Methods/XDirectory.cs b/Shared/Models/Stateless/Methods/XDirectory.cs index 6372eea..580ac0f 100644 --- a/Shared/Models/Stateless/Methods/XDirectory.cs +++ b/Shared/Models/Stateless/Methods/XDirectory.cs @@ -188,7 +188,7 @@ internal abstract partial class XDirectory filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null); filePaths.Add(filePath); } - results.Add(new(filePaths)); + results.Add(filePaths.AsReadOnly()); } return results.AsReadOnly(); } @@ -201,14 +201,14 @@ internal abstract partial class XDirectory 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 = []; FilePath? match; bool uniqueFileName; - List? collection; Models.FilePair filePair; bool? isNotUniqueAndNeedsReview; + ReadOnlyCollection? collection; foreach (ReadOnlyCollection filePaths in filePathsCollection) { foreach (FilePath filePath in filePaths) @@ -227,7 +227,7 @@ internal abstract partial class XDirectory filePair = new(FilePath: filePath, IsUnique: uniqueFileName, IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview, - Collection: [], + Collection: new([]), Match: null); else { @@ -259,7 +259,7 @@ internal abstract partial class XDirectory return results; } - private static bool GetIsNotUniqueAndNeedsReview(FilePath filePath, List collection) + private static bool GetIsNotUniqueAndNeedsReview(FilePath filePath, ReadOnlyCollection collection) { bool result = false; long max; @@ -280,7 +280,7 @@ internal abstract partial class XDirectory return result; } - private static FilePath? GetMatch(FilePath filePath, List collection) + private static FilePath? GetMatch(FilePath filePath, ReadOnlyCollection collection) { FilePath? result = null; List lengths = []; @@ -299,7 +299,7 @@ internal abstract partial class XDirectory return result; } - internal static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, Dictionary? exifDirectoriesById, Action? tick) + internal static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary> fileGroups, ReadOnlyDictionary? exifDirectoriesById, Action? tick) { List<(FilePath, string)> results = []; string paddedId; diff --git a/Shared/Models/Stateless/Methods/XPath.cs b/Shared/Models/Stateless/Methods/XPath.cs index d86b3b1..e875cf9 100644 --- a/Shared/Models/Stateless/Methods/XPath.cs +++ b/Shared/Models/Stateless/Methods/XPath.cs @@ -446,7 +446,7 @@ internal abstract class XPath { Dictionary> results = []; foreach (KeyValuePair> keyValuePair in keyValuePairs) - results.Add(keyValuePair.Key, new(keyValuePair.Value)); + results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly()); return results.AsReadOnly(); } diff --git a/View-by-Distance-MKLink-Console.sln b/View-by-Distance-MKLink-Console.sln index 913b79f..369e024 100644 --- a/View-by-Distance-MKLink-Console.sln +++ b/View-by-Distance-MKLink-Console.sln @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Search", "Drag-Dr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Set-Property-Item", "Drag-Drop-Set-Property-Item\Drag-Drop-Set-Property-Item.csproj", "{BFF75D8C-48E6-4B84-B480-3E5A4F9B2DD8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicate-Search", "Duplicate-Search\Duplicate-Search.csproj", "{48E87D9B-B802-467A-BDC7-E86F7FD01D5C}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Face", "Face\Face.csproj", "{A12E19E5-59C0-40D4-B807-DF1334D4906D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FaceParts", "FaceParts\FaceParts.csproj", "{919525B1-60BA-40C6-BA66-6F7F4C526E01}"