Removed Obsolete A_Property Methods

Changed GetDimensions to handle a stream at the end and one exit

Switched to using Action? over IDlibDotNet for Tick method

Switched to using AsReadOnly over new()

Moved Meta Base to Shared
This commit is contained in:
2025-06-30 16:38:30 -07:00
parent c7ded16e50
commit 86f0ff0486
38 changed files with 901 additions and 960 deletions

12
.vscode/tasks.json vendored
View File

@ -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",

View File

@ -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<FilePath> Collection,
ReadOnlyCollection<FilePath> 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<int, Identifier>? splatNineIdentifiers, string _)
internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers)
{
int count;
Models.Container[] results;
@ -66,18 +69,18 @@ internal abstract class Container
const string fileSearchFilter = "*";
IDlibDotNet dlibDotNet = GetDlibDotNet();
const string directorySearchFilter = "*";
ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById = null;
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs = null;
ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById = new(new Dictionary<int, ExifDirectory>());
ReadOnlyCollection<string[]> filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
(count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter);
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> 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<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, string directorySearchFilter)
private static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById, string directorySearchFilter)
{
List<Models.Container> 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<FilePair> filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter);
ReadOnlyCollection<FilePair> filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, filesCollectionDirectory, idToFilePaths, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter);
foreach (FilePair filePair in filePairs)
{
if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items))
@ -120,47 +119,80 @@ 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<FilePair> GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, string directorySearchFilter)
private static ReadOnlyCollection<FilePair> GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById, string directorySearchFilter)
{
List<FilePair> results = [];
ReadOnlyCollection<FilePair> results;
const string extension = ".json";
Dictionary<int, ExifDirectory> keyValuePairs = [];
ReadOnlyCollection<Shared.Models.FilePair> 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<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, int rootDirectoryLength, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, Shared.Models.FilePair filePair, List<FilePair> results)
private static ReadOnlyCollection<FilePair> GetFilePairs(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById, string extension, Dictionary<int, ExifDirectory> keyValuePairs, ReadOnlyCollection<Shared.Models.FilePair> filePairs, string jsonGroupDirectory, int rootDirectoryLength)
{
dlibDotNet?.Tick();
bool abandoned = false;
List<FilePair> 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<Record> records = [];
bool? lastWriteTimeChanged;
char missingDateTimeOriginal;
ExifDirectory? exifDirectory;
bool isValidImageFormatExtension;
ReadOnlyCollection<string> keywords;
FileHolder sourceDirectoryFileHolder;
if (exifDirectoriesById is null || filePair.FilePath.Id is null || !exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out ExifDirectory? exifDirectory))
ReadOnlyCollection<FilePath>? filePaths;
foreach (Shared.Models.FilePair filePair in filePairs)
{
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);
ReadOnlyCollection<string> 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);
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)
{
char? change;
ReadOnlyCollection<FilePath>? 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];
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)
{
if (filePair.FilePath.FileNameFirstSegment[^1] == hasIgnoreKeyword)
@ -168,7 +200,7 @@ internal abstract class Container
else
{
change = hasIgnoreKeyword;
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
if (!idToFilePaths.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>");
}
}
@ -179,14 +211,14 @@ internal abstract class Container
else
{
change = missingDateTimeOriginal;
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
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 (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
if (!idToFilePaths.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>");
}
else
@ -194,31 +226,53 @@ internal abstract class Container
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;
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
{
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);
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);
sourceDirectoryFileHolder = IFileHolder.Get(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 = Item.Get(filePair.FilePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, exifDirectory, abandoned, fileSizeChanged, lastWriteTimeChanged);
lock (results)
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<int, ExifDirectory> exifDirectoriesById, Shared.Models.FilePair filePair, Dictionary<int, ExifDirectory> 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<FilePath> 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<Models.Container> GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById)
internal static ReadOnlyCollection<Models.Container> GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> 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<int, ReadOnlyCollection<FilePath>> 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<ReadOnlyCollection<FilePath>> filePathsCollection, string directorySearchFilter)
private static void AnyMoved(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths, string directorySearchFilter)
{
const string extension = ".json";
(_, string bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories();
ReadOnlyDictionary<int, List<FilePath>> 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<int, List<FilePath>> fileNamesToFiles, string jsonGroupDirectory)
private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, string extension, string hiddenExtension, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> 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<int, List<FilePath>> fileNamesToFiles, List<string> directories)
private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> fileNamesToFiles, List<string> directories)
{
bool result = false;
string checkFile;
FilePath filePath;
FilePath? filePath;
string subDirectory;
string directoryName;
string checkDirectory;
FileHolder fileHolder;
string directoryNameWith;
List<FilePath>? collection;
List<string> directoryNames = [];
ReadOnlyCollection<FilePath>? 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<int, List<FilePath>> fileNamesToFiles, string jsonGroupDirectory)
private static void AnyMovedDistance(IPropertyConfiguration propertyConfiguration, string extension, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths, string jsonGroupDirectory)
{
bool result = false;
string checkFile;
@ -445,8 +498,8 @@ internal abstract class Container
FilePath filePath;
FileHolder fileHolder;
string[] fileNameSegments;
List<FilePath>? collection;
List<string> fileNames = [];
ReadOnlyCollection<FilePath>? 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)

View File

@ -8,17 +8,14 @@ namespace View_by_Distance.Container.Models.Stateless.Methods;
public interface IContainer
{
public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string aPropertySingletonDirectory) =>
Container.GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory);
public static DateTime[] GetContainerDateTimes(ReadOnlyCollection<Item> items) =>
Container.GetContainerDateTimes(items);
public static ReadOnlyCollection<Item> 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<int> GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers);
@ -29,8 +26,8 @@ public interface IContainer
public static ReadOnlyCollection<Item> GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems) =>
Container.GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems);
public static ReadOnlyCollection<Models.Container> GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById) =>
Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById);
public static ReadOnlyCollection<Models.Container> GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById) =>
Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection, exifDirectoriesById);
internal DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection<Item> items) =>
GetContainerDateTimes(items);
@ -38,8 +35,8 @@ public interface IContainer
internal ReadOnlyCollection<Item> 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<int> TestStatic_GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers);
@ -50,10 +47,10 @@ public interface IContainer
internal ReadOnlyCollection<Item> TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems) =>
GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems);
internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string aPropertySingletonDirectory) =>
GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory);
internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers) =>
Container.GetContainers(propertyConfiguration, splatNineIdentifiers);
internal ReadOnlyCollection<Models.Container> TestStatic_GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById) =>
GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById);
internal ReadOnlyCollection<Models.Container> TestStatic_GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById) =>
GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection, exifDirectoriesById);
}

View File

@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Phares.Shared;
using System.Globalization;

View File

@ -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<LocationContainer> GetMatrixLocationContainers(IDlibDotNet dlibDotNet, Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding, List<LocationContainer> preFiltered, DistanceLimits distanceLimits, List<LocationContainer> postFiltered)
public static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding, List<LocationContainer> preFiltered, DistanceLimits distanceLimits, List<LocationContainer> postFiltered, Action? tick)
{
List<LocationContainer> results = [];
ReadOnlyCollection<LocationContainer> locationContainers;
ReadOnlyCollection<LocationContainer> 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<string> files = [];
List<Relation> mappedRelations;
long ticks = DateTime.Now.Ticks;
FaceDistance? faceDistanceEncoding;
List<Relation> mappedRelations;
List<FaceDistance> faceDistanceLengths;
List<FaceDistance> 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();
}

View File

@ -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<int, Item> _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<Item> 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;

View File

@ -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<int, List<MappingFromItem?>> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, Container.Models.Container[] containers, string destinationRoot, List<int> preloadIds)
{
Dictionary<int, List<MappingFromItem?>> results = [];
string? model;
string directory;
const int zero = 0;
DateTime? dateTime;
FileHolder resizedFileHolder;
DateTime[] containerDateTimes;
MappingFromItem? mappingFromItem;
List<MappingFromItem?>? collection;
ReadOnlyCollection<string> keywords;
ReadOnlyCollection<Item> 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);
}
}

View File

@ -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<Shared.Models.Face> GetFaces(string outputResolution, string cResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<MappingFromPhotoPrism>? mappingFromPhotoPrismCollection)
public List<Shared.Models.Face> GetFaces(string cResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, List<MappingFromPhotoPrism>? mappingFromPhotoPrismCollection)
{
List<Shared.Models.Face>? 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<Shared.Models.Face> GetFaces(string outputResolution, string cResultsFullGroupDirectory, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<Location> locations)
private List<Shared.Models.Face> GetFaces(string cResultsFullGroupDirectory, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, List<Location> 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<FacePart, FacePoint[]>? 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);

View File

@ -92,8 +92,8 @@ public class D2_FaceParts
List<FaceFile> 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)
{

View File

@ -29,9 +29,10 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string FilesCollectionRootDirectory,
bool FilesCollectionCountIsOne,
ReadOnlyCollection<ReadOnlyCollection<FilePath>> FilePathsCollection,
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? IdToFilePaths,
ReadOnlyDictionary<int, Identifier>? 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<PersonContainer> 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<Tuple<string, DateTime>> 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<string> keywords = IMetaBase.GetKeywords(exifDirectory.ExifBaseDirectories);
ReadOnlyCollection<string> keywords = IMetaBase.GetKeywords(exifDirectory);
shouldIgnore = _Configuration.PropertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains);
if (shouldIgnore.Value)
{
@ -261,7 +265,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
distance += 1;
}
}
int? orientation = IMetaBase.GetOrientation(exifDirectory);
if (exifDirectory is null || orientation is null || exifDirectory.Width is null || exifDirectory.Height is null)
faces = [];
else
{
Dictionary<string, int[]> 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)
{
if (shouldIgnore is not null && item.FilePath.HasIgnoreKeyword is not null && item.FilePath.HasIgnoreKeyword.Value != shouldIgnore.Value)
@ -279,7 +289,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection))
mappingFromPhotoPrismCollection = null;
bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution);
faces = _Faces.GetFaces(outputResolution, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection);
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))
@ -303,6 +313,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
if (_Configuration.SaveFaceLandmarkForOutputResolutionsV2.Contains(outputResolution))
_FaceParts.SaveFaceLandmarkImages(d2ResultsFullGroupDirectory, mappingFromItem, exifDirectory, faces);
}
}
lock (sourceDirectoryChanges)
{
item.Faces.AddRange(faces);
@ -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<FilePath> HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(Property.Models.Configuration propertyConfiguration, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> keyValuePairs)
private static ReadOnlyCollection<FilePath> HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(Property.Models.Configuration propertyConfiguration, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths)
{
List<FilePath> results = [];
string checkFile;
@ -817,7 +853,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
FilePath? filePath;
string checkDirectory;
List<FilePath> collection = [];
foreach (KeyValuePair<int, ReadOnlyCollection<FilePath>> keyValuePair in keyValuePairs)
foreach (KeyValuePair<int, ReadOnlyCollection<FilePath>> 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<int, Identifier> GetSplatNineIdentifiers(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> keyValuePairs)
private static ReadOnlyDictionary<int, Identifier> GetSplatNineIdentifiers(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths)
{
Dictionary<int, Identifier> 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<int, ReadOnlyCollection<FilePath>> keyValuePair in keyValuePairs)
foreach (KeyValuePair<int, ReadOnlyCollection<FilePath>> 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<int, Identifier> GetSplatNineIdentifiersAndHideSplatNine(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> keyValuePairs)
private static ReadOnlyDictionary<int, Identifier> GetSplatNineIdentifiersAndHideSplatNine(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
{
ReadOnlyDictionary<int, Identifier> results;
if (keyValuePairs.Count == 0)
if (filePathsCollection.Count == 0)
results = new(new Dictionary<int, Identifier>());
else
{
ReadOnlyCollection<FilePath> moved = HideSplatNineAndGetMovedDuplicatesWithSameSplatNine(propertyConfiguration, keyValuePairs);
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection);
ReadOnlyCollection<FilePath> 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<FilePath> GetFilePath(long ticks, string dFacesContentDirectory)
private ReadOnlyCollection<FilePath> GetFilePath(string dFacesContentDirectory)
{
List<FilePath> 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<int, ExifDirectory> results)
private void ParallelFor(FilePair filePair, Dictionary<int, ExifDirectory> 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)
{
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<PersonContainer> personContainers, string argZero, string propertyRoot)
private void Search(ReadOnlyCollection<PersonContainer> 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<long, List<int>> 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<int, ExifDirectory> exifDirectoriesById = [];
(cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution);
string? filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent);
ReadOnlyCollection<ReadOnlyCollection<FilePath>>? filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: true);
ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById = record?.ExifDirectoriesById ?? new(new Dictionary<int, ExifDirectory>());
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<Container.Models.Container> 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<Item> 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<Container.Models.Container> readOnlyContainers)
{
int count = 0;
List<Item> items = [];
Container.Models.Container container;
ReadOnlyCollection<Item> 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<FilePair> filePairs;
Dictionary<int, ExifDirectory> results = [];
int maxDegreeOfParallelism = Environment.ProcessorCount;
Dictionary<int, ExifDirectory> 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<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs =
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs =
Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, [_Configuration.PropertyConfiguration.ResultContent]);
ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups = keyValuePairs[_Configuration.PropertyConfiguration.ResultContent];
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: false);
foreach (KeyValuePair<int, ExifDirectory> 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<int, ExifDirectory> 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<int, ReadOnlyCollection<FilePath>> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection);
ReadOnlyDictionary<int, Identifier> 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<int, Identifier> 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<PersonContainer> personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory)
private void MapFaceFileLogic(ReadOnlyCollection<PersonContainer> 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<SaveContainer> saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, mapLogic, outputResolution);
List<SaveContainer> 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<SaveContainer> GetSaveContainers(long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, MapLogic mapLogic, string outputResolution)
private List<SaveContainer> GetSaveContainers(ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, MapLogic mapLogic, string outputResolution)
{
List<SaveContainer> 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<int, ReadOnlyDictionary<int, LocationContainer>> mapped = Map.Models.Stateless.Methods.IMapLogic.GetMapped(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> 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<FilePath> filePaths = GetFilePath(ticks, dFacesContentDirectory);
List<LocationContainer> available = Map.Models.Stateless.Methods.IMapLogic.GetAvailable(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Faces, ticks, filePaths);
ReadOnlyCollection<FilePath> filePaths = GetFilePath(dFacesContentDirectory);
List<LocationContainer> 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<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding = E_Distance.GetMappedWithEncoding(mapped);
if (mappedWithEncoding.Count == 0 && !_Configuration.SaveSortingWithoutPerson)
throw new NotSupportedException($"Switch {nameof(_Configuration.SaveSortingWithoutPerson)}!");
List<LocationContainer> preFiltered = E_Distance.GetPreFilterLocationContainer(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, _Configuration.FocusDirectory, _Configuration.FocusModel, _Configuration.SkipPersonWithMoreThen, ticks, mapLogic, jLinkResolvedPersonKeys, mapped, available);
List<LocationContainer> 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<LocationContainer> 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<LocationContainer> matrix = E_Distance.GetMatrixLocationContainers(_MapConfiguration, dlibDotNet.Ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered, dlibDotNet.Tick);
ReadOnlyDictionary<string, LocationContainer> 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<Container.Models.Container> containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary<long, List<int>> personKeyToIds, ReadOnlyCollection<Shared.Models.Face> distinctValidImageFaces, ReadOnlyCollection<Mapping> distinctValidImageMappingCollection)
private void MapLogic(ReadOnlyCollection<Container.Models.Container> containers, string fPhotoPrismContentDirectory, MapLogic mapLogic, string outputResolution, ReadOnlyDictionary<long, List<int>> personKeyToIds, ReadOnlyCollection<Shared.Models.Face> distinctValidImageFaces, ReadOnlyCollection<Mapping> 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<int, ReadOnlyDictionary<int, Mapping>> 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<Shared.Models.Face> distinctValidImageFaces, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> idToWholePercentagesToMapping)
private void SaveFaceDistances(MapLogic mapLogic, ReadOnlyCollection<Shared.Models.Face> distinctValidImageFaces, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> idToWholePercentagesToMapping)
{
E_Distance.PreFilterSetFaceDistances(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, ticks, distinctValidImageFaces);
IDlibDotNet dlibDotNet = this;
E_Distance.PreFilterSetFaceDistances(_AppSettings.MaxDegreeOfParallelism, _MapConfiguration, dlibDotNet.Ticks, distinctValidImageFaces);
ReadOnlyCollection<FaceDistanceContainer> 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<int, ReadOnlyDictionary<int, Mapping>> idToWholePercentagesToMapping, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, ReadOnlyCollection<FaceDistanceContainer> faceDistanceContainers)
private void SaveFaceDistances(MapLogic mapLogic, string dFacesContentDirectory, string d2FacePartsContentDirectory, string d2FacePartsContentCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> idToWholePercentagesToMapping, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, ReadOnlyCollection<FaceDistanceContainer> faceDistanceContainers)
{
int? useFiltersCounter = null;
IDlibDotNet dlibDotNet = this;
DistanceLimits distanceLimits;
ReadOnlyCollection<SortingContainer> 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<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.ExifDirectory?.FilePath.Id);
ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? 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;

View File

@ -82,8 +82,8 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
List<string> personKeyFormattedCollection = [];
Dictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted = [];
Stateless.MapLogic.SetPersonCollectionsAfterSetSkipCollections(configuration, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection);
readOnlyPersonKeyFormattedCollection = new(personKeyFormattedCollection);
readOnlyPersonKeyFormattedToNewestPersonKeyFormatted = new(personKeyFormattedToNewestPersonKeyFormatted);
readOnlyPersonKeyFormattedCollection = personKeyFormattedCollection.AsReadOnly();
readOnlyPersonKeyFormattedToNewestPersonKeyFormatted = personKeyFormattedToNewestPersonKeyFormatted.AsReadOnly();
}
List<Stateless.Record> 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<LocationContainer> 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<Container.Models.Container> readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory)
public void LookForAbandoned(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection<Container.Models.Container> readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, Action? tick)
{
string[] directories;
string? directoryName;
List<int> distinctFilteredIds = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers);
LookForAbandoned(propertyConfiguration, distinctFilteredIds);
dlibDotNet.Tick();
tick?.Invoke();
List<string> 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<int> distinctFilteredIds)

View File

@ -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<Face> 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<int, ReadOnlyDictionary<int, Mapping>> results = [];
foreach (KeyValuePair<int, Dictionary<int, Mapping>> 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<LocationContainer> locationContainers = new(results.OrderBy(l => l.DirectoryNumber).ToArray());
ReadOnlyCollection<LocationContainer> locationContainers = results.OrderBy(l => l.DirectoryNumber).ToArray().AsReadOnly();
LookForPossibleDuplicates(configuration, locationContainers);
}
return results;

View File

@ -14,10 +14,7 @@ namespace View_by_Distance.Metadata.Models;
public class B_Metadata : IMetadata<MetadataExtractor.Directory>
{
public string DateGroupDirectory { get; init; }
public ReadOnlyCollection<FilePath> Collection { get; private set; }
public ReadOnlyDictionary<int, List<FilePath>> SingletonById { get; private set; }
public ReadOnlyDictionary<int, ExifDirectory> ExifDirectoriesById { get; private set; }
private readonly Dictionary<int, ExifDirectory> _ExifDirectoriesById;
// First
// Set DateGroupDirectory
@ -50,52 +47,49 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
else
throw new Exception();
}
List<ExifDirectory> results = [];
string jsonGroupDirectory;
const string extension = ".json";
const string fileSearchFilter = "*";
string filesCollectionRootDirectory;
const string directorySearchFilter = "*";
int maxDegreeOfParallelism = Environment.ProcessorCount;
filesCollectionRootDirectory = propertyConfiguration.RootDirectory;
Dictionary<int, ExifDirectory> 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<string> directories = new([jsonGroupSingletonDirectory, jsonGroupCollectionDirectory]);
Shared.Models.Stateless.Methods.IPath.CreateDirectories(directories);
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: false);
ReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles = FilePath.GetFilesKeyValuePairs(filePathsCollection);
ReadOnlyCollection<FilePair> 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<FilePair> 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<ReadOnlyCollection<FilePath>> 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<ExifDirectory> results)
public ReadOnlyDictionary<int, ExifDirectory> GetKeyValuePairsAndClear()
{
dlibDotNet?.Tick();
Dictionary<int, ExifDirectory> results = [];
foreach (KeyValuePair<int, ExifDirectory> keyValuePair in _ExifDirectoriesById)
results.Add(keyValuePair.Key, keyValuePair.Value);
_ExifDirectoriesById.Clear();
return results.AsReadOnly();
}
private void ParallelFor(FilePair filePair, Dictionary<int, ExifDirectory> 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<MetadataExtractor.Directory>
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<MetadataExtractor.Directory>
DateTime? result = null;
DateTime? dateTime;
DateTime checkDateTime;
string dateTimeFormat = Stateless.Methods.IMetadata.DateTimeFormat();
string dateTimeFormat = IMetaBase.DateTimeFormat();
MetadataExtractor.Formats.Exif.ExifDirectoryBase? exifDirectoryBase = directories.OfType<MetadataExtractor.Formats.Exif.ExifDirectoryBase>().FirstOrDefault();
results.Add(new DateTime(filePath.CreationTicks));
results.Add(new DateTime(filePath.LastWriteTicks));
@ -213,7 +220,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
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<MetadataExtractor.Directory>
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<MetadataExtractor.Directory>
}
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<MetadataExtractor.Directory>
}
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<MetadataExtractor.Directory>
}
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<MetadataExtractor.Directory>
}
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;

View File

@ -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
}

View File

@ -8,18 +8,18 @@ internal static class Dimensions
#pragma warning disable IDE0230
private static readonly Dictionary<byte[], Func<BinaryReader, Size?>> _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<byte> 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<byte> 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();
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<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders)
{
if (StartsWith(magicBytes, kvPair.Key))
return kvPair.Value(binaryReader);
{
result = kvPair.Value(binaryReader);
break;
}
}
return null;
if (result is not null)
break;
}
}
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;
}
}

View File

@ -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;
}

View File

@ -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<string, MetadataExtractorDirectory> GetMetadataCollection(FileInfo fileInfo, List<Tuple<string, DateTime>> subFileTuples, List<string> 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);
}

View File

@ -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<MetadataExtractor.Directory> metadata, int t, Container.Models.Container[] containers)
{
int total = 0;
string message;
int totalSeconds;
bool anyNullOrNoIsUniqueFileName;
List<Exception> exceptions = [];
Container.Models.Container container;
int containersLength = containers.Length;
const string outputResolution = "Original";
List<Tuple<string, DateTime>> 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<MetadataExtractor.Directory> metadata, List<Exception> exceptions, List<Tuple<string, DateTime>> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection<Item> items, string message)
{
List<Tuple<string, DateTime>> 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<Tuple<string, DateTime>> 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<MetadataExtractor.Directory> metadata, string sourceDirectory, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<Tuple<string, DateTime>> sourceDirectoryChanges, Item item)
{
Shared.Models.Property property;
List<string> 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<string, DateTime>(nameof(A_Property), DateTime.Now));
lock (item)
item.Update(null);
}
}
[Obsolete("Use ExifDirectory")]
private Shared.Models.Property GetImageProperty(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, Item item, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<string> 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<DateTime> 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<string, DateTime>(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<string, DateTime>(nameof(A_Property), DateTime.Now));
else
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
sourceDirectoryFileTuples.Add(new Tuple<string, DateTime>(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<string, DateTime>(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<MetadataExtractor.Directory> metadata, Item item, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<string> 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;
}
}

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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;
}

View File

@ -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<FilePath> Collection,
ReadOnlyCollection<FilePath> Collection,
FilePath? Match)
{

View File

@ -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<int, List<FilePath>> GetFilesKeyValuePairs(ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
{
Dictionary<int, List<FilePath>> results = [];
List<FilePath>? collection;
foreach (ReadOnlyCollection<FilePath> 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<int, ReadOnlyCollection<FilePath>> GetKeyValuePairs(ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
{
Dictionary<int, ReadOnlyCollection<FilePath>> results = [];
@ -123,7 +115,7 @@ public record FilePath(long CreationTicks,
}
}
foreach (KeyValuePair<int, List<FilePath>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return results.AsReadOnly();
}

View File

@ -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<DateTime> dateTimes = Stateless.Methods.IDate.GetDateTimes(exifDirectory);
ReadOnlyCollection<string> keywords = Stateless.Methods.IMetaBase.GetKeywords(exifDirectory);
(bool? isWrongYear, _) = Stateless.Methods.IProperty.IsWrongYear(item.FilePath, dateTime, dateTimes.ToList());
ReadOnlyCollection<string> 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;
}

View File

@ -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<byte[], Func<BinaryReader, Size?>> _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<byte> 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<byte> 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<byte[], Func<BinaryReader, Size?>> 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;
}
}

View File

@ -7,29 +7,22 @@ internal abstract class FilePair
{
internal static ReadOnlyCollection<Models.FilePair> GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
{
ReadOnlyCollection<Models.FilePair> results;
ReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles = FilePath.GetFilesKeyValuePairs(filePathsCollection);
results = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles);
return results;
}
internal static ReadOnlyCollection<Models.FilePair> GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles)
{
List<Models.FilePair>? results = null;
int renamed;
const bool useCeilingAverage = true;
ReadOnlyCollection<string[]>? jsonFilesCollection = null;
IReadOnlyDictionary<int, List<FilePath>>? compareFileNamesToFiles = null;
for (int i = 0; i < fileNamesToFiles.Count; i++)
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection);
IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? 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<string[]> jsonFilesCollection, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, string extension)
private static int LookForAbandoned(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> jsonFilesCollection, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> fileNamesToFiles, string extension)
{
int result;
bool check;
@ -63,7 +56,7 @@ internal abstract class FilePair
return result;
}
private static bool AnyMoved(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, string extension, List<string> renameCollection, string[] files)
private static bool AnyMoved(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> fileNamesToFiles, string extension, List<string> renameCollection, string[] files)
{
bool result = false;
string checkFile;
@ -72,11 +65,13 @@ internal abstract class FilePair
string fileNameWith;
string checkDirectory;
string directoryName;
List<FilePath>? collection;
Models.FileHolder fileHolder;
List<string> directoryNames = [];
ReadOnlyCollection<FilePath>? 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<int, List<FilePath>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection)
private static ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection)
{
Dictionary<int, List<FilePath>> results = [];
List<FilePath>? collection;
Dictionary<int, ReadOnlyCollection<FilePath>> results = [];
FilePath filePath;
List<FilePath>? collection;
Models.FileHolder fileHolder;
Dictionary<int, List<FilePath>> 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<int, List<FilePath>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return new(results);
}

View File

@ -29,13 +29,13 @@ public interface IDirectory
public static ReadOnlyCollection<ReadOnlyCollection<FilePath>> 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<Models.FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, List<FilePath>> compareFileNamesToFiles) =>
public static List<Models.FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> compareFileNamesToFiles) =>
XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles);
public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, Action? tick) =>
XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, null, tick);
public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, Dictionary<int, ExifDirectory> exifDirectoriesById, Action? tick) =>
public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, ReadOnlyDictionary<int, ExifDirectory> 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<ReadOnlyCollection<FilePath>> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useIgnoreExtensions, bool useCeilingAverage) =>
GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useIgnoreExtensions, useCeilingAverage);
internal List<Models.FilePair> TestStatic_GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, List<FilePath>> compareFileNamesToFiles) =>
internal List<Models.FilePair> TestStatic_GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> compareFileNamesToFiles) =>
GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles);
internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, Action? tick) =>
GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, tick);
internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, Dictionary<int, ExifDirectory> exifDirectoriesById, Action? tick) =>
internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById, Action? tick) =>
GetToDoCollection(propertyConfiguration, filePathsCollection, fileGroups, exifDirectoriesById, tick);
}

View File

@ -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);

View File

@ -9,13 +9,7 @@ public interface IFilePair
public static ReadOnlyCollection<Models.FilePair> GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection) =>
FilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection);
public static ReadOnlyCollection<Models.FilePair> GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles) =>
FilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles);
internal ReadOnlyCollection<Models.FilePair> TestStatic_GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection) =>
GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection);
internal ReadOnlyCollection<Models.FilePair> TestStatic_GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles) =>
GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles);
}

View File

@ -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<string> GetKeywords(ExifDirectory? exifDirectory) =>
MetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories);
public static ReadOnlyCollection<string> 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);

View File

@ -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)
{

View File

@ -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.");
}
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
internal static Size GetDimensions(BinaryReader binaryReader, int? faceRight, int? faceBottom)
{
Size? result = null;
#pragma warning disable IDE0230
Dictionary<byte[], Func<BinaryReader, Size>> _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<byte[], Func<BinaryReader, Size>> 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;
}
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
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;
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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<Models.FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, List<FilePath>> compareFileNamesToFiles)
internal static List<Models.FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> compareFileNamesToFiles)
{
List<Models.FilePair> results = [];
FilePath? match;
bool uniqueFileName;
List<FilePath>? collection;
Models.FilePair filePair;
bool? isNotUniqueAndNeedsReview;
ReadOnlyCollection<FilePath>? collection;
foreach (ReadOnlyCollection<FilePath> 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<FilePath> collection)
private static bool GetIsNotUniqueAndNeedsReview(FilePath filePath, ReadOnlyCollection<FilePath> collection)
{
bool result = false;
long max;
@ -280,7 +280,7 @@ internal abstract partial class XDirectory
return result;
}
private static FilePath? GetMatch(FilePath filePath, List<FilePath> collection)
private static FilePath? GetMatch(FilePath filePath, ReadOnlyCollection<FilePath> collection)
{
FilePath? result = null;
List<long> 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<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, Dictionary<int, ExifDirectory>? exifDirectoriesById, Action? tick)
internal static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, Action? tick)
{
List<(FilePath, string)> results = [];
string paddedId;

View File

@ -446,7 +446,7 @@ internal abstract class XPath
{
Dictionary<byte, ReadOnlyCollection<string>> results = [];
foreach (KeyValuePair<byte, List<string>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return results.AsReadOnly();
}

View File

@ -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}"