Switch to ExifDirectory from Property

This commit is contained in:
Mike Phares 2025-04-06 18:23:57 -07:00
parent 3f7affceef
commit c7ded16e50
50 changed files with 2647 additions and 1846 deletions

3
.gitignore vendored
View File

@ -470,4 +470,7 @@ globalStorage/
Shared/.kanbn
.Immich/immich-assets.json
Tests/.vscode/.UserSecrets/*
Instance/.vscode/.UserSecrets/*
Drag-Drop-Set-Property-Item/.vscode/.UserSecrets/*
TestsWithFaceRecognitionDotNet/.vscode/.UserSecrets/*

11
.vscode/mklink.md vendored
View File

@ -18,3 +18,14 @@ mklink /J "L:\Git\View-by-Distance-MKLink-Console\.Immich" "D:\1-Images-A\Images
mklink /J "V:\Tmp\Phares\Pictures-Results" "V:\6-Other-Large-Z\Current-Results-Test"
mklink /J "V:\1-Images-A\Images-0b793904-Results" "V:\6-Other-Large-Z\Current-Results"
```
```bash 1742827407172 = 638784242071720000 = 2025-1.Spring = Mon Mar 24 2025 07:43:26 GMT-0700 (Mountain Standard Time)
mkdir "L:\Git\View-by-Distance-MKLink-Console\Instance\.vscode"
mklink /J "L:\Git\View-by-Distance-MKLink-Console\Instance\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\2999dda1-5329-4d9f-9d68-cccfabe0e47f"
mkdir "L:\Git\View-by-Distance-MKLink-Console\Tests\.vscode"
mklink /J "L:\Git\View-by-Distance-MKLink-Console\Tests\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\e8c3d25d-9715-4b35-9010-1cdc74840190"
mkdir "L:\Git\View-by-Distance-MKLink-Console\TestsWithFaceRecognitionDotNet\.vscode"
mklink /J "L:\Git\View-by-Distance-MKLink-Console\TestsWithFaceRecognitionDotNet\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\ecbdc76d-6037-4046-86a4-1a7626a3d342"
mkdir "L:\Git\View-by-Distance-MKLink-Console\Drag-Drop-Set-Property-Item\.vscode"
mklink /J "L:\Git\View-by-Distance-MKLink-Console\Drag-Drop-Set-Property-Item\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\c64a15ed-0ba3-4378-8f80-0c19d0531747"
```

48
.vscode/tasks.json vendored
View File

@ -100,6 +100,54 @@
],
"problemMatcher": "$msCompile"
},
{
"label": "buildTests",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Tests/Tests.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "buildTestsWithFaceRecognitionDotNet",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "buildCopyDistinct",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Copy-Distinct/Copy-Distinct.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "buildDragDropSetPropertyItem",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Drag-Drop-Set-Property-Item/Drag-Drop-Set-Property-Item.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "File-Folder-Helper AOT s X Day-Helper-2025-03-20",
"type": "shell",

View File

@ -47,5 +47,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" />
</ItemGroup>
</Project>

View File

@ -1,5 +1,4 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless.Methods;
@ -10,7 +9,7 @@ internal abstract class Container
{
private record FilePair(bool IsUnique,
List<string> Collection,
List<FilePath> Collection,
FilePath FilePath,
Item Item);
@ -48,11 +47,11 @@ internal abstract class Container
continue;
foreach (Item item in filteredItems)
{
if (item.Property?.Id is null || item.ResizedFileHolder is null)
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
continue;
if (results.Contains(item.Property.Id.Value))
if (results.Contains(item.ExifDirectory.FilePath.Id.Value))
continue;
results.Add(item.Property.Id.Value);
results.Add(item.ExifDirectory.FilePath.Id.Value);
}
}
return results;
@ -62,21 +61,23 @@ internal abstract class Container
{
int count;
Models.Container[] results;
bool useIgnoreExtensions = true;
const bool useCeilingAverage = true;
const string fileSearchFilter = "*";
IDlibDotNet dlibDotNet = GetDlibDotNet();
const string directorySearchFilter = "*";
ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById = null;
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs = null;
ReadOnlyCollection<string[]> filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection);
(count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter);
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
(count, results) = GetContainers(dlibDotNet, propertyConfiguration, propertyConfiguration.RootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter);
return (count, results);
}
private static IDlibDotNet GetDlibDotNet() =>
throw new NotImplementedException();
private static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, string directorySearchFilter)
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)
{
List<Models.Container> results = [];
string directory;
@ -104,7 +105,7 @@ internal abstract class Container
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, propertyConfiguration.ResultSingleton);
if (!Directory.Exists(aPropertySingletonDirectory))
_ = Directory.CreateDirectory(aPropertySingletonDirectory);
List<FilePair> filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter);
List<FilePair> filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter);
foreach (FilePair filePair in filePairs)
{
if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items))
@ -125,463 +126,112 @@ internal abstract class Container
return (filePairs.Count, results.ToArray());
}
internal static ReadOnlyCollection<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)
{
Models.Container[] results;
const string directorySearchFilter = "*";
(_, results) = GetContainers(dlibDotNet, propertyConfiguration, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, directorySearchFilter);
if (keyValuePairs is not null)
DoGetFilePairsForRemaining(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filePathsCollection, directorySearchFilter);
return results.AsReadOnly();
}
private static void DoGetFilePairsForRemaining(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, string directorySearchFilter)
{
const string extension = ".json";
(_, string bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories();
IReadOnlyDictionary<string, List<string>> fileNamesToFiles = GetFilesKeyValuePairs(filePathsCollection);
string bMetaSingletonDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultSingleton);
if (!Directory.Exists(bMetaSingletonDirectory))
_ = Directory.CreateDirectory(bMetaSingletonDirectory);
_ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, bMetaSingletonDirectory, filePathsCollection, fileNamesToFiles);
(string cResultsFullGroupDirectory, _, string dResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories("Original");
string cResizeSingletonDirectory = Path.Combine(cResultsFullGroupDirectory, propertyConfiguration.ResultSingleton);
if (!Directory.Exists(cResizeSingletonDirectory))
_ = Directory.CreateDirectory(cResizeSingletonDirectory);
_ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, cResizeSingletonDirectory, filePathsCollection, fileNamesToFiles);
string dFaceCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultCollection);
if (!Directory.Exists(dFaceCollectionDirectory))
_ = Directory.CreateDirectory(dFaceCollectionDirectory);
_ = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, dFaceCollectionDirectory, filePathsCollection, fileNamesToFiles);
string dFaceContentDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultContent);
if (!Directory.Exists(dFaceContentDirectory))
_ = Directory.CreateDirectory(dFaceContentDirectory);
AnyMovedFace(facesFileNameExtension, facesHiddenFileNameExtension, fileNamesToFiles, dFaceContentDirectory);
AnyMovedDistance(facesFileNameExtension, fileNamesToFiles, eDistanceContentDirectory);
}
private static IReadOnlyDictionary<string, List<string>> GetFilesKeyValuePairs(ReadOnlyCollection<string[]> filesCollection)
{
Dictionary<string, List<string>> results = [];
string fileNameWithoutExtension;
string fileNameWithoutExtensionSecond;
string fileNameWithoutExtensionSecondMinusOne;
List<string>? collection;
foreach (string[] files in filesCollection)
{
foreach (string file in files)
{
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(fileNameWithoutExtension);
if (string.IsNullOrEmpty(fileNameWithoutExtensionSecond) || fileNameWithoutExtensionSecond == fileNameWithoutExtension)
continue;
fileNameWithoutExtensionSecondMinusOne = fileNameWithoutExtensionSecond[..^1];
if (!results.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection))
{
results.Add(fileNameWithoutExtensionSecondMinusOne, []);
if (!results.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection))
throw new Exception();
}
collection.Add(file);
}
}
return results;
}
private static IReadOnlyDictionary<string, List<string>> GetFilesKeyValuePairs(ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
{
Dictionary<string, List<string>> results = [];
List<string>? collection;
string fileNameWithoutExtensionMinusOne;
foreach (ReadOnlyCollection<FilePath> filePaths in filePathsCollection)
{
foreach (FilePath filePath in filePaths)
{
fileNameWithoutExtensionMinusOne = filePath.NameWithoutExtension[..^1];
if (!results.TryGetValue(fileNameWithoutExtensionMinusOne, out collection))
{
results.Add(fileNameWithoutExtensionMinusOne, []);
if (!results.TryGetValue(fileNameWithoutExtensionMinusOne, out collection))
throw new Exception();
}
collection.Add(filePath.FullName);
}
}
return results;
}
private static List<Shared.Models.FilePair> GetFilePairs(IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string jsonGroupDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, List<string>> fileNamesToFiles)
{
List<Shared.Models.FilePair>? results = null;
int renamed;
const bool useCeilingAverage = true;
ReadOnlyCollection<string[]>? jsonFilesCollection = null;
IReadOnlyDictionary<string, List<string>>? compareFileNamesToFiles = null;
for (int i = 0; i < short.MaxValue; i++)
{
renamed = 0;
jsonFilesCollection = IDirectory.GetFilesCollection(jsonGroupDirectory, directorySearchFilter, extension, useCeilingAverage);
compareFileNamesToFiles = GetFilesKeyValuePairs(jsonFilesCollection);
renamed += LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension);
results = IDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles);
renamed += IDirectory.MaybeMove(propertyConfiguration, results, jsonGroupDirectory, extension);
if (renamed == 0)
break;
if (i > 10)
throw new NotImplementedException();
}
if (results is null || jsonFilesCollection is null || compareFileNamesToFiles is null)
throw new NullReferenceException(nameof(results));
return results;
}
private static int LookForAbandoned(ReadOnlyCollection<string[]> jsonFilesCollection, IReadOnlyDictionary<string, List<string>> fileNamesToFiles, string extension)
{
int result;
bool check;
bool moved = false;
List<string> renameCollection = [];
foreach (string[] files in jsonFilesCollection)
{
if (files.Length == 0)
continue;
check = AnyMoved(fileNamesToFiles, extension, renameCollection, files);
if (!moved && check)
moved = true;
}
if (renameCollection.Count > 0)
IDirectory.MoveFiles(renameCollection, "{}", "{abd}");
result = renameCollection.Count;
if (moved && result == 0)
result = 1;
return result;
}
private static bool AnyMoved(IReadOnlyDictionary<string, List<string>> fileNamesToFiles, string extension, List<string> renameCollection, string[] files)
{
bool result = false;
string checkFile;
string directory;
string fileNameWith;
string checkDirectory;
string directoryName;
List<string>? collection;
string fileNameWithoutExtension;
List<string> directoryNames = [];
string fileNameWithoutExtensionSecond;
string fileNameWithoutExtensionSecondMinusOne;
foreach (string file in files)
{
if (!file.EndsWith(extension))
throw new Exception();
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(fileNameWithoutExtension);
if (string.IsNullOrEmpty(fileNameWithoutExtensionSecond) || fileNameWithoutExtensionSecond == fileNameWithoutExtension)
continue;
fileNameWithoutExtensionSecondMinusOne = fileNameWithoutExtensionSecond[..^1];
if (!fileNamesToFiles.TryGetValue(fileNameWithoutExtensionSecondMinusOne, out collection))
renameCollection.Add(file);
else
{
directoryNames.Clear();
directoryName = Path.GetFileName(Path.GetDirectoryName(file)) ?? throw new Exception();
foreach (string f in collection)
directoryNames.Add(Path.GetFileName(Path.GetDirectoryName(f)) ?? throw new Exception());
if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1)
continue;
if (directoryName != directoryNames[0])
{
directory = Path.GetDirectoryName(Path.GetDirectoryName(file)) ?? throw new Exception();
checkDirectory = Path.Combine(directory, directoryNames[0]);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
fileNameWith = collection.Count > 1 ? Path.GetFileName(file) : $"{Path.GetFileName(collection[0])}{extension}";
checkFile = Path.Combine(checkDirectory, fileNameWith);
if (!result)
result = true;
if (!File.Exists(checkFile))
File.Move(file, checkFile);
else
{
if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime)
File.Delete(file);
else
File.Move(file, checkFile, true);
}
}
}
}
return result;
}
private static void AnyMovedFace(string extension, string hiddenExtension, IReadOnlyDictionary<string, List<string>> fileNamesToFiles, string jsonGroupDirectory)
{
string directory;
string checkFile;
string directoryName;
List<string> files = [];
string[] fileNameSegments;
List<string> directories = [];
files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories));
files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{hiddenExtension}", SearchOption.AllDirectories));
foreach (string file in files)
{
directory = Path.GetDirectoryName(file) ?? throw new Exception();
if (!directories.Contains(directory))
directories.Add(directory);
directoryName = Path.GetFileName(directory);
fileNameSegments = Path.GetFileName(file).Split('.');
if (fileNameSegments[0] != directoryName)
{
fileNameSegments[0] = string.Empty;
checkFile = Path.Combine(directory, $"{directoryName}{string.Join('.', fileNameSegments)}");
if (!File.Exists(checkFile))
File.Move(file, checkFile);
else
{
if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime)
File.Delete(file);
else
File.Move(file, checkFile, true);
}
}
}
if (directories.Count > 0)
AnyMovedFace(fileNamesToFiles, directories);
}
private static void AnyMovedFace(IReadOnlyDictionary<string, List<string>> fileNamesToFiles, List<string> directories)
{
bool result = false;
string checkFile;
string subDirectory;
string directoryName;
string checkDirectory;
List<string>? collection;
string directoryNameWith;
string directoryNameMinusOne;
List<string> directoryNames = [];
foreach (string directory in directories)
{
directoryNameMinusOne = Path.GetFileName(directory)[..^1];
if (!fileNamesToFiles.TryGetValue(directoryNameMinusOne, out collection))
throw new Exception();
directoryNames.Clear();
foreach (string f in collection)
directoryNames.Add(Path.GetFileName(Path.GetDirectoryName(f)) ?? throw new Exception());
if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1)
continue;
directoryName = Path.GetFileName(Path.GetDirectoryName(directory)) ?? throw new Exception();
if (directoryName != directoryNames[0])
{
subDirectory = Path.GetDirectoryName(Path.GetDirectoryName(directory)) ?? throw new Exception();
checkDirectory = Path.Combine(subDirectory, directoryNames[0]);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
directoryNameWith = collection.Count > 1 ? directoryName : $"{Path.GetFileNameWithoutExtension(collection[0])}";
checkFile = Path.Combine(checkDirectory, directoryNameWith);
if (!result)
result = true;
if (!Directory.Exists(checkFile))
Directory.Move(directory, checkFile);
else
{
if (new DirectoryInfo(directory).LastWriteTime > new DirectoryInfo(checkFile).LastWriteTime)
Directory.Delete(directory, recursive: true);
else
{
Directory.Delete(checkFile, recursive: true);
Directory.Move(directory, checkFile);
}
}
}
}
}
private static void AnyMovedDistance(string extension, IReadOnlyDictionary<string, List<string>> fileNamesToFiles, string jsonGroupDirectory)
{
bool result = false;
string fileName;
string checkFile;
string directory;
List<string>? collection;
string[] fileNameSegments;
List<string> fileNames = [];
string fileNameSegmentsZeroMinusOne;
string[] files = Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories);
foreach (string file in files)
{
fileName = Path.GetFileName(file);
fileNameSegments = fileName.Split('.');
fileNameSegmentsZeroMinusOne = fileNameSegments[0][..^1];
if (!fileNamesToFiles.TryGetValue(fileNameSegmentsZeroMinusOne, out collection))
continue;
fileNames.Clear();
foreach (string f in collection)
fileNames.Add(Path.GetFileNameWithoutExtension(f) ?? throw new Exception());
if (fileNames.Count == 0 || fileNames.Distinct().Count() != 1)
continue;
if (fileNameSegments[0] != fileNames[0])
{
fileNameSegments[0] = string.Empty;
directory = Path.GetDirectoryName(file) ?? throw new Exception();
checkFile = Path.Combine(directory, $"{fileNames[0]}{string.Join('.', fileNameSegments)}");
if (!result)
result = true;
if (!File.Exists(checkFile))
File.Move(file, checkFile);
else
{
if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime)
File.Delete(file);
else
File.Move(file, checkFile, true);
}
}
}
}
private static List<FilePair> GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, string directorySearchFilter)
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)
{
List<FilePair> results = [];
const string extension = ".json";
List<Shared.Models.FilePair> filePairs;
ReadOnlyCollection<Shared.Models.FilePair> filePairs;
string jsonGroupDirectory = aPropertySingletonDirectory;
int maxDegreeOfParallelism = Environment.ProcessorCount;
int filesCollectionDirectoryLength = filesCollectionDirectory.Length;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
IReadOnlyDictionary<string, List<string>> fileNamesToFiles = GetFilesKeyValuePairs(filePathsCollection);
filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection, fileNamesToFiles);
filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection);
_ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) =>
ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, filePairs[i], results));
ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, exifDirectoriesById, filePairs[i], results));
return results;
}
private static void ParallelFor(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, int rootDirectoryLength, Shared.Models.FilePair filePair, List<FilePair> 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)
{
dlibDotNet?.Tick();
bool abandoned = false;
FileHolder sourceDirectoryFileHolder;
Property? property = GetProperty(filePair);
FileHolder fileHolder = IFileHolder.Get(filePair.Path);
ExifDirectory? exifDirectory = GetExifDirectory(filePair);
FilePath filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
bool? fileSizeChanged = property is not null ? property.FileSize != filePath.Length : null;
bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered);
bool? shouldIgnore = property is null || property.Keywords is null ? null : propertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l));
bool? isArchive = filePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePath.Id.Value, out Identifier? identifier);
if (property is not null && filePath.Id is not null && filePath.HasIgnoreKeyword is not null && filePath.HasDateTimeOriginal is not null)
if (exifDirectoriesById is null || filePair.FilePath.Id is null || !exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out ExifDirectory? exifDirectory))
exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath);
ReadOnlyCollection<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);
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;
char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePath).ToString()[0];
char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePath).ToString()[0];
char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePath).ToString()[0];
DateTime? dateTime = IDate.GetDateTimeOriginal(exifDirectory);
char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePair.FilePath).ToString()[0];
char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0];
char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0];
if (shouldIgnore is not null && shouldIgnore.Value)
{
if (filePath.NameWithoutExtension[^1] == hasIgnoreKeyword)
if (filePair.FilePath.FileNameFirstSegment[^1] == hasIgnoreKeyword)
change = null;
else
{
change = hasIgnoreKeyword;
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>");
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>");
}
}
else if ((shouldIgnore is null || !shouldIgnore.Value) && property.DateTimeOriginal is null)
else if ((shouldIgnore is null || !shouldIgnore.Value) && dateTime is null)
{
if (filePath.NameWithoutExtension[^1] == missingDateTimeOriginal)
if (filePair.FilePath.FileNameFirstSegment[^1] == missingDateTimeOriginal)
change = null;
else
{
change = missingDateTimeOriginal;
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>");
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>");
}
}
else if (filePath.NameWithoutExtension[^1] != hasDateTimeOriginal)
else if (filePair.FilePath.FileNameFirstSegment[^1] != hasDateTimeOriginal)
{
change = hasDateTimeOriginal;
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>");
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>");
}
else
change = null;
if (filePaths is not null && change is not null)
RenameFile(extension, filePair, filePath, change.Value, filePaths);
RenameFile(filePair, filePair.FilePath, change.Value, filePaths);
}
string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.Path, rootDirectoryLength, forceExtensionToLower: true);
bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != filePath.LastWriteTicks : null;
string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.FilePath.FullName, rootDirectoryLength, forceExtensionToLower: true);
bool? lastWriteTimeChanged = exifDirectory is not null ? propertyConfiguration.PropertiesChangedForProperty || exifDirectory.FilePath.LastWriteTicks != filePair.FilePath.LastWriteTicks : null;
if (filePair.Match is not null)
sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match);
else if (!filePair.IsUnique)
sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(jsonGroupDirectory, relativePath, extension)));
else
{
string fileName = Path.GetFileName(filePair.Path);
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath);
string fileName = Path.GetFileName(filePair.FilePath.FullName);
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePair.FilePath);
string directory = Path.Combine(jsonGroupDirectory, cei.Combined);
string jsonFileName = $"{fileName}{extension}";
string fullFileName = Path.Combine(directory, jsonFileName);
MoveIf(jsonFileName, cei, directory, fullFileName);
sourceDirectoryFileHolder = IFileHolder.Get(fullFileName);
}
if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks)
if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePair.FilePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks)
{
File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePath.LastWriteTicks));
File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePair.FilePath.LastWriteTicks));
File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value);
}
Item item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged);
Item item = Item.Get(filePair.FilePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, exifDirectory, abandoned, fileSizeChanged, lastWriteTimeChanged);
lock (results)
results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item));
results.Add(new(filePair.IsUnique, filePair.Collection, filePair.FilePath, item));
}
private static Property? GetProperty(Shared.Models.FilePair filePair)
{
Property? result;
if (filePair.Match is null)
result = null;
else
{
string json = File.ReadAllText(filePair.Match);
if (string.IsNullOrEmpty(json))
result = null;
else
result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property);
}
return result;
}
private static ExifDirectory? GetExifDirectory(Shared.Models.FilePair filePair)
{
ExifDirectory? result;
if (filePair.Match is null)
result = null;
else
{
string json = File.ReadAllText(filePair.Match);
if (string.IsNullOrEmpty(json))
result = null;
else
result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
}
return result;
}
private static void RenameFile(string extension, Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection<FilePath> filePaths)
private static void RenameFile(Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection<FilePath> filePaths)
{
string checkFile;
if (filePath.DirectoryFullPath.Contains("Results") && filePath.DirectoryFullPath.Contains("Resize"))
File.Delete(filePath.FullName);
if (!string.IsNullOrEmpty(filePair.Match))
if (filePair.Match is not null)
{
string directory = Path.GetDirectoryName(filePair.Match) ?? throw new Exception();
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePair.Match);
string fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(filePair.Match));
string extensionSecond = Path.GetExtension(fileNameWithoutExtension);
checkFile = Path.Combine(directory, $"{fileNameWithoutExtensionSecond[..^1]}{change}{extensionSecond}{extension}");
string fileNameWithoutExtensionSecond = Path.GetFileNameWithoutExtension(filePair.Match.NameWithoutExtension);
string extensionSecond = Path.GetExtension(filePair.Match.Name);
checkFile = Path.Combine(filePair.Match.DirectoryFullPath, $"{fileNameWithoutExtensionSecond[..^1]}{change}{extensionSecond}{filePair.Match.ExtensionLowered}");
if (!File.Exists(checkFile))
File.Move(filePair.Match, checkFile);
File.Move(filePair.Match.FullName, checkFile);
}
foreach (FilePath f in filePaths)
{
@ -621,7 +271,7 @@ internal abstract class Container
continue;
foreach (Item item in filteredItems)
{
if (item.Property?.Id is null || item.ResizedFileHolder is null)
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
continue;
if (results.Contains(item.FilePath.FileNameFirstSegment))
continue;
@ -650,13 +300,13 @@ internal abstract class Container
}
foreach (Item item in filteredItems)
{
if (item.Property?.Id is null || item.ResizedFileHolder is null)
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
continue;
if (distinctItems)
{
if (distinct.Contains(item.Property.Id.Value))
if (distinct.Contains(item.ExifDirectory.FilePath.Id.Value))
continue;
distinct.Add(item.Property.Id.Value);
distinct.Add(item.ExifDirectory.FilePath.Id.Value);
}
results.Add(item);
}
@ -664,4 +314,174 @@ internal abstract class Container
return results.AsReadOnly();
}
internal static ReadOnlyCollection<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)
{
Models.Container[] results;
const string directorySearchFilter = "*";
(_, results) = GetContainers(dlibDotNet, propertyConfiguration, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter);
if (keyValuePairs is not null)
DoGetFilePairsForRemaining(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filePathsCollection, directorySearchFilter);
return results.AsReadOnly();
}
private static void DoGetFilePairsForRemaining(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, 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);
(string cResultsFullGroupDirectory, _, string dResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories("Original");
string cResizeSingletonDirectory = Path.Combine(cResultsFullGroupDirectory, propertyConfiguration.ResultSingleton);
if (!Directory.Exists(cResizeSingletonDirectory))
_ = Directory.CreateDirectory(cResizeSingletonDirectory);
_ = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, cResizeSingletonDirectory, filePathsCollection, fileNamesToFiles);
string dFaceCollectionDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultCollection);
if (!Directory.Exists(dFaceCollectionDirectory))
_ = Directory.CreateDirectory(dFaceCollectionDirectory);
_ = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, dFaceCollectionDirectory, filePathsCollection, fileNamesToFiles);
string dFaceContentDirectory = Path.Combine(dResultsFullGroupDirectory, propertyConfiguration.ResultContent);
if (!Directory.Exists(dFaceContentDirectory))
_ = Directory.CreateDirectory(dFaceContentDirectory);
AnyMovedFace(propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, fileNamesToFiles, dFaceContentDirectory);
AnyMovedDistance(propertyConfiguration, facesFileNameExtension, fileNamesToFiles, eDistanceContentDirectory);
}
private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, string extension, string hiddenExtension, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, string jsonGroupDirectory)
{
string directory;
string checkFile;
string directoryName;
List<string> files = [];
string[] fileNameSegments;
List<string> directories = [];
files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories));
files.AddRange(Directory.GetFiles(jsonGroupDirectory, $"*{hiddenExtension}", SearchOption.AllDirectories));
foreach (string file in files)
{
directory = Path.GetDirectoryName(file) ?? throw new Exception();
if (!directories.Contains(directory))
directories.Add(directory);
directoryName = Path.GetFileName(directory);
fileNameSegments = Path.GetFileName(file).Split('.');
if (fileNameSegments[0] != directoryName)
{
fileNameSegments[0] = string.Empty;
checkFile = Path.Combine(directory, $"{directoryName}{string.Join('.', fileNameSegments)}");
if (!File.Exists(checkFile))
File.Move(file, checkFile);
else
{
if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime)
File.Delete(file);
else
File.Move(file, checkFile, true);
}
}
}
if (directories.Count > 0)
AnyMovedFace(propertyConfiguration, fileNamesToFiles, directories);
}
private static void AnyMovedFace(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, List<string> directories)
{
bool result = false;
string checkFile;
FilePath filePath;
string subDirectory;
string directoryName;
string checkDirectory;
FileHolder fileHolder;
string directoryNameWith;
List<FilePath>? collection;
List<string> directoryNames = [];
foreach (string directory in directories)
{
fileHolder = IFileHolder.Get(Path.GetFileName(directory));
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
throw new Exception();
directoryNames.Clear();
foreach (FilePath f in collection)
directoryNames.Add(Path.GetFileName(f.DirectoryFullPath) ?? throw new Exception());
if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1)
continue;
directoryName = Path.GetFileName(Path.GetDirectoryName(directory)) ?? throw new Exception();
if (directoryName != directoryNames[0])
{
subDirectory = Path.GetDirectoryName(Path.GetDirectoryName(directory)) ?? throw new Exception();
checkDirectory = Path.Combine(subDirectory, directoryNames[0]);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
directoryNameWith = collection.Count > 1 ? directoryName : $"{collection[0].NameWithoutExtension}";
checkFile = Path.Combine(checkDirectory, directoryNameWith);
if (!result)
result = true;
if (!Directory.Exists(checkFile))
Directory.Move(directory, checkFile);
else
{
if (new DirectoryInfo(directory).LastWriteTime > new DirectoryInfo(checkFile).LastWriteTime)
Directory.Delete(directory, recursive: true);
else
{
Directory.Delete(checkFile, recursive: true);
Directory.Move(directory, checkFile);
}
}
}
}
}
private static void AnyMovedDistance(IPropertyConfiguration propertyConfiguration, string extension, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, string jsonGroupDirectory)
{
bool result = false;
string checkFile;
string directory;
FilePath filePath;
FileHolder fileHolder;
string[] fileNameSegments;
List<FilePath>? collection;
List<string> fileNames = [];
string[] files = Directory.GetFiles(jsonGroupDirectory, $"*{extension}", SearchOption.AllDirectories);
foreach (string file in files)
{
fileHolder = IFileHolder.Get(file);
if (!fileHolder.Exists)
continue;
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
fileNameSegments = filePath.Name.Split('.');
if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
continue;
fileNames.Clear();
foreach (FilePath f in collection)
fileNames.Add(f.NameWithoutExtension ?? throw new Exception());
if (fileNames.Count == 0 || fileNames.Distinct().Count() != 1)
continue;
if (filePath.FileNameFirstSegment != fileNames[0])
{
fileNameSegments[0] = string.Empty;
directory = Path.GetDirectoryName(file) ?? throw new Exception();
checkFile = Path.Combine(directory, $"{fileNames[0]}{string.Join('.', fileNameSegments)}");
if (!result)
result = true;
if (!File.Exists(checkFile))
File.Move(file, checkFile);
else
{
if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime)
File.Delete(file);
else
File.Move(file, checkFile, true);
}
}
}
}
}

View File

@ -29,8 +29,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) =>
Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection);
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);
internal DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection<Item> items) =>
GetContainerDateTimes(items);
@ -53,7 +53,7 @@ public interface IContainer
internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string aPropertySingletonDirectory) =>
GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory);
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) =>
GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection);
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);
}

View File

@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Phares.Shared;
using ShellProgressBar;
@ -19,7 +19,7 @@ public class CopyDistinct
private readonly IsEnvironment _IsEnvironment;
private readonly IConfigurationRoot _ConfigurationRoot;
private readonly Property.Models.Configuration _PropertyConfiguration;
private readonly ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> _FileGroups;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>> _FileGroups;
public CopyDistinct(List<string> args, ILogger<Program> logger, IsEnvironment isEnvironment, IConfigurationRoot configurationRoot, AppSettings appSettings, string workingDirectory, bool isSilent, IConsole console)
{
@ -37,7 +37,9 @@ public class CopyDistinct
_Configuration = configuration;
logger?.LogInformation(propertyConfiguration.RootDirectory);
(bool move, ReadOnlyCollection<string[]> filesCollection, bool anyLenFiles, bool moveBack) = Verify();
_FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, appSettings.CopyTo, [appSettings.ResultDirectoryKey]);
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs =
Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, appSettings.CopyTo, [appSettings.ResultDirectoryKey]);
_FileGroups = keyValuePairs[appSettings.ResultDirectoryKey];
List<string> lines = CopyDistinctFilesInDirectories(logger, move, filesCollection, anyLenFiles, moveBack);
if (lines.Count != 0)
File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines);
@ -93,6 +95,76 @@ public class CopyDistinct
return (move, new(filesCollection), anyLenFiles, moveBack);
}
private List<string> CopyDistinctFilesInDirectories(ILogger<Program>? logger, bool move, ReadOnlyCollection<string[]> filesCollection, bool anyLenFiles, bool moveBack)
{
List<string> results = [];
ProgressBar progressBar;
string[] distinctDirectories;
ConsoleKey? consoleKey = null;
string message = nameof(CopyDistinct);
List<(FilePath, string)> toDoCollection;
int count = filesCollection.Select(l => l.Length).Sum();
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
if (moveBack)
{
if (!anyLenFiles)
throw new NotSupportedException();
(distinctDirectories, toDoCollection) = GetMoveBackToDoCollection(_PropertyConfiguration, filesCollection);
}
else
{
progressBar = new(count, message, options);
string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey;
if (key != _PropertyConfiguration.ResultContent)
throw new NotImplementedException("Changed but didn't update!");
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, filesCollection, useIgnoreExtensions: true);
(distinctDirectories, toDoCollection) = IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, _AppSettings.IfCanUseId, filePathsCollection, _FileGroups, () => progressBar.Tick());
progressBar.Dispose();
}
foreach (string distinctDirectory in distinctDirectories)
{
if (!Directory.Exists(distinctDirectory))
_ = Directory.CreateDirectory(distinctDirectory);
}
if (move)
logger?.LogInformation($"Ready to Move {toDoCollection.Count} file(s)?");
else if (!moveBack)
logger?.LogInformation($"Ready to Copy {toDoCollection.Count} file(s)?");
else
logger?.LogInformation($"Ready to Move back {toDoCollection.Count} file(s)?");
for (int y = 0; y < int.MaxValue; y++)
{
if (move)
logger?.LogInformation("Press \"Y\" key to move file(s), \"N\" key to log file(s) or close console to not move files");
else if (!moveBack)
logger?.LogInformation("Press \"Y\" key to copy file(s), \"N\" key to log file(s) or close console to not copy files");
else
logger?.LogInformation("Press \"Y\" key to move back file(s), \"N\" key to log file(s) or close console to not move back files");
consoleKey = System.Console.ReadKey().Key;
if (consoleKey is ConsoleKey.Y or ConsoleKey.N)
break;
}
logger?.LogInformation(". . .");
if (consoleKey is null || consoleKey.Value != ConsoleKey.Y)
{
if (move || moveBack)
logger?.LogInformation("Nothing moved!");
else
logger?.LogInformation("Nothing copied!");
}
else
{
progressBar = new(count, message, options);
results.AddRange(IDirectory.CopyOrMove(toDoCollection, move, moveBack, () => progressBar.Tick()));
progressBar.Dispose();
if (move || moveBack)
logger?.LogInformation("Done moving");
else
logger?.LogInformation("Done copying");
}
return results;
}
private static (string[], List<(FilePath, string)>) GetMoveBackToDoCollection(Property.Models.Configuration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection)
{
List<(FilePath, string)> results = [];
@ -159,74 +231,4 @@ public class CopyDistinct
return (distinctDirectories.ToArray(), results);
}
private List<string> CopyDistinctFilesInDirectories(ILogger<Program>? logger, bool move, ReadOnlyCollection<string[]> filesCollection, bool anyLenFiles, bool moveBack)
{
List<string> results = [];
ProgressBar progressBar;
string[] distinctDirectories;
ConsoleKey? consoleKey = null;
string message = nameof(CopyDistinct);
List<(FilePath, string)> toDoCollection;
int count = filesCollection.Select(l => l.Length).Sum();
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
if (moveBack)
{
if (!anyLenFiles)
throw new NotSupportedException();
(distinctDirectories, toDoCollection) = GetMoveBackToDoCollection(_PropertyConfiguration, filesCollection);
}
else
{
progressBar = new(count, message, options);
string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey;
if (key != _PropertyConfiguration.ResultContent)
throw new NotImplementedException("Changed but didn't update!");
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, filesCollection);
(distinctDirectories, toDoCollection) = IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, _AppSettings.IfCanUseId, filePathsCollection, _FileGroups, () => progressBar.Tick());
progressBar.Dispose();
}
foreach (string distinctDirectory in distinctDirectories)
{
if (!Directory.Exists(distinctDirectory))
_ = Directory.CreateDirectory(distinctDirectory);
}
if (move)
logger?.LogInformation($"Ready to Move {toDoCollection.Count} file(s)?");
else if (!moveBack)
logger?.LogInformation($"Ready to Copy {toDoCollection.Count} file(s)?");
else
logger?.LogInformation($"Ready to Move back {toDoCollection.Count} file(s)?");
for (int y = 0; y < int.MaxValue; y++)
{
if (move)
logger?.LogInformation("Press \"Y\" key to move file(s), \"N\" key to log file(s) or close console to not move files");
else if (!moveBack)
logger?.LogInformation("Press \"Y\" key to copy file(s), \"N\" key to log file(s) or close console to not copy files");
else
logger?.LogInformation("Press \"Y\" key to move back file(s), \"N\" key to log file(s) or close console to not move back files");
consoleKey = System.Console.ReadKey().Key;
if (consoleKey is ConsoleKey.Y or ConsoleKey.N)
break;
}
logger?.LogInformation(". . .");
if (consoleKey is null || consoleKey.Value != ConsoleKey.Y)
{
if (move || moveBack)
logger?.LogInformation("Nothing moved!");
else
logger?.LogInformation("Nothing copied!");
}
else
{
progressBar = new(count, message, options);
results.AddRange(IDirectory.CopyOrMove(toDoCollection, move, moveBack, () => progressBar.Tick()));
progressBar.Dispose();
if (move || moveBack)
logger?.LogInformation("Done moving");
else
logger?.LogInformation("Done copying");
}
return results;
}
}

View File

@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Phares.Shared;
using System.Globalization;
@ -96,203 +96,6 @@ public class DateGroup
throw new Exception("Change configuration!");
}
private static bool WriteAllText(string path, string contents, bool compareBeforeWrite)
{
bool result;
string text;
if (!compareBeforeWrite)
result = true;
else
{
if (!File.Exists(path))
text = string.Empty;
else
text = File.ReadAllText(path);
result = text != contents;
}
if (result)
{
if (path.Contains("()"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("[]") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{')
File.WriteAllText(path, contents);
else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[')
File.WriteAllText(path, contents);
else
File.WriteAllText(path, contents);
}
return result;
}
private List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> GetMoveFileCollection(string destinationDirectory, string topDirectory, Item[] filteredItems)
{
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> results = [];
char flag;
string day;
int season;
string year;
string month;
string? check;
string fileName;
string? pathRoot;
DateTime dateTime;
string seasonName;
string weekOfYear;
bool? isWrongYear;
string seasonValue;
string directoryName;
string topDirectoryName;
List<DateTime> dateTimes;
string[]? matches = null;
string[] directorySegments;
List<string> destinationCollection;
List<string> directoryNames = [];
List<string> topDirectorySegments = [];
StringBuilder destinationDirectoryName = new();
Calendar calendar = new CultureInfo("en-US").Calendar;
for (int z = 1; z < 3; z++)
{
if (z == 1)
{
check = Path.Combine(destinationDirectory, ".");
pathRoot = Path.GetPathRoot(destinationDirectory);
}
else if (z == 2)
{
check = Path.Combine(topDirectory, ".");
pathRoot = Path.GetPathRoot(topDirectory);
}
else
throw new Exception();
if (string.IsNullOrEmpty(pathRoot))
continue;
for (int i = 0; i < int.MaxValue; i++)
{
check = Path.GetDirectoryName(check);
if (string.IsNullOrEmpty(check) || check == pathRoot)
break;
directoryName = Path.GetFileName(check);
directorySegments = directoryName.Split(' ');
topDirectorySegments.AddRange(directorySegments);
(_, matches) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(directorySegments, string.Empty);
if (matches.Length != 0)
break;
}
if (matches is not null && matches.Length != 0)
break;
}
foreach (Item item in filteredItems)
{
directoryNames.Clear();
destinationCollection = [];
_ = destinationDirectoryName.Clear();
if (item.Property is not null)
dateTimes = item.Property.GetDateTimes();
else
dateTimes = [new(item.FilePath.LastWriteTicks)];
if (item.Property is not null && item.Property.DateTimeOriginal is not null)
dateTime = item.Property.DateTimeOriginal.Value;
else if (item.Property is null)
dateTime = new(item.FilePath.LastWriteTicks);
else
dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
day = dateTime.ToString("MM-dd");
month = dateTime.ToString("MMMM");
if (item.Property?.Id is null)
{
flag = '#';
isWrongYear = null;
}
else
{
(isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes);
if (isWrongYear is null)
flag = '#';
else if (isWrongYear.Value)
flag = '~';
else
{
if (item.Property.DateTimeOriginal is not null && dateTime.DayOfYear != item.Property.DateTimeOriginal.Value.DayOfYear && Math.Abs(new TimeSpan(dateTime.Ticks - item.Property.DateTimeOriginal.Value.Ticks).TotalHours) > 8)
flag = '^';
else
flag = '=';
}
}
(season, seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
if ((from l in topDirectorySegments where l == "Christmas" select true).Any())
seasonValue = string.Empty;
else
seasonValue = $".{season}";
if (isWrongYear is null || !isWrongYear.Value)
year = $"{flag}{dateTime:yyyy}{seasonValue}";
else if (matches is null || matches.Length < 3)
year = "----";
else
{
if (matches[0][0] != '~')
year = $"{flag}{matches[0].Split('.')[0]}{seasonValue}";
else
year = $"{flag}{matches[0][1..].Split('.')[0]}{seasonValue}";
}
topDirectoryName = Path.GetFileName(topDirectory);
weekOfYear = calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
if (_Configuration.ByHash)
directoryNames.Add($"{year} {seasonName}");
else if (_Configuration.BySeason && topDirectoryName.Length == 1 && topDirectoryName[0] == '_')
directoryNames.Add($"{year} {seasonName}");
else
{
if (!_Configuration.KeepFullPath)
{
if (topDirectoryName.Length > 1)
_ = destinationDirectoryName.Append(topDirectoryName);
if (_Configuration.BySeason)
directoryNames.AddRange([$"{destinationDirectoryName} {year} {seasonName}"]);
else if (_Configuration.ByDay)
directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year}-{day}"]);
else if (_Configuration.ByWeek)
directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year} {month}"]);
else
throw new Exception();
}
else
{
foreach (string sourceDirectoryNameSegment in topDirectorySegments)
{
if (matches is not null && matches.Contains(sourceDirectoryNameSegment))
_ = destinationDirectoryName.Append(year);
else
_ = destinationDirectoryName.Append(sourceDirectoryNameSegment);
}
if (_Configuration.BySeason)
directoryNames.Add($"{year} {seasonName}");
else if (_Configuration.ByDay)
directoryNames.Add($"{weekOfYear}) {year} {day}");
else if (_Configuration.ByWeek)
directoryNames.Add($"{weekOfYear}) {month} {year}");
else
throw new Exception();
}
}
if (!_Configuration.ByHash || item.Property?.Id is null)
fileName = item.FilePath.Name;
else
fileName = $"{item.Property.Id.Value}{item.FilePath.ExtensionLowered}";
destinationCollection.Add(destinationDirectory);
destinationCollection.AddRange(directoryNames);
destinationCollection.Add(fileName);
if (item.Property is null)
results.Add(new(item, item.FilePath.LastWriteTicks, dateTime.Ticks, destinationCollection.ToArray()));
else
results.Add(new(item, item.Property.LastWriteTime.Ticks, dateTime.Ticks, destinationCollection.ToArray()));
}
return results;
}
private A_Property GetPropertyLogic(bool reverse, string outputExtension, string aResultsFullGroupDirectory)
{
A_Property result;
@ -302,54 +105,64 @@ public class DateGroup
return result;
}
private static Item[] GetFilterItems(Container.Models.Container container)
private static void CreateDateShortcut(Property.Models.Configuration configuration, Container.Models.Container[] containers)
{
List<Item> results = [];
foreach (Item item in container.Items)
{
if (item.FilePath is not null)
results.Add(item);
}
return results.ToArray();
}
private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers)
{
(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results;
Item[] filteredItems;
string? topDirectory;
string? checkDirectory;
string destinationDirectory;
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollection = [];
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollectionDirectory;
string path;
string fileName;
string directory;
DateTime dateTime;
int selectedTotal;
const int minimum = 3;
List<DateTime> dateTimes;
List<Item> selectedItems;
const int maximumHours = 24;
string? relativePathDirectory;
WindowsShortcut windowsShortcut;
TimeSpan threeStandardDeviationHigh;
string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()");
foreach (Container.Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
if (!_Configuration.KeepFullPath)
destinationDirectory = destinationRoot;
else
destinationDirectory = string.Concat(destinationRoot, container.SourceDirectory[configuration.RootDirectory.Length..]);
checkDirectory = Path.GetFullPath(container.SourceDirectory);
for (int z = 0; z < int.MaxValue; z++)
selectedTotal = 0;
threeStandardDeviationHigh = Property.Models.Stateless.IProperty.GetThreeStandardDeviationHigh(minimum, container);
if (threeStandardDeviationHigh.TotalHours > maximumHours)
threeStandardDeviationHigh = new(maximumHours, 0, 0);
for (int i = 0; i < container.Items.Count; i++)
{
if (checkDirectory == configuration.RootDirectory)
break;
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory))
break;
}
if (string.IsNullOrEmpty(checkDirectory))
(i, dateTimes, selectedItems) = Property.Models.Stateless.IProperty.Get(container, threeStandardDeviationHigh, i);
selectedTotal += selectedItems.Count;
foreach (Item item in selectedItems)
{
if (item.ExifDirectory is null)
continue;
topDirectory = checkDirectory;
filteredItems = GetFilterItems(container);
if (filteredItems.Length == 0)
relativePathDirectory = Path.GetDirectoryName(item.RelativePath);
if (string.IsNullOrEmpty(relativePathDirectory))
continue;
if (item.ExifDirectory is null)
dateTime = new(item.FilePath.LastWriteTicks);
else if (item.ExifDirectory is not null && item.ExifDirectory.DateTimeOriginal is not null)
dateTime = item.ExifDirectory.DateTimeOriginal.Value;
else
dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.ExifDirectory);
path = Path.GetFullPath($"{configuration.RootDirectory}{item.RelativePath[..^5]}");
directory = Path.Combine($"{aPropertyContentDirectory}{relativePathDirectory}", $"{dateTimes.Min():yyyy-MM-dd_HH-mm-ss}---{dateTimes.Max():yyyy-MM-dd_HH-mm-ss}");
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
fileName = Path.Combine(directory, $"{Path.GetFileName(item.RelativePath[..^5])}.lnk");
if (File.Exists(fileName))
continue;
windowsShortcut = new() { Path = path };
windowsShortcut.Save(fileName);
windowsShortcut.Dispose();
if (!File.Exists(fileName))
continue;
File.SetLastWriteTime(fileName, dateTime);
}
}
if (selectedTotal < container.Items.Count && selectedTotal < (from l in container.Items where l.Property is not null select true).Count())
continue;
fileMoveCollectionDirectory = GetMoveFileCollection(destinationDirectory, topDirectory, filteredItems);
fileMoveCollection.AddRange(fileMoveCollectionDirectory);
}
results = (from l in fileMoveCollection orderby l.MinimumDateTimeTicks descending, l.LastWriteTimeTicks descending select l).ToArray();
return results;
}
private void MoveFiles(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers)
@ -453,64 +266,251 @@ public class DateGroup
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(configuration.RootDirectory);
}
private static void CreateDateShortcut(Property.Models.Configuration configuration, Container.Models.Container[] containers)
private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers)
{
string path;
string fileName;
string directory;
DateTime dateTime;
int selectedTotal;
const int minimum = 3;
List<DateTime> dateTimes;
List<Item> selectedItems;
const int maximumHours = 24;
string? relativePathDirectory;
WindowsShortcut windowsShortcut;
TimeSpan threeStandardDeviationHigh;
string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()");
(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results;
Item[] filteredItems;
string? topDirectory;
string? checkDirectory;
string destinationDirectory;
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollection = [];
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollectionDirectory;
foreach (Container.Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
selectedTotal = 0;
threeStandardDeviationHigh = Property.Models.Stateless.IProperty.GetThreeStandardDeviationHigh(minimum, container);
if (threeStandardDeviationHigh.TotalHours > maximumHours)
threeStandardDeviationHigh = new(maximumHours, 0, 0);
for (int i = 0; i < container.Items.Count; i++)
{
(i, dateTimes, selectedItems) = Property.Models.Stateless.IProperty.Get(container, threeStandardDeviationHigh, i);
selectedTotal += selectedItems.Count;
foreach (Item item in selectedItems)
{
if (item.Property is null)
continue;
relativePathDirectory = Path.GetDirectoryName(item.RelativePath);
if (string.IsNullOrEmpty(relativePathDirectory))
continue;
if (item.Property is null)
dateTime = new(item.FilePath.LastWriteTicks);
else if (item.Property is not null && item.Property.DateTimeOriginal is not null)
dateTime = item.Property.DateTimeOriginal.Value;
if (!_Configuration.KeepFullPath)
destinationDirectory = destinationRoot;
else
dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
path = Path.GetFullPath($"{configuration.RootDirectory}{item.RelativePath[..^5]}");
directory = Path.Combine($"{aPropertyContentDirectory}{relativePathDirectory}", $"{dateTimes.Min():yyyy-MM-dd_HH-mm-ss}---{dateTimes.Max():yyyy-MM-dd_HH-mm-ss}");
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
fileName = Path.Combine(directory, $"{Path.GetFileName(item.RelativePath[..^5])}.lnk");
if (File.Exists(fileName))
destinationDirectory = string.Concat(destinationRoot, container.SourceDirectory[configuration.RootDirectory.Length..]);
checkDirectory = Path.GetFullPath(container.SourceDirectory);
for (int z = 0; z < int.MaxValue; z++)
{
if (checkDirectory == configuration.RootDirectory)
break;
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory))
break;
}
if (string.IsNullOrEmpty(checkDirectory))
continue;
windowsShortcut = new() { Path = path };
windowsShortcut.Save(fileName);
windowsShortcut.Dispose();
if (!File.Exists(fileName))
topDirectory = checkDirectory;
filteredItems = GetFilterItems(container);
if (filteredItems.Length == 0)
continue;
File.SetLastWriteTime(fileName, dateTime);
fileMoveCollectionDirectory = GetMoveFileCollection(destinationDirectory, topDirectory, filteredItems);
fileMoveCollection.AddRange(fileMoveCollectionDirectory);
}
results = (from l in fileMoveCollection orderby l.MinimumDateTimeTicks descending, l.LastWriteTimeTicks descending select l).ToArray();
return results;
}
private static Item[] GetFilterItems(Container.Models.Container container)
{
List<Item> results = [];
foreach (Item item in container.Items)
{
if (item.FilePath is not null)
results.Add(item);
}
return results.ToArray();
}
private List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> GetMoveFileCollection(string destinationDirectory, string topDirectory, Item[] filteredItems)
{
List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> results = [];
char flag;
string day;
int season;
string year;
string month;
string? check;
string fileName;
string? pathRoot;
DateTime dateTime;
string seasonName;
string weekOfYear;
bool? isWrongYear;
string seasonValue;
string directoryName;
string topDirectoryName;
List<DateTime> dateTimes;
string[]? matches = null;
string[] directorySegments;
List<string> destinationCollection;
List<string> directoryNames = [];
List<string> topDirectorySegments = [];
StringBuilder destinationDirectoryName = new();
Calendar calendar = new CultureInfo("en-US").Calendar;
for (int z = 1; z < 3; z++)
{
if (z == 1)
{
check = Path.Combine(destinationDirectory, ".");
pathRoot = Path.GetPathRoot(destinationDirectory);
}
else if (z == 2)
{
check = Path.Combine(topDirectory, ".");
pathRoot = Path.GetPathRoot(topDirectory);
}
else
throw new Exception();
if (string.IsNullOrEmpty(pathRoot))
continue;
for (int i = 0; i < int.MaxValue; i++)
{
check = Path.GetDirectoryName(check);
if (string.IsNullOrEmpty(check) || check == pathRoot)
break;
directoryName = Path.GetFileName(check);
directorySegments = directoryName.Split(' ');
topDirectorySegments.AddRange(directorySegments);
(_, matches) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(directorySegments, string.Empty);
if (matches.Length != 0)
break;
}
if (matches is not null && matches.Length != 0)
break;
}
foreach (Item item in filteredItems)
{
directoryNames.Clear();
destinationCollection = [];
_ = destinationDirectoryName.Clear();
if (item.ExifDirectory is not null)
dateTimes = item.ExifDirectory.GetDateTimes();
else
dateTimes = [new(item.FilePath.LastWriteTicks)];
if (item.ExifDirectory is not null && item.ExifDirectory.DateTimeOriginal is not null)
dateTime = item.ExifDirectory.DateTimeOriginal.Value;
else if (item.ExifDirectory is null)
dateTime = new(item.FilePath.LastWriteTicks);
else
dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.ExifDirectory);
day = dateTime.ToString("MM-dd");
month = dateTime.ToString("MMMM");
if (item.ExifDirectory?.FilePath.Id is null)
{
flag = '#';
isWrongYear = null;
}
else
{
(isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.ExifDirectory.DateTimeOriginal, dateTimes);
if (isWrongYear is null)
flag = '#';
else if (isWrongYear.Value)
flag = '~';
else
{
if (item.ExifDirectory.DateTimeOriginal is not null && dateTime.DayOfYear != item.ExifDirectory.DateTimeOriginal.Value.DayOfYear && Math.Abs(new TimeSpan(dateTime.Ticks - item.ExifDirectory.DateTimeOriginal.Value.Ticks).TotalHours) > 8)
flag = '^';
else
flag = '=';
}
}
if (selectedTotal < container.Items.Count && selectedTotal < (from l in container.Items where l.Property is not null select true).Count())
continue;
(season, seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
if ((from l in topDirectorySegments where l == "Christmas" select true).Any())
seasonValue = string.Empty;
else
seasonValue = $".{season}";
if (isWrongYear is null || !isWrongYear.Value)
year = $"{flag}{dateTime:yyyy}{seasonValue}";
else if (matches is null || matches.Length < 3)
year = "----";
else
{
if (matches[0][0] != '~')
year = $"{flag}{matches[0].Split('.')[0]}{seasonValue}";
else
year = $"{flag}{matches[0][1..].Split('.')[0]}{seasonValue}";
}
topDirectoryName = Path.GetFileName(topDirectory);
weekOfYear = calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00");
if (_Configuration.ByHash)
directoryNames.Add($"{year} {seasonName}");
else if (_Configuration.BySeason && topDirectoryName.Length == 1 && topDirectoryName[0] == '_')
directoryNames.Add($"{year} {seasonName}");
else
{
if (!_Configuration.KeepFullPath)
{
if (topDirectoryName.Length > 1)
_ = destinationDirectoryName.Append(topDirectoryName);
if (_Configuration.BySeason)
directoryNames.AddRange([$"{destinationDirectoryName} {year} {seasonName}"]);
else if (_Configuration.ByDay)
directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year}-{day}"]);
else if (_Configuration.ByWeek)
directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year} {month}"]);
else
throw new Exception();
}
else
{
foreach (string sourceDirectoryNameSegment in topDirectorySegments)
{
if (matches is not null && matches.Contains(sourceDirectoryNameSegment))
_ = destinationDirectoryName.Append(year);
else
_ = destinationDirectoryName.Append(sourceDirectoryNameSegment);
}
if (_Configuration.BySeason)
directoryNames.Add($"{year} {seasonName}");
else if (_Configuration.ByDay)
directoryNames.Add($"{weekOfYear}) {year} {day}");
else if (_Configuration.ByWeek)
directoryNames.Add($"{weekOfYear}) {month} {year}");
else
throw new Exception();
}
}
if (!_Configuration.ByHash || item.ExifDirectory?.FilePath.Id is null)
fileName = item.FilePath.Name;
else
fileName = $"{item.ExifDirectory.FilePath.Id.Value}{item.FilePath.ExtensionLowered}";
destinationCollection.Add(destinationDirectory);
destinationCollection.AddRange(directoryNames);
destinationCollection.Add(fileName);
if (item.ExifDirectory is null)
results.Add(new(item, item.FilePath.LastWriteTicks, dateTime.Ticks, destinationCollection.ToArray()));
else
results.Add(new(item, item.ExifDirectory.LastWriteTime.Ticks, dateTime.Ticks, destinationCollection.ToArray()));
}
return results;
}
private static bool WriteAllText(string path, string contents, bool compareBeforeWrite)
{
bool result;
string text;
if (!compareBeforeWrite)
result = true;
else
{
if (!File.Exists(path))
text = string.Empty;
else
text = File.ReadAllText(path);
result = text != contents;
}
if (result)
{
if (path.Contains("()"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("[]") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{')
File.WriteAllText(path, contents);
else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[')
File.WriteAllText(path, contents);
else
File.WriteAllText(path, contents);
}
return result;
}
}

View File

@ -64,63 +64,6 @@ public partial class DragDropSearch : Form
Controls.Add(_TextBox);
}
private void Form1_Load(object? sender, EventArgs e)
{
try
{
AllowDrop = true;
DragDrop += new DragEventHandler(Form1_DragDrop);
DragEnter += new DragEventHandler(Form1_DragEnter);
_TextBox.LostFocus += new EventHandler(TextBox_LostFocus);
}
catch (Exception)
{
throw;
}
}
private void TextBox_LostFocus(object? sender, EventArgs e)
{
try
{
if (_TextBox.Text == "ps")
throw new NotImplementedException();
}
catch (Exception)
{
throw;
}
}
private void Form1_DragEnter(object? sender, DragEventArgs e)
{
try
{
if (e.Data is not null && e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
}
catch (Exception)
{
throw;
}
}
private void LoadData()
{
Container.Models.Container[] containers;
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}");
(_, containers) = View_by_Distance.Container.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory);
List<Item> collection = Program.GetItemCollection(_Configuration, containers);
foreach (Item item in collection)
{
if (item.Property?.Id is null)
continue;
if (_IdToItem.ContainsKey(item.Property.Id.Value))
continue;
_IdToItem.Add(item.Property.Id.Value, item);
}
}
public static string? GetFaceEncoding(string file)
{
string? result;
@ -147,6 +90,37 @@ public partial class DragDropSearch : Form
return result;
}
private void LoadData()
{
Container.Models.Container[] containers;
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}");
(_, containers) = View_by_Distance.Container.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory);
List<Item> collection = Program.GetItemCollection(_Configuration, containers);
foreach (Item item in collection)
{
if (item.ExifDirectory?.FilePath.Id is null)
continue;
if (_IdToItem.ContainsKey(item.ExifDirectory.FilePath.Id.Value))
continue;
_IdToItem.Add(item.ExifDirectory.FilePath.Id.Value, item);
}
}
private void Form1_Load(object? sender, EventArgs e)
{
try
{
AllowDrop = true;
DragDrop += new DragEventHandler(Form1_DragDrop);
DragEnter += new DragEventHandler(Form1_DragEnter);
_TextBox.LostFocus += new EventHandler(TextBox_LostFocus);
}
catch (Exception)
{
throw;
}
}
private void GetDirectoriesOrDoDragDrop(string[] paths)
{
string name;
@ -168,6 +142,19 @@ public partial class DragDropSearch : Form
}
}
private void TextBox_LostFocus(object? sender, EventArgs e)
{
try
{
if (_TextBox.Text == "ps")
throw new NotImplementedException();
}
catch (Exception)
{
throw;
}
}
private void Form1_DragDrop(object? sender, DragEventArgs e)
{
try
@ -183,4 +170,17 @@ public partial class DragDropSearch : Form
}
}
private void Form1_DragEnter(object? sender, DragEventArgs e)
{
try
{
if (e.Data is not null && e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
}
catch (Exception)
{
throw;
}
}
}

View File

@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Phares.Shared;
using ShellProgressBar;
@ -61,6 +61,144 @@ public class DuplicateSearch
File.WriteAllText(Path.Combine(alongSideDirectory, $"{directoryName}-{ticks}.json"), json);
}
private static Container.Models.Container[] GetContainers(long ticks, Configuration configuration)
{
int f;
Container.Models.Container[] containers;
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
string message = $") Building Container(s) - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using (ProgressBar progressBar = new(1, message, options))
{
progressBar.Tick();
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}");
(f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory);
}
return containers;
}
private static List<int> GetPreloadIds(string destinationRoot)
{
List<int> results = [];
string[] lines;
string preloadDirectory = Path.Combine(destinationRoot, "Preload");
if (!Directory.Exists(preloadDirectory))
_ = Directory.CreateDirectory(preloadDirectory);
string[] files = Directory.GetFiles(preloadDirectory, "*.lsv", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
lines = File.ReadAllLines(file);
foreach (string line in lines)
{
if (string.IsNullOrEmpty(line) || !int.TryParse(line, out int id) || id == 0)
continue;
results.Add(id);
}
}
return results;
}
private static Dictionary<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 directory;
const int zero = 0;
FileHolder resizedFileHolder;
DateTime[] containerDateTimes;
MappingFromItem? mappingFromItem;
List<MappingFromItem?>? collection;
ReadOnlyCollection<Item> validImageItems;
const string duplicates = "-Duplicate(s)";
if (containers.Length != 0)
{
foreach (int id in preloadIds)
results.Add(id, [null]);
}
foreach (Container.Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
if (!argZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero))
continue;
validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(configuration, container);
if (validImageItems.Count == 0)
continue;
containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(validImageItems);
foreach (Item item in validImageItems)
{
if (item.ExifDirectory?.FilePath.Id is null)
{
if (int.TryParse(item.FilePath.NameWithoutExtension, out int id))
continue;
continue;
}
if (!results.TryGetValue(item.ExifDirectory.FilePath.Id.Value, out collection))
results.Add(item.ExifDirectory.FilePath.Id.Value, []);
if (collection is null && !results.TryGetValue(item.ExifDirectory.FilePath.Id.Value, out collection))
continue;
if (collection.Count == 0)
directory = $"0{duplicates}";
else
directory = $"{collection.Count + 1}{duplicates}";
if (collection.Count == 1)
{
mappingFromItem = collection[zero];
if (mappingFromItem is not null)
{
resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(mappingFromItem.ResizedFileHolder.FullName.Replace($"0{duplicates}", $"1{duplicates}"));
collection[0] = new(mappingFromItem.ContainerDateTimes, item.Property.DateTimeDigitized, item.Property.DateTimeOriginal, mappingFromItem.Id, mappingFromItem.IsArchive, mappingFromItem.FilePath, mappingFromItem.IsWrongYear, item.Property.Keywords ?? [], mappingFromItem.MinimumDateTime, item.Property.Model, mappingFromItem.RelativePath, resizedFileHolder);
}
}
resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(string.Concat(Path.Combine(destinationRoot, directory), item.RelativePath));
mappingFromItem = Shared.Models.Stateless.Methods.IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
collection.Add(mappingFromItem);
}
}
return results;
}
private static void QuestionMove(long ticks, ILogger<Program>? logger, string destinationRoot, Dictionary<int, List<MappingFromItem?>> idToCollection, int duplicates)
{
int[] ids = (from l in idToCollection orderby l.Key where l.Value.Any(m => m is not null) select l.Key).ToArray();
_ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(destinationRoot, $"{ticks}-id(s).lsv"), string.Join(Environment.NewLine, ids), updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
string json = JsonSerializer.Serialize(idToCollection, new JsonSerializerOptions { WriteIndented = true });
_ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(destinationRoot, $"{ticks}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
logger?.LogInformation($"Found {duplicates} duplicate file(s)");
for (int y = 0; y < int.MaxValue; y++)
{
logger?.LogInformation("Press \"Y\" key to continue or close console to leave them moved");
if (System.Console.ReadKey().Key != ConsoleKey.Y)
continue;
logger?.LogInformation(". . .");
List<(FilePath FilePath, string Destination)> collection = GetCollectionAndCreateDirectories(idToCollection);
Move(logger, ticks, destinationRoot, collection);
}
}
private static List<(FilePath FilePath, string Destination)> GetCollectionAndCreateDirectories(Dictionary<int, List<MappingFromItem?>> idToCollection)
{
List<(FilePath FilePath, string Destination)> results = [];
List<string> collection = [];
foreach (KeyValuePair<int, List<MappingFromItem?>> keyValuePair in idToCollection)
{
foreach (MappingFromItem? mappingFromItem in keyValuePair.Value)
{
if (mappingFromItem?.ResizedFileHolder.DirectoryFullPath is null)
continue;
if (mappingFromItem.ResizedFileHolder.Exists)
continue;
collection.Add(mappingFromItem.ResizedFileHolder.DirectoryFullPath);
results.Add(new(mappingFromItem.FilePath, mappingFromItem.ResizedFileHolder.FullName));
}
}
foreach (string directory in collection.Distinct())
{
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
}
return results;
}
private static void Move(ILogger<Program>? logger, long ticks, string destinationRoot, List<(FilePath FilePath, string Destination)> collection)
{
StringBuilder stringBuilder = new();
@ -107,142 +245,4 @@ public class DuplicateSearch
logger?.LogInformation(". . .");
}
private static void QuestionMove(long ticks, ILogger<Program>? logger, string destinationRoot, Dictionary<int, List<MappingFromItem?>> idToCollection, int duplicates)
{
int[] ids = (from l in idToCollection orderby l.Key where l.Value.Any(m => m is not null) select l.Key).ToArray();
_ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(destinationRoot, $"{ticks}-id(s).lsv"), string.Join(Environment.NewLine, ids), updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
string json = JsonSerializer.Serialize(idToCollection, new JsonSerializerOptions { WriteIndented = true });
_ = Shared.Models.Stateless.Methods.IPath.WriteAllText(Path.Combine(destinationRoot, $"{ticks}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
logger?.LogInformation($"Found {duplicates} duplicate file(s)");
for (int y = 0; y < int.MaxValue; y++)
{
logger?.LogInformation("Press \"Y\" key to continue or close console to leave them moved");
if (System.Console.ReadKey().Key != ConsoleKey.Y)
continue;
logger?.LogInformation(". . .");
List<(FilePath FilePath, string Destination)> collection = GetCollectionAndCreateDirectories(idToCollection);
Move(logger, ticks, destinationRoot, collection);
}
}
private static Container.Models.Container[] GetContainers(long ticks, Configuration configuration)
{
int f;
Container.Models.Container[] containers;
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
string message = $") Building Container(s) - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using (ProgressBar progressBar = new(1, message, options))
{
progressBar.Tick();
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}");
(f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory);
}
return containers;
}
private static Dictionary<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 directory;
const int zero = 0;
FileHolder resizedFileHolder;
DateTime[] containerDateTimes;
MappingFromItem? mappingFromItem;
List<MappingFromItem?>? collection;
ReadOnlyCollection<Item> validImageItems;
const string duplicates = "-Duplicate(s)";
if (containers.Length != 0)
{
foreach (int id in preloadIds)
results.Add(id, [null]);
}
foreach (Container.Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
if (!argZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero))
continue;
validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(configuration, container);
if (validImageItems.Count == 0)
continue;
containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(validImageItems);
foreach (Item item in validImageItems)
{
if (item.Property?.Id is null)
{
if (int.TryParse(item.FilePath.NameWithoutExtension, out int id))
continue;
continue;
}
if (!results.TryGetValue(item.Property.Id.Value, out collection))
results.Add(item.Property.Id.Value, []);
if (collection is null && !results.TryGetValue(item.Property.Id.Value, out collection))
continue;
if (collection.Count == 0)
directory = $"0{duplicates}";
else
directory = $"{collection.Count + 1}{duplicates}";
if (collection.Count == 1)
{
mappingFromItem = collection[zero];
if (mappingFromItem is not null)
{
resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(mappingFromItem.ResizedFileHolder.FullName.Replace($"0{duplicates}", $"1{duplicates}"));
collection[0] = new(mappingFromItem.ContainerDateTimes, item.Property.DateTimeDigitized, item.Property.DateTimeOriginal, mappingFromItem.Id, mappingFromItem.IsArchive, mappingFromItem.FilePath, mappingFromItem.IsWrongYear, item.Property.Keywords ?? [], mappingFromItem.MinimumDateTime, item.Property.Model, mappingFromItem.RelativePath, resizedFileHolder);
}
}
resizedFileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(string.Concat(Path.Combine(destinationRoot, directory), item.RelativePath));
mappingFromItem = Shared.Models.Stateless.Methods.IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
collection.Add(mappingFromItem);
}
}
return results;
}
private static List<(FilePath FilePath, string Destination)> GetCollectionAndCreateDirectories(Dictionary<int, List<MappingFromItem?>> idToCollection)
{
List<(FilePath FilePath, string Destination)> results = [];
List<string> collection = [];
foreach (KeyValuePair<int, List<MappingFromItem?>> keyValuePair in idToCollection)
{
foreach (MappingFromItem? mappingFromItem in keyValuePair.Value)
{
if (mappingFromItem?.ResizedFileHolder.DirectoryFullPath is null)
continue;
if (mappingFromItem.ResizedFileHolder.Exists)
continue;
collection.Add(mappingFromItem.ResizedFileHolder.DirectoryFullPath);
results.Add(new(mappingFromItem.FilePath, mappingFromItem.ResizedFileHolder.FullName));
}
}
foreach (string directory in collection.Distinct())
{
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
}
return results;
}
private static List<int> GetPreloadIds(string destinationRoot)
{
List<int> results = [];
string[] lines;
string preloadDirectory = Path.Combine(destinationRoot, "Preload");
if (!Directory.Exists(preloadDirectory))
_ = Directory.CreateDirectory(preloadDirectory);
string[] files = Directory.GetFiles(preloadDirectory, "*.lsv", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
lines = File.ReadAllLines(file);
foreach (string line in lines)
{
if (string.IsNullOrEmpty(line) || !int.TryParse(line, out int id) || id == 0)
continue;
results.Add(id);
}
}
return results;
}
}

View File

@ -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, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<MappingFromPhotoPrism>? mappingFromPhotoPrismCollection)
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)
{
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, property, mappingFromItem, outputResolutionToResize, locations);
results = GetFaces(outputResolution, cResultsFullGroupDirectory, exifDirectory, mappingFromItem, outputResolutionToResize, locations);
if (results.Count == 0)
File.Move(mappingFromItem.ResizedFileHolder.FullName, $"{mappingFromItem.ResizedFileHolder.FullName}.err");
else
@ -338,7 +338,7 @@ public class D_Face : IFaceD
return results;
}
private List<Shared.Models.Face> GetFaces(string outputResolution, string cResultsFullGroupDirectory, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<Location> locations)
private List<Shared.Models.Face> GetFaces(string outputResolution, string cResultsFullGroupDirectory, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<Location> locations)
{
if (_PropertyConfiguration.NumberOfJitters is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters));
@ -374,7 +374,7 @@ public class D_Face : IFaceD
FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, _Model, _ModelParameter, _PredictorModel);
collection = faceRecognition.GetCollection(unknownImage, locations, includeFaceEncoding: true, includeFaceParts: true);
if (collection.Count == 0)
results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null));
results.Add(new(exifDirectory, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null));
else
{
double[] rawEncoding;
@ -382,7 +382,7 @@ public class D_Face : IFaceD
Shared.Models.FaceEncoding convertedFaceEncoding;
foreach ((Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary<FacePart, FacePoint[]>? faceParts) in collection)
{
face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location);
face = new(exifDirectory, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location);
if (faceEncoding is not null)
{
rawEncoding = faceEncoding.GetRawEncoding();

View File

@ -25,6 +25,13 @@ namespace View_by_Distance.Instance;
public partial class DlibDotNet : IDlibDotNet, IDisposable
{
public record Record(ReadOnlyDictionary<int, ExifDirectory> ExifDirectoriesById,
string FilesCollectionRootDirectory,
bool FilesCollectionCountIsOne,
ReadOnlyCollection<ReadOnlyCollection<FilePath>> FilePathsCollection,
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? IdToFilePaths,
ReadOnlyDictionary<int, Identifier>? SplatNineIdentifiers);
private readonly D_Face _Faces;
private ProgressBar? _ProgressBar;
private readonly C_Resize _Resize;
@ -40,6 +47,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
private readonly DistanceLimits _DistanceLimits;
private readonly bool _PropertyRootExistedBefore;
private readonly Models.Configuration _Configuration;
private readonly ProgressBarOptions _ProgressBarOptions;
private readonly bool _ArgZeroIsConfigurationRootDirectory;
private readonly Map.Models.Configuration _MapConfiguration;
private readonly List<(string Directory, long PersonKey)> _JLinkResolvedDirectories;
@ -56,15 +64,16 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
{
string message;
_Logger = logger;
_Exceptions = [];
_Console = console;
_AppSettings = appSettings;
_IsEnvironment = isEnvironment;
long ticks = DateTime.Now.Ticks;
_Exceptions = [];
_JLinkResolvedDirectories = [];
if (ticks.ToString().Last() == '0')
ticks += 1;
ReadOnlyCollection<PersonContainer> personContainers;
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot);
Models.Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration);
_Logger?.LogInformation(propertyConfiguration.RootDirectory);
@ -119,8 +128,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
{
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $") Building People Collection - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(1, message, options);
using ProgressBar progressBar = new(1, message, _ProgressBarOptions);
progressBar.Tick();
string peopleRootDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(A2_People));
string? rootResultsDirectory = Path.GetDirectoryName(Path.GetDirectoryName(peopleRootDirectory)) ?? throw new Exception();
@ -165,8 +173,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
_Logger?.LogInformation("First run completed. Run again if wanted");
}
private int FullParallelForWork(A_Property propertyLogic,
B_Metadata metadata,
private int FullParallelForWork(B_Metadata metadata,
MapLogic mapLogic,
string outputResolution,
bool outputResolutionHasNumber,
@ -174,40 +181,23 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string d2ResultsFullGroupDirectory,
List<Tuple<string, DateTime>> sourceDirectoryChanges,
Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection,
Record record,
Container.Models.Container container,
Item item,
DateTime[] containerDateTimes,
bool? isFocusRelativePath)
{
int result = 0;
bool? shouldIgnore;
List<Shared.Models.Face> faces;
long ticks = DateTime.Now.Ticks;
DateTime dateTime = DateTime.Now;
Shared.Models.Property? property;
List<string> parseExceptions = [];
string[] changesFrom = [nameof(A_Property)];
List<Tuple<string, DateTime>> subFileTuples = [];
FileHolder resizedFileHolder = _Resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber);
if (item.Property is null || item.Property.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any())
{
LogItemPropertyIsNull(item);
int? propertyHashCode = item.Property?.GetHashCode();
property = propertyLogic.GetProperty(metadata, item, subFileTuples, parseExceptions);
item.Update(property);
if (propertyHashCode is null)
{
lock (sourceDirectoryChanges)
sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
}
else if (propertyHashCode.Value != property.GetHashCode())
{
lock (sourceDirectoryChanges)
sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
}
}
else
{
property = item.Property;
if (item.ExifDirectory is null || item.ExifDirectory.FilePath.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any())
throw new Exception();
if (_Configuration.PropertyConfiguration.ForcePropertyLastWriteTimeToCreationTime && item.SourceDirectoryFileHolder.LastWriteTime.Value != item.SourceDirectoryFileHolder.CreationTime.Value)
{
File.SetLastWriteTime(item.SourceDirectoryFileHolder.FullName, item.SourceDirectoryFileHolder.CreationTime.Value);
@ -217,16 +207,21 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), item.SourceDirectoryFileHolder.LastWriteTime.Value));
else
subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), new FileInfo(item.SourceDirectoryFileHolder.FullName).LastWriteTime));
if (resizedFileHolder.Exists && item.Property.Width is not null && item.Property.Width.Value > 4 && _Configuration.SaveBlurHashForOutputResolutions.Contains(outputResolution))
if (resizedFileHolder.Exists && item.ExifDirectory.Width is not null && item.ExifDirectory.Width.Value > 4 && _Configuration.SaveBlurHashForOutputResolutions.Contains(outputResolution))
{
string? file = _BlurHasher.GetFile(item.FilePath);
if (file is not null && !File.Exists(file))
_ = _BlurHasher.EncodeAndSave(item.FilePath, resizedFileHolder);
}
}
bool? shouldIgnore = property is null || property.Keywords is null ? null : _Configuration.PropertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l));
if (shouldIgnore is not null)
if (item.FilePath.Id is null || !record.ExifDirectoriesById.TryGetValue(item.FilePath.Id.Value, out ExifDirectory? exifDirectory))
{
shouldIgnore = null;
exifDirectory = null;
}
else
{
ReadOnlyCollection<string> keywords = IMetaBase.GetKeywords(exifDirectory.ExifBaseDirectories);
shouldIgnore = _Configuration.PropertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains);
if (shouldIgnore.Value)
{
FileInfo fileInfo = new(resizedFileHolder.FullName);
@ -241,11 +236,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
throw new NotSupportedException($"Rename File! <{item.FilePath.FileNameFirstSegment}>");
}
}
if (property is null || item.Property is null)
throw new NullReferenceException(nameof(property));
item.SetResizedFileHolder(_Resize.FileNameExtension, resizedFileHolder);
MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem);
exifDirectory ??= metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem);
if (_AppSettings.Places.Count > 0)
{
float latitude;
@ -268,13 +261,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
distance += 1;
}
}
Dictionary<string, int[]> outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.Property, mappingFromItem);
Dictionary<string, int[]> outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem);
if (_Configuration.SaveResizedSubfiles)
{
if (shouldIgnore is not null && item.FilePath.HasIgnoreKeyword is not null && item.FilePath.HasIgnoreKeyword.Value != shouldIgnore.Value)
faces = [];
else
_Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize);
_Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize);
}
if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution))
faces = [];
@ -286,7 +279,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection))
mappingFromPhotoPrismCollection = null;
bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution);
faces = _Faces.GetFaces(outputResolution, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection);
faces = _Faces.GetFaces(outputResolution, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection);
result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces);
List<(Shared.Models.Face, FileHolder?, string, bool Saved)> faceCollection = _Faces.SaveFaces(item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces);
if (move && faceCollection.All(l => !l.Saved))
@ -319,7 +312,6 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
}
private (int, bool) FullParallelWork(int maxDegreeOfParallelism,
A_Property propertyLogic,
B_Metadata metadata,
MapLogic mapLogic,
string outputResolution,
@ -328,6 +320,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string d2ResultsFullGroupDirectory,
List<Tuple<string, DateTime>> sourceDirectoryChanges,
Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection,
Record record,
Container.Models.Container container,
ReadOnlyCollection<Item> filteredItems,
string message)
@ -336,17 +329,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
int exceptionsCount = 0;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
DateTime[] containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems);
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
string focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory));
bool? isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath);
bool anyPropertiesChangedForX = _Configuration.PropertyConfiguration.PropertiesChangedForProperty || _Configuration.PropertiesChangedForDistance || _Configuration.PropertiesChangedForFaces || _Configuration.PropertiesChangedForIndex || _Configuration.PropertiesChangedForMetadata || _Configuration.PropertiesChangedForResize;
using ProgressBar progressBar = new(filteredItems.Count, message, options);
using ProgressBar progressBar = new(filteredItems.Count, message, _ProgressBarOptions);
_ = Parallel.For(0, filteredItems.Count, parallelOptions, (i, state) =>
{
try
{
result += FullParallelForWork(propertyLogic,
metadata,
result += FullParallelForWork(metadata,
mapLogic,
outputResolution,
outputResolutionHasNumber,
@ -354,6 +345,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
d2ResultsFullGroupDirectory,
sourceDirectoryChanges,
fileNameToCollection,
record,
container,
filteredItems[i],
containerDateTimes,
@ -371,6 +363,98 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
return (result, exceptionsCount > 0);
}
private void FullDoWork(string argZero,
string propertyRoot,
long ticks,
string fPhotoPrismSingletonDirectory,
int count,
B_Metadata metadata,
Record record,
ReadOnlyCollection<Container.Models.Container> readOnlyContainers,
MapLogic mapLogic)
{
int total;
int notMapped;
string message;
bool exceptions;
int totalSeconds;
int totalNotMapped = 0;
IDlibDotNet dlibDotNet = this;
bool outputResolutionHasNumber;
bool anyNullOrNoIsUniqueFileName;
string cResultsFullGroupDirectory;
string dResultsFullGroupDirectory;
string c2ResultsFullGroupDirectory;
string d2ResultsFullGroupDirectory;
Container.Models.Container container;
ReadOnlyCollection<Item> filteredItems;
List<Tuple<string, DateTime>> sourceDirectoryChanges = [];
int maxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism;
Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection = !Directory.Exists(fPhotoPrismSingletonDirectory) ? fileNameToCollection = [] : F_PhotoPrism.GetFileNameToCollection(fPhotoPrismSingletonDirectory);
string dResultsDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(D_Face));
foreach (string outputResolution in _Configuration.OutputResolutions)
{
total = 0;
outputResolutionHasNumber = outputResolution.Any(char.IsNumber);
(cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution);
_Faces.Update(dResultsFullGroupDirectory);
_Resize.Update(cResultsFullGroupDirectory);
_FaceParts.Update(d2ResultsFullGroupDirectory);
_BlurHasher.Update(c2ResultsFullGroupDirectory);
for (int i = 0; i < readOnlyContainers.Count; i++)
{
container = readOnlyContainers[i];
if (container.Items.Count == 0)
continue;
if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero))
continue;
filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, container);
if (filteredItems.Count == 0)
continue;
sourceDirectoryChanges.Clear();
anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName);
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{totalSeconds} total second(s) - {outputResolution} - {i + 1:000} / {readOnlyContainers.Count:000} - {total} / {count} total - <{container.SourceDirectory}> [{filteredItems.Count:000}] - total not mapped {totalNotMapped:000000}";
if (outputResolutionHasNumber)
_Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory);
(notMapped, exceptions) = FullParallelWork(maxDegreeOfParallelism,
metadata,
mapLogic,
outputResolution,
outputResolutionHasNumber,
cResultsFullGroupDirectory,
d2ResultsFullGroupDirectory,
sourceDirectoryChanges,
fileNameToCollection,
record,
container,
filteredItems,
message);
totalNotMapped += notMapped;
if (exceptions)
{
_Exceptions.Add(container.SourceDirectory);
continue;
}
if (Directory.GetFiles(propertyRoot, "*.txt", SearchOption.TopDirectoryOnly).Length > 0)
{
for (int y = 0; y < int.MaxValue; y++)
{
_Logger?.LogInformation("Press \"Y\" key when ready to continue or close console");
if (_Console.ReadKey() == ConsoleKey.Y)
break;
}
_Logger?.LogInformation(". . .");
}
total += container.Items.Count;
}
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{totalSeconds} total second(s) - {outputResolution} - ### [###] / {readOnlyContainers.Count:000} - {total} / {count} total - <> - total not mapped {totalNotMapped:000000}";
using ProgressBar progressBar = new(1, message, _ProgressBarOptions);
progressBar.Tick();
}
}
void IDlibDotNet.Tick() =>
_ProgressBar?.Tick();
@ -399,6 +483,12 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory);
}
void IDlibDotNet.ConstructProgressBar(int maxTicks, string message)
{
_ProgressBar?.Dispose();
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
}
(string, string, string, string) IDlibDotNet.GetResultsFullGroupDirectories(string outputResolution)
{
string cResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
@ -466,7 +556,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
if (filePath.Id is null)
throw new Exception();
string[] directoryNames = keyValuePair.Value.Select(l => l.DirectoryFullPath.Replace('\\', '/')).ToArray();
string paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, index: null);
string paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, index: null);
result = new(directoryNames, filePath.ExtensionLowered, filePath.HasDateTimeOriginal, filePath.Id.Value, filePath.Length, paddedId, filePath.LastWriteTicks);
return result;
}
@ -513,6 +603,22 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
throw new NullReferenceException(nameof(configuration.ModelDirectory));
}
private static ExifDirectory? GetExifDirectory(FilePair filePair)
{
ExifDirectory? result;
if (filePair.Match is null)
result = null;
else
{
string json = File.ReadAllText(filePair.Match.FullName);
if (string.IsNullOrEmpty(json))
result = null;
else
result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
}
return result;
}
private static void DeleteContinueFiles(ReadOnlyCollection<PersonContainer> personContainers)
{
foreach (PersonContainer personContainer in personContainers)
@ -554,7 +660,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
}
}
private static bool IsFilesCollectionCountIsOne(ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
private static bool GetFilesCollectionCountIsOne(ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
{
bool result = true;
int count = 0;
@ -582,11 +688,11 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
foreach (Item item in distinctFilteredItems)
{
found = false;
if (item.Property?.Id is null)
if (item.ExifDirectory?.FilePath.Id is null)
continue;
foreach (Mapping mapping in distinctFilteredMappingCollection)
{
if (mapping.MappingFromItem.Id != item.Property.Id.Value)
if (mapping.MappingFromItem.Id != item.ExifDirectory.FilePath.Id.Value)
continue;
found = true;
break;
@ -601,13 +707,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string fileName;
string directory;
bool? isWrongYear;
List<DateTime> dateTimes;
DateTime? dateTime;
string? exifDirectoryModel;
List<string> distinct = [];
WindowsShortcut windowsShortcut;
ReadOnlyCollection<DateTime> dateTimes;
List<(string, string, string)> collection = [];
foreach (Item item in distinctValidImageItems)
{
if (item.Property?.Id is null)
if (item.ExifDirectory?.FilePath.Id is null)
continue;
if (item.IsNotUniqueAndNeedsReview is null || !item.IsNotUniqueAndNeedsReview.Value)
continue;
@ -620,15 +728,19 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
}
foreach (Item item in distinctValidImageItems)
{
if (item.Property?.Id is null || item.Property.DateTimeOriginal is null)
if (item.ExifDirectory?.FilePath.Id is null)
continue;
dateTimes = item.Property.GetDateTimes();
(isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes);
dateTime = IDate.GetDateTimeOriginal(item.ExifDirectory);
if (dateTime is null)
continue;
dateTimes = IDate.GetDateTimes(item.ExifDirectory);
(isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, dateTime, dateTimes.ToList());
if (isWrongYear is null || !isWrongYear.Value)
continue;
// Remove-Item -LiteralPath "\\?\D:\Tmp\a\EX-Z70 "
model = string.IsNullOrEmpty(item.Property.Model) ? "Unknown" : CameraRegex().Replace(item.Property.Model.Trim(), "_");
directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Item)})", item.Property.DateTimeOriginal.Value.Year.ToString(), model);
exifDirectoryModel = IMetaBase.GetModel(item.ExifDirectory?.ExifBaseDirectories);
model = string.IsNullOrEmpty(exifDirectoryModel) ? "Unknown" : CameraRegex().Replace(exifDirectoryModel.Trim(), "_");
directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Item)})", dateTime.Value.Year.ToString(), model);
fileName = item.IsNotUniqueAndNeedsReview is not null && item.IsNotUniqueAndNeedsReview.Value ? Path.Combine(directory, $"{item.FilePath.Name} {item.FilePath.Length}.lnk") : Path.Combine(directory, $"{item.FilePath.Name}.lnk");
collection.Add((item.FilePath.FullName, directory, fileName));
if (distinct.Contains(directory))
@ -797,15 +909,19 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
return results;
}
private bool? GetIsFocusModel(Shared.Models.Property? property)
private bool? GetIsFocusModel(ExifDirectory? exifDirectory)
{
bool? result;
if (string.IsNullOrEmpty(_Configuration.FocusModel))
result = null;
else if (property is null || string.IsNullOrEmpty(property.Model))
else
{
string? model = IMetaBase.GetModel(exifDirectory?.ExifBaseDirectories);
if (exifDirectory is null || string.IsNullOrEmpty(model))
result = null;
else
result = property.Model.Contains(_Configuration.FocusModel);
result = model.Contains(_Configuration.FocusModel);
}
return result;
}
@ -867,7 +983,39 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
return result;
}
private bool GetRunToDoCollectionFirst(Models.Configuration configuration, long ticks)
private ReadOnlyCollection<FilePath> GetFilePath(long ticks, 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;
foreach (string file in files)
{
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(file);
filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
if (skipOlderThan is not null && (fileHolder.LastWriteTime is null || fileHolder.LastWriteTime.Value.Ticks < skipOlderThan.Value))
continue;
results.Add(filePath);
}
return results.AsReadOnly();
}
private void ParallelFor(IDlibDotNet dlibDotNet, FilePair filePair, Dictionary<int, ExifDirectory> results)
{
dlibDotNet?.Tick();
if (filePair.FilePath.Id is null || results.ContainsKey(filePair.FilePath.Id.Value))
return;
ExifDirectory? exifDirectory = GetExifDirectory(filePair);
if (exifDirectory is null)
return;
lock (results)
results.Add(filePair.FilePath.Id.Value, exifDirectory);
}
private bool GetRunToDoCollectionFirst(Models.Configuration configuration, long ticks, string[] checkDirectories)
{
bool result = configuration.SaveSortingWithoutPerson;
if (!result)
@ -887,20 +1035,16 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent);
(int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
FileSystemInfo fileSystemInfo = new DirectoryInfo(eDistanceContentDirectory);
string[] checkDirectories =
[
Path.Combine(rootDirectory, "Ancestry"),
Path.Combine(rootDirectory, "Facebook"),
Path.Combine(rootDirectory, "LinkedIn")
];
foreach (string checkDirectory in checkDirectories)
{
if (!Directory.Exists(checkDirectory))
result = true;
if (checkDirectory == rootDirectory)
seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName}");
else
seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}");
if (!Directory.Exists(seasonDirectory))
_ = Directory.CreateDirectory(seasonDirectory);
result = true;
if (result)
continue;
directories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly);
@ -923,32 +1067,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
return result;
}
private ReadOnlyCollection<FilePath> GetFilePath(long ticks, 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;
foreach (string file in files)
{
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(file);
filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
if (skipOlderThan is not null && (fileHolder.LastWriteTime is null || fileHolder.LastWriteTime.Value.Ticks < skipOlderThan.Value))
continue;
results.Add(filePath);
}
return results.AsReadOnly();
}
private void Search(long ticks, ReadOnlyCollection<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);
string eDistanceContentDirectory;
string? a2PeopleContentDirectory;
string aResultsFullGroupDirectory;
@ -957,23 +1084,24 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string fPhotoPrismContentDirectory;
const string fileSearchFilter = "*";
string fPhotoPrismSingletonDirectory;
bool filesCollectionCountIsOne = false;
const string directorySearchFilter = "*";
string? filesCollectionRootDirectory = null;
bool configurationOutputResolutionsHas = false;
ReadOnlyDictionary<long, List<int>> personKeyToIds;
ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers = null;
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs = null;
ReadOnlyCollection<ReadOnlyCollection<FilePath>>? filePathsCollection = null;
bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks);
(int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
string[] checkDirectories =
[
Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Ancestry"),
Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Facebook"),
Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "LinkedIn")
];
bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks, checkDirectories);
(aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories();
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(_Configuration.PropertyConfiguration.RootDirectory, ticks);
a2PeopleContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), "([])");
B_Metadata metadata = new(_Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory);
eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent);
string a2PeopleSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), _Configuration.PropertyConfiguration.ResultSingleton);
_ = Directory.CreateDirectory(Path.Combine(eDistanceContentDirectory, ticks.ToString()));
B_Metadata metadata = new(dlibDotNet, _Configuration.PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, ticks, bResultsFullGroupDirectory);
if (runToDoCollectionFirst)
mapLogic = null;
else
@ -985,18 +1113,16 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
configurationOutputResolutionsHas = true;
if (!runToDoCollectionFirst)
break;
(filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne) = GetFilesCollectionThenCopyOrMove(ticks, fileSearchFilter, directorySearchFilter, options, outputResolution);
keyValuePairs = FilePath.GetKeyValuePairs(filePathsCollection);
splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, keyValuePairs);
record = GetFilesCollectionThenCopyOrMove(dlibDotNet, ticks, fileSearchFilter, directorySearchFilter, bResultsFullGroupDirectory, outputResolution);
break;
}
fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent);
fPhotoPrismSingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultSingleton);
propertyLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _Resize.FileNameExtension, _Configuration.Reverse, aResultsFullGroupDirectory);
if (filesCollectionCountIsOne)
if (record is not null && record.FilesCollectionCountIsOne)
{
if (filePathsCollection is null)
throw new NullReferenceException(nameof(filePathsCollection));
if (record.FilePathsCollection is null)
throw new NullReferenceException(nameof(record.FilePathsCollection));
string resultsGroupDirectory;
a2PeopleContentDirectory = null;
eDistanceContentDirectory = Path.Combine($"{Path.GetPathRoot(argZero)}", _Configuration.PropertyConfiguration.ResultContent);
@ -1007,7 +1133,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
resultsGroupDirectory = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, string.Empty, create: true);
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(resultsGroupDirectory);
}
argZero = SaveUrlAndGetNewRootDirectory(filePathsCollection.First());
argZero = SaveUrlAndGetNewRootDirectory(record.FilePathsCollection.First());
_Configuration.PropertyConfiguration.ChangeRootDirectory(argZero);
(aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories();
propertyRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), create: false);
@ -1019,24 +1145,55 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
{
if (outputResolution.Any(char.IsNumber))
continue;
Dictionary<int, ExifDirectory> exifDirectoriesById = [];
(cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution);
filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent);
filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useCeilingAverage: true);
string? filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent);
ReadOnlyCollection<ReadOnlyCollection<FilePath>>? filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: true);
record = new(FilesCollectionRootDirectory: filesCollectionRootDirectory,
FilesCollectionCountIsOne: false,
FilePathsCollection: filePathsCollection,
ExifDirectoriesById: new(exifDirectoriesById),
IdToFilePaths: null,
SplatNineIdentifiers: null);
break;
}
}
if (filesCollectionRootDirectory is null || filePathsCollection is null)
throw new NullReferenceException(nameof(filePathsCollection));
int count = filePathsCollection.Select(l => l.Count).Sum();
if (string.IsNullOrEmpty(record?.FilesCollectionRootDirectory) || record.FilePathsCollection.Count == 0)
throw new NullReferenceException(nameof(record.FilePathsCollection));
foreach (string checkDirectory in checkDirectories)
{
seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}");
if (!Directory.Exists(seasonDirectory))
_ = Directory.CreateDirectory(seasonDirectory);
}
int count = record.FilePathsCollection.Select(l => l.Count).Sum();
message = $") Building Container(s) - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
_ProgressBar = new(count, message, options);
ReadOnlyCollection<Container.Models.Container> readOnlyContainers = Container.Models.Stateless.Methods.IContainer.GetContainers(this, _Configuration.PropertyConfiguration, _Faces.FileNameExtension, _Faces.HiddenFileNameExtension, eDistanceContentDirectory, filesCollectionRootDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection);
_ProgressBar = new(count, message, _ProgressBarOptions);
ReadOnlyCollection<Container.Models.Container> readOnlyContainers =
Container.Models.Stateless.Methods.IContainer.GetContainers(dlibDotNet,
_Configuration.PropertyConfiguration,
_Faces.FileNameExtension,
_Faces.HiddenFileNameExtension,
eDistanceContentDirectory,
record.FilesCollectionRootDirectory,
record.IdToFilePaths,
record.SplatNineIdentifiers,
record.FilePathsCollection,
record.ExifDirectoriesById);
_ProgressBar.Dispose();
mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory);
DeleteContinueFiles(personContainers);
if (!runToDoCollectionFirst)
MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory, options);
FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, fPhotoPrismSingletonDirectory, count, metadata, readOnlyContainers, propertyLogic, mapLogic);
MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory);
FullDoWork(argZero,
propertyRoot,
ticks,
fPhotoPrismSingletonDirectory,
count,
metadata,
record,
readOnlyContainers,
mapLogic);
ReadOnlyCollection<Item> distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true);
if (_Configuration.LookForAbandoned)
{
@ -1044,9 +1201,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string d2ResultsFullGroupDirectory;
foreach (string outputResolution in _Configuration.OutputResolutions)
{
_ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), options);
_ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), _ProgressBarOptions);
(cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution);
mapLogic.LookForAbandoned(this, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory);
mapLogic.LookForAbandoned(dlibDotNet, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory);
_ProgressBar.Dispose();
}
}
@ -1077,8 +1234,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
&& _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)
&& _Exceptions.Count == 0)
MapLogic(ticks, readOnlyContainers, fPhotoPrismContentDirectory, mapLogic, outputResolution, new(personKeyToIds), distinctValidImageFaces, distinctValidImageMappingCollection);
if (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && splatNineIdentifiers is not null && distinctValidImageMappingCollection.Count > 0)
_Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.ImmichOwnerId, _Configuration.ImmichRoot, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, splatNineIdentifiers, distinctValidImageMappingCollection);
if (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && record?.SplatNineIdentifiers is not null && distinctValidImageMappingCollection.Count > 0)
_Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.ImmichOwnerId, _Configuration.ImmichRoot, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, record.SplatNineIdentifiers, distinctValidImageMappingCollection);
if (_IsEnvironment.Development)
continue;
if (!_IsEnvironment.Development)
@ -1096,19 +1253,37 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
}
}
private (string, ReadOnlyCollection<ReadOnlyCollection<FilePath>>, bool) GetFilesCollectionThenCopyOrMove(long ticks, string fileSearchFilter, string directorySearchFilter, ProgressBarOptions options, string outputResolution)
private Record GetFilesCollectionThenCopyOrMove(IDlibDotNet dlibDotNet, long ticks, string fileSearchFilter, string directorySearchFilter, string bResultsFullGroupDirectory, string outputResolution)
{
Record result;
int count;
string message;
ProgressBar progressBar;
IDlibDotNet dlibDotNet = this;
const string extension = ".json";
ReadOnlyCollection<FilePair> filePairs;
int maxDegreeOfParallelism = Environment.ProcessorCount;
Dictionary<int, ExifDirectory> exifDirectoriesById = [];
string filesCollectionRootDirectory = _Configuration.PropertyConfiguration.RootDirectory;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
(string cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution);
IReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> fileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, [_Configuration.PropertyConfiguration.ResultContent]);
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useCeilingAverage: false);
int count = filePathsCollection.Select(l => l.Count).Sum();
bool filesCollectionCountIsOne = IsFilesCollectionCountIsOne(filePathsCollection);
string message = $") Selecting for ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
progressBar = new(count, message, options);
(string[] distinctDirectories, List<(FilePath, string)> toDoCollection) = IDirectory.GetToDoCollection(_Configuration.PropertyConfiguration, filePathsCollection, fileGroups, () => progressBar.Tick());
string jsonGroupDirectory = Path.Combine(bResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultSingleton);
if (!Directory.Exists(jsonGroupDirectory))
_ = Directory.CreateDirectory(jsonGroupDirectory);
IReadOnlyDictionary<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);
count = filePathsCollection.Select(l => l.Count).Sum();
filePairs = IFilePair.GetFilePairs(_Configuration.PropertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection);
message = $") Preloading ExifDirectory Dictionary - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
progressBar = new(count, message, _ProgressBarOptions);
_ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(dlibDotNet, filePairs[i], exifDirectoriesById));
progressBar.Dispose();
count = filePathsCollection.Select(l => l.Count).Sum();
bool filesCollectionCountIsOne = GetFilesCollectionCountIsOne(filePathsCollection);
message = $") Selecting for ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
progressBar = new(count, message, _ProgressBarOptions);
(string[] distinctDirectories, List<(FilePath, string)> toDoCollection) = IDirectory.GetToDoCollection(_Configuration.PropertyConfiguration, filePathsCollection, fileGroups, exifDirectoriesById, () => progressBar.Tick());
progressBar.Dispose();
foreach (string distinctDirectory in distinctDirectories)
{
@ -1116,13 +1291,21 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
_ = Directory.CreateDirectory(distinctDirectory);
}
message = $") Copying to ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
progressBar = new(count, message, options);
progressBar = new(count, message, _ProgressBarOptions);
_ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, () => progressBar.Tick());
progressBar.Dispose();
return (filesCollectionRootDirectory, filePathsCollection, filesCollectionCountIsOne);
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection);
ReadOnlyDictionary<int, Identifier> splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, idToFilePaths);
result = new(ExifDirectoriesById: new(exifDirectoriesById),
FilesCollectionRootDirectory: filesCollectionRootDirectory,
FilesCollectionCountIsOne: filesCollectionCountIsOne,
FilePathsCollection: filePathsCollection,
IdToFilePaths: idToFilePaths,
SplatNineIdentifiers: splatNineIdentifiers);
return result;
}
private void MapFaceFileLogic(long ticks, ReadOnlyCollection<PersonContainer> personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory, ProgressBarOptions options)
private void MapFaceFileLogic(long ticks, ReadOnlyCollection<PersonContainer> personContainers, MapLogic mapLogic, string? a2PeopleContentDirectory, string eDistanceContentDirectory)
{
foreach (string outputResolution in _Configuration.OutputResolutions)
{
@ -1140,7 +1323,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
continue;
if (!_Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution))
continue;
List<SaveContainer> saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, options, mapLogic, outputResolution);
List<SaveContainer> saveContainers = GetSaveContainers(ticks, personContainers, a2PeopleContentDirectory, eDistanceContentDirectory, mapLogic, outputResolution);
if (saveContainers.Count > 0)
{
int updated = 0;
@ -1150,7 +1333,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
}
}
private List<SaveContainer> GetSaveContainers(long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, ProgressBarOptions options, MapLogic mapLogic, string outputResolution)
private List<SaveContainer> GetSaveContainers(long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, MapLogic mapLogic, string outputResolution)
{
List<SaveContainer> results;
IDlibDotNet dlibDotNet = this;
@ -1179,8 +1362,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
else
{
string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
_ProgressBar = new(postFiltered.Count, message, options);
ReadOnlyCollection<LocationContainer> matrix = E_Distance.GetMatrixLocationContainers(this, _MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered);
_ProgressBar = new(postFiltered.Count, message, _ProgressBarOptions);
ReadOnlyCollection<LocationContainer> matrix = E_Distance.GetMatrixLocationContainers(dlibDotNet, _MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered);
_ProgressBar.Dispose();
ReadOnlyDictionary<string, LocationContainer> onlyOne = GetOnlyOne(distanceLimits, matrix);
if (onlyOne.Count == 0)
@ -1195,92 +1378,6 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
return results;
}
private void FullDoWork(string argZero, string propertyRoot, long ticks, string aResultsFullGroupDirectory, string fPhotoPrismSingletonDirectory, int count, B_Metadata metadata, ReadOnlyCollection<Container.Models.Container> readOnlyContainers, A_Property propertyLogic, MapLogic mapLogic)
{
int total;
int notMapped;
string message;
bool exceptions;
int totalSeconds;
int totalNotMapped = 0;
IDlibDotNet dlibDotNet = this;
bool outputResolutionHasNumber;
bool anyNullOrNoIsUniqueFileName;
string cResultsFullGroupDirectory;
string dResultsFullGroupDirectory;
string c2ResultsFullGroupDirectory;
string d2ResultsFullGroupDirectory;
Container.Models.Container container;
ReadOnlyCollection<Item> filteredItems;
List<Tuple<string, DateTime>> sourceDirectoryChanges = [];
int maxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism;
Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection = !Directory.Exists(fPhotoPrismSingletonDirectory) ? fileNameToCollection = [] : F_PhotoPrism.GetFileNameToCollection(fPhotoPrismSingletonDirectory);
string dResultsDateGroupDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(D_Face));
foreach (string outputResolution in _Configuration.OutputResolutions)
{
total = 0;
outputResolutionHasNumber = outputResolution.Any(char.IsNumber);
(cResultsFullGroupDirectory, c2ResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution);
_Faces.Update(dResultsFullGroupDirectory);
_Resize.Update(cResultsFullGroupDirectory);
_FaceParts.Update(d2ResultsFullGroupDirectory);
_BlurHasher.Update(c2ResultsFullGroupDirectory);
for (int i = 0; i < readOnlyContainers.Count; i++)
{
container = readOnlyContainers[i];
if (container.Items.Count == 0)
continue;
if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero))
continue;
filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, container);
if (filteredItems.Count == 0)
continue;
sourceDirectoryChanges.Clear();
anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName);
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{totalSeconds} total second(s) - {outputResolution} - {i + 1:000} / {readOnlyContainers.Count:000} - {total} / {count} total - <{container.SourceDirectory}> [{filteredItems.Count:000}] - total not mapped {totalNotMapped:000000}";
propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, container.SourceDirectory, anyNullOrNoIsUniqueFileName);
if (outputResolutionHasNumber)
_Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory);
(notMapped, exceptions) = FullParallelWork(maxDegreeOfParallelism,
propertyLogic,
metadata,
mapLogic,
outputResolution,
outputResolutionHasNumber,
cResultsFullGroupDirectory,
d2ResultsFullGroupDirectory,
sourceDirectoryChanges,
fileNameToCollection,
container,
filteredItems,
message);
totalNotMapped += notMapped;
if (exceptions)
{
_Exceptions.Add(container.SourceDirectory);
continue;
}
if (Directory.GetFiles(propertyRoot, "*.txt", SearchOption.TopDirectoryOnly).Length > 0)
{
for (int y = 0; y < int.MaxValue; y++)
{
_Logger?.LogInformation("Press \"Y\" key when ready to continue or close console");
if (_Console.ReadKey() == ConsoleKey.Y)
break;
}
_Logger?.LogInformation(". . .");
}
total += container.Items.Count;
}
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{totalSeconds} total second(s) - {outputResolution} - ### [###] / {readOnlyContainers.Count:000} - {total} / {count} total - <> - total not mapped {totalNotMapped:000000}";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(1, message, options);
progressBar.Tick();
}
}
private ReadOnlyCollection<Mapping> GetMappings(Property.Models.Configuration propertyConfiguration, string eDistanceContentDirectory, ReadOnlyCollection<Container.Models.Container> readOnlyContainers, MapLogic mapLogic, bool distinctItems)
{
List<Mapping> results = [];
@ -1306,14 +1403,14 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath);
foreach (Item item in filteredItems)
{
if (item.Property?.Id is null || item.ResizedFileHolder is null)
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
continue;
mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, item.ResizedFileHolder);
if (distinctItems)
{
if (distinct.Contains(item.Property.Id.Value))
if (distinct.Contains(item.ExifDirectory.FilePath.Id.Value))
continue;
distinct.Add(item.Property.Id.Value);
distinct.Add(item.ExifDirectory.FilePath.Id.Value);
}
count++;
anyValidFaces = false;
@ -1345,7 +1442,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
bool? eyeReview = null;
int confidencePercent = 0;
int faceAreaPermyriad = 0;
bool? isFocusModel = GetIsFocusModel(item.Property);
bool? isFocusModel = GetIsFocusModel(item.ExifDirectory);
long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray();
int wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(Shared.Models.Stateless.ILocation.Digits);
string deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, Shared.Models.Stateless.ILocation.Digits);
@ -1501,12 +1598,12 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
MappingFromLocation? mappingFromLocation;
MappingFromFilterPre mappingFromFilterPre;
MappingFromFilterPost mappingFromFilterPost;
bool? isFocusModel = GetIsFocusModel(item.Property);
ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.Property?.Id);
bool? isFocusModel = GetIsFocusModel(item.ExifDirectory);
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.Property?.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
if (item.ExifDirectory?.FilePath.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
{
canReMap = null;
isFocusPerson = null;
@ -1529,7 +1626,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle);
inSkipCollection = mapLogic.InSkipCollection(item.Property.Id.Value, mappingFromLocation);
inSkipCollection = mapLogic.InSkipCollection(item.ExifDirectory.FilePath.Id.Value, mappingFromLocation);
mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath);
canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);
isFocusPerson = mapLogic.IsFocusPerson(_Configuration.SkipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);

View File

@ -55,7 +55,6 @@
<ProjectReference Include="..\Metadata\Metadata.csproj" />
<ProjectReference Include="..\PhotoPrism\PhotoPrism.csproj" />
<ProjectReference Include="..\Property-Compare\Property-Compare.csproj" />
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Resize\Resize.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
</ItemGroup>

View File

@ -320,12 +320,12 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic
public ReadOnlyCollection<LocationContainer> GetLocationContainers(Item item)
{
LocationContainer[] results;
if (item.Property?.Id is null)
if (item.ExifDirectory?.FilePath.Id is null)
results = [];
else
{
List<LocationContainer>? locationContainers;
if (_IdToLocationContainers.TryGetValue(item.Property.Id.Value, out locationContainers))
if (_IdToLocationContainers.TryGetValue(item.ExifDirectory.FilePath.Id.Value, out locationContainers))
results = locationContainers.ToArray();
else
results = [];

View File

@ -110,7 +110,7 @@ internal abstract class MapLogic
List<Face> results = [];
foreach (Item item in items)
{
if (item.Property?.Id is null || item.ResizedFileHolder is null)
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
continue;
foreach (Face face in item.Faces)
{

View File

@ -4,6 +4,7 @@ using System.Text.Json;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Methods;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Metadata.Models;
@ -13,30 +14,28 @@ namespace View_by_Distance.Metadata.Models;
public class B_Metadata : IMetadata<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; }
// First
// Set DateGroupDirectory
// Create a directories for Singleton
// Populate Collection
// Populate Singleton
// Populate existing ExifDirectories
// Run similar for resize
// Second
// Populate needed ExifDirectories Dictionary can't be init only
private readonly bool _PropertiesChangedForMetadata;
private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly bool _ForceMetadataLastWriteTimeToCreationTime;
private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups;
public B_Metadata(IPropertyConfiguration propertyConfiguration)
{
_PropertiesChangedForMetadata = false;
_PropertyConfiguration = propertyConfiguration;
_ForceMetadataLastWriteTimeToCreationTime = false;
_ResultSingletonFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
_WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, null, [propertyConfiguration.ResultSingleton]);
foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
{
if (keyValuePair.Key == _PropertyConfiguration.ResultSingleton)
_ResultSingletonFileGroups[0] = keyValuePair.Value;
else
throw new Exception();
}
}
public B_Metadata(IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, string bResultsFullGroupDirectory)
public B_Metadata(IDlibDotNet? dlibDotNet, IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, long ticks, string bResultsFullGroupDirectory)
{
_PropertyConfiguration = propertyConfiguration;
_PropertiesChangedForMetadata = propertiesChangedForMetadata;
@ -51,33 +50,70 @@ 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 = [];
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)";
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];
}
public override string ToString()
private void ParallelFor(IDlibDotNet? dlibDotNet, FilePair filePair, List<ExifDirectory> results)
{
string result = JsonSerializer.Serialize(this, _WriteIndentedJsonSerializerOptions);
dlibDotNet?.Tick();
if (filePair.FilePath.Id is null)
return;
ExifDirectory? exifDirectory = GetExifDirectory(filePair);
if (exifDirectory is null)
return;
lock (results)
results.Add(exifDirectory);
}
private static ExifDirectory? GetExifDirectory(FilePair filePair)
{
ExifDirectory? result;
if (filePair.Match is null)
result = null;
else
{
string json = File.ReadAllText(filePair.Match.FullName);
if (string.IsNullOrEmpty(json))
result = null;
else
result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
}
return result;
}
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo)
{
string[] segments = directory.Split(cei.Combined);
string? checkDirectory = segments.Length == 1 ?
Path.Combine(segments[0], $"{cei.Combined[2..]}") :
segments.Length == 2 ?
$"{segments[0]}{cei.Combined[2..]}{segments[1]}" :
null;
if (checkDirectory is not null && System.IO.Directory.Exists(checkDirectory))
{
string checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
{
File.Move(checkFile, fileInfo.FullName);
fileInfo.Refresh();
}
}
}
public ExifDirectory GetMetadataCollection(FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, string[] changesFrom, MappingFromItem mappingFromItem)
{
ExifDirectory? result = null;
@ -142,6 +178,25 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
return result;
}
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo)
{
string[] segments = directory.Split(cei.Combined);
string? checkDirectory = segments.Length == 1 ?
Path.Combine(segments[0], $"{cei.Combined[2..]}") :
segments.Length == 2 ?
$"{segments[0]}{cei.Combined[2..]}{segments[1]}" :
null;
if (checkDirectory is not null && System.IO.Directory.Exists(checkDirectory))
{
string checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
{
File.Move(checkFile, fileInfo.FullName);
fileInfo.Refresh();
}
}
}
(DateTime?, DateTime?[]) IMetadata<MetadataExtractor.Directory>.GetDateTimes(FilePath filePath, IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<DateTime?> results = [];

View File

@ -66,6 +66,7 @@ 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;
@ -111,6 +112,7 @@ 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 = [];
@ -140,6 +142,7 @@ public class A_Property
});
}
[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;
@ -148,16 +151,17 @@ public class A_Property
string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}");
if (item.IsValidImageFormatExtension && item.FilePath.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.FilePath.FullName != filteredSourceDirectoryFileExtensionLowered)
File.Move(item.FilePath.FullName, filteredSourceDirectoryFileExtensionLowered);
if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.Property is null)
if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.ExifDirectory is null)
{
property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension);
lock (sourceDirectoryChanges)
sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
lock (item)
item.Update(property);
item.Update(null);
}
}
[Obsolete("Use ExifDirectory")]
private Shared.Models.Property GetImageProperty(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, Item item, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<string> parseExceptions, bool isIgnoreExtension)
{
Shared.Models.Property? result;
@ -202,8 +206,8 @@ public class A_Property
json = File.ReadAllText(fileInfo.FullName);
try
{
if (item.Property is not null)
result = item.Property;
if (item.ExifDirectory is not null)
result = null;
else
result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property);
if (result is not null && json.Contains("WrongYear"))
@ -301,6 +305,7 @@ public class A_Property
}
}
[Obsolete("Use ExifDirectory")]
public Shared.Models.Property GetProperty(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, Item item, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<string> parseExceptions)
{
Shared.Models.Property result;

View File

@ -21,9 +21,9 @@ internal partial class Property
List<long> ticksCollection = [];
foreach (Item item in container.Items)
{
if (item.Property is null)
if (item.ExifDirectory is null)
continue;
minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
minimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(item.ExifDirectory);
if (minimumDateTime is null)
continue;
ticksCollection.Add(minimumDateTime.Value.Ticks);
@ -108,12 +108,12 @@ internal partial class Property
format = dateFormat[1];
length = dateFormat[0].Length + dateFormat[1].Length;
for (int i = dateFormat[0].Length; i < length; i++)
_ = value.Append(filePath.NameWithoutExtension[i]);
_ = value.Append(filePath.FileNameFirstSegment[i]);
if (value.Length != format.Length)
continue;
if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime))
{
if (filePath.NameWithoutExtension.Length < ticksExample.Length || !long.TryParse(filePath.NameWithoutExtension[^ticksExample.Length..], out long ticks))
if (filePath.NameWithoutExtension.Length < ticksExample.Length || !long.TryParse(filePath.FileNameFirstSegment[^ticksExample.Length..], out long ticks))
result = checkDateTime;
else
result = new DateTime(ticks);
@ -195,17 +195,17 @@ internal partial class Property
{
ticks = null;
item = container.Items[j];
if (item.Property is null)
if (item.ExifDirectory is null)
continue;
minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
minimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(item.ExifDirectory);
if (minimumDateTime is null)
continue;
for (int k = j + 1; k < container.Items.Count; k++)
{
nextItem = container.Items[k];
if (nextItem.Property is null)
if (nextItem.ExifDirectory is null)
continue;
nextMinimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(nextItem.Property);
nextMinimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(nextItem.ExifDirectory);
if (nextMinimumDateTime is null)
continue;
ticks = nextMinimumDateTime.Value.Ticks;

View File

@ -191,7 +191,7 @@ public class C_Resize
public FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, Item item, bool outputResolutionHasNumber) =>
GetResizedFileHolder(cResultsFullGroupDirectory, item.FilePath, outputResolutionHasNumber, item.FilePath.Name);
public Dictionary<string, int[]> GetResizeKeyValuePairs(Configuration configuration, string cResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem)
public Dictionary<string, int[]> GetResizeKeyValuePairs(Configuration configuration, string cResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, ExifDirectory exifDirectory, MappingFromItem mappingFromItem)
{
Dictionary<string, int[]>? results;
string json;
@ -238,7 +238,7 @@ public class C_Resize
}
if (results is null)
{
results = GetImageResizes(property);
results = GetImageResizes(exifDirectory);
json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions);
bool updateDateWhenMatches = dateTimes.Count != 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
@ -276,7 +276,7 @@ public class C_Resize
}
}
private Dictionary<string, int[]> GetImageResizes(Shared.Models.Property property)
private Dictionary<string, int[]> GetImageResizes(ExifDirectory exifDirectory)
{
Dictionary<string, int[]> results = [];
int[] desired;
@ -284,13 +284,13 @@ public class C_Resize
int checkHeight;
int desiredWidth;
int desiredHeight;
int orientation = property.Orientation is null || string.IsNullOrEmpty(property.Orientation) || !int.TryParse(property.Orientation, out int propertyOrientation) ? 0 : propertyOrientation;
if (property is null || property.Width is null || property.Height is null)
int? orientation = Shared.Models.Stateless.Methods.IMetaBase.GetOrientation(exifDirectory.ExifBaseDirectories);
if (exifDirectory is null || orientation is null || exifDirectory.Width is null || exifDirectory.Height is null)
throw new NotSupportedException();
checkWidth = property.Width.Value;
checkHeight = property.Height.Value;
checkWidth = exifDirectory.Width.Value;
checkHeight = exifDirectory.Height.Value;
if (!_ValidResolutions.Contains(_Original))
results.Add(_Original, [checkWidth, checkHeight, orientation]);
results.Add(_Original, [checkWidth, checkHeight, orientation.Value]);
foreach (string validResolution in _ValidResolutions)
{
if (validResolution == _Original)
@ -305,22 +305,22 @@ public class C_Resize
desiredHeight = desired[1];
}
if (checkWidth <= desiredWidth && checkHeight <= desiredHeight)
results.Add(validResolution, [checkWidth, checkHeight, orientation]);
results.Add(validResolution, [checkWidth, checkHeight, orientation.Value]);
else
{
if (desiredWidth != desiredHeight)
{
if (checkWidth * desiredHeight > desiredWidth * checkHeight)
results.Add(validResolution, [desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth), orientation]);
results.Add(validResolution, [desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth), orientation.Value]);
else
results.Add(validResolution, [Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight, orientation]);
results.Add(validResolution, [Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight, orientation.Value]);
}
else
{
if (checkWidth * desiredHeight <= desiredWidth * checkHeight)
results.Add(validResolution, [desiredWidth, desiredHeight, orientation, desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth)]);
results.Add(validResolution, [desiredWidth, desiredHeight, orientation.Value, desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth)]);
else
results.Add(validResolution, [desiredWidth, desiredHeight, orientation, Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight]);
results.Add(validResolution, [desiredWidth, desiredHeight, orientation.Value, Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight]);
}
}
}
@ -336,7 +336,7 @@ public class C_Resize
return results.ToArray();
}
public void SaveResizedSubfile(Configuration configuration, string outputResolution, string cResultsFullGroupDirectory, List<Tuple<string, DateTime>> subFileTuples, Item item, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize)
public void SaveResizedSubfile(Configuration configuration, string outputResolution, string cResultsFullGroupDirectory, List<Tuple<string, DateTime>> subFileTuples, Item item, ExifDirectory exifDirectory, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize)
{
if (mappingFromItem.ResizedFileHolder is null)
throw new NullReferenceException(nameof(mappingFromItem.ResizedFileHolder));
@ -378,18 +378,19 @@ public class C_Resize
check = true;
if (check)
{
SaveResizedSubfile(property, mappingFromItem, resize);
SaveResizedSubfile(exifDirectory, mappingFromItem, resize);
item.SetResizedFileHolder(_FileNameExtension, Shared.Models.Stateless.Methods.IFileHolder.Refresh(mappingFromItem.ResizedFileHolder));
subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), DateTime.Now));
}
}
}
private void SaveResizedSubfile(Shared.Models.Property property, MappingFromItem mappingFromItem, int[] resize)
private void SaveResizedSubfile(ExifDirectory exifDirectory, MappingFromItem mappingFromItem, int[] resize)
{
string dateTimeFormat = IProperty.DateTimeFormat;
DateTime dateTime = property.DateTimeOriginal is not null ? property.DateTimeOriginal.Value : Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(property);
string dateTimeValue = dateTime.ToString(dateTimeFormat);
DateTime? dateTime = Shared.Models.Stateless.Methods.IDate.GetDateTimeOriginal(exifDirectory);
dateTime ??= Shared.Models.Stateless.Methods.IDate.GetMinimum(exifDirectory);
string dateTimeValue = dateTime.Value.ToString(dateTimeFormat);
byte[] bytes = _ASCIIEncoding.GetBytes(dateTimeValue);
if (_ASCIIEncoding.GetString(bytes, 0, bytes.Length) != dateTimeValue)
throw new Exception();

34
Shared/Models/C_Resize.cs Normal file
View File

@ -0,0 +1,34 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record C_ResizeX(ReadOnlyDictionary<int, List<FilePath>> Amazon,
ReadOnlyDictionary<int, List<FilePath>> Content,
string OutputResolutionDirectory,
ReadOnlyDictionary<int, ReadOnlyDictionary<string, int[]>> OutputResolutionToResize,
ReadOnlyDictionary<int, List<FilePath>> Singleton)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, C_ResizeXSourceGenerationContext.Default.C_ResizeX);
return result;
}
// If first
// Set OutputResolutionDirectory
// Create a directories for Amazon, Content and Singleton
// Populate Amazon
// Populate Content
// Populate Singleton
// Populate existing OutputResolutionToResize
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(C_ResizeX))]
public partial class C_ResizeXSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,24 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record D2_FacePartsX(Dictionary<int, List<FilePath>> Amazon,
List<FilePath> Collection,
Dictionary<int, List<FilePath>> Content,
string OutputResolutionWithDirectory)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, D2_FacePartsXSourceGenerationContext.Default.D2_FacePartsX);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(D2_FacePartsX))]
public partial class D2_FacePartsXSourceGenerationContext : JsonSerializerContext
{
}

24
Shared/Models/D_Face.cs Normal file
View File

@ -0,0 +1,24 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record D_FaceX(Dictionary<int, List<FilePath>> Amazon,
Dictionary<int, List<FilePath>> Content,
Dictionary<int, List<FilePath>> Collection,
string OutputResolutionWithDirectory)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, D_FaceXSourceGenerationContext.Default.D_FaceX);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(D_FaceX))]
public partial class D_FaceXSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
@ -33,13 +34,12 @@ public class Face : Properties.IFace
_OutputResolution = outputResolution;
}
public Face(Property property, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, Location? location) :
public Face(ExifDirectory exifDirectory, int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation, Location? location) :
this(DateTime.MinValue, null, null, null, location, null, null)
{
DateTime?[] dateTimes;
_OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth);
dateTimes = [property.CreationTime, property.LastWriteTime, property.DateTime, property.DateTimeDigitized, property.DateTimeFromName, property.DateTimeOriginal, property.GPSDateStamp];
_DateTime = (from l in dateTimes where l.HasValue select l.Value).Min();
ReadOnlyCollection<DateTime> dateTimes = Stateless.Methods.IDate.GetDateTimes(exifDirectory);
_DateTime = dateTimes.Min();
}
public override string ToString()
@ -48,12 +48,16 @@ public class Face : Properties.IFace
return result;
}
public void SetFaceEncoding(FaceEncoding faceEncoding) => _FaceEncoding = faceEncoding;
public void SetFaceEncoding(FaceEncoding faceEncoding) =>
_FaceEncoding = faceEncoding;
public void SetFaceParts(Dictionary<Stateless.FacePart, FacePoint[]> faceParts) => _FaceParts = faceParts;
public void SetFaceParts(Dictionary<Stateless.FacePart, FacePoint[]> faceParts) =>
_FaceParts = faceParts;
public void SetMapping(Mapping mapping) => _Mapping = mapping;
public void SetMapping(Mapping mapping) =>
_Mapping = mapping;
public void SetFaceDistance(FaceDistance? faceDistance) => _FaceDistance = faceDistance;
public void SetFaceDistance(FaceDistance? faceDistance) =>
_FaceDistance = faceDistance;
}

View File

@ -3,11 +3,11 @@ using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record FilePair(string Path,
public record FilePair(FilePath FilePath,
bool IsUnique,
bool? IsNotUniqueAndNeedsReview,
List<string> Collection,
string? Match)
List<FilePath> Collection,
FilePath? Match)
{
public override string ToString()

View File

@ -21,12 +21,6 @@ public record FilePath(long CreationTicks,
int? SortOrder)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, FilePathSourceGenerationContext.Default.FilePath);
return result;
}
public static FilePath Get(Properties.IPropertyConfiguration propertyConfiguration, FileHolder fileHolder, int? index)
{
if (fileHolder.CreationTime is null)
@ -84,6 +78,28 @@ public record FilePath(long CreationTicks,
return result;
}
public static ReadOnlyDictionary<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 = [];
@ -111,6 +127,12 @@ public record FilePath(long CreationTicks,
return results.AsReadOnly();
}
public override string ToString()
{
string result = JsonSerializer.Serialize(this, FilePathSourceGenerationContext.Default.FilePath);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]

View File

@ -6,6 +6,7 @@ namespace View_by_Distance.Shared.Models;
public class Item : Properties.IItem
{
protected ExifDirectory? _ExifDirectory;
protected List<Face> _Faces;
protected readonly bool? _FileSizeChanged;
protected readonly FilePath _FilePath;
@ -15,10 +16,10 @@ public class Item : Properties.IItem
protected bool _IsValidImageFormatExtension;
protected bool? _LastWriteTimeChanged;
protected bool? _Moved;
protected Property? _Property;
protected readonly string _RelativePath;
protected FileHolder? _ResizedFileHolder;
protected readonly FileHolder _SourceDirectoryFileHolder;
public ExifDirectory? ExifDirectory => _ExifDirectory;
public List<Face> Faces => _Faces;
public bool? FileSizeChanged => _FileSizeChanged;
public FilePath FilePath => _FilePath;
@ -28,13 +29,12 @@ public class Item : Properties.IItem
public bool IsValidImageFormatExtension => _IsValidImageFormatExtension;
public bool? LastWriteTimeChanged => _LastWriteTimeChanged;
public bool? Moved => _Moved;
public Property? Property => _Property;
public string RelativePath => _RelativePath;
public FileHolder? ResizedFileHolder => _ResizedFileHolder;
public FileHolder SourceDirectoryFileHolder => _SourceDirectoryFileHolder;
[JsonConstructor]
public Item(List<Face> faces, FilePath filePath, bool? fileSizeChanged, bool? isArchive, bool? isNotUniqueAndNeedsReview, bool isUniqueFileName, bool isValidImageFormatExtension, bool? lastWriteTimeChanged, bool? moved, Property? property, string relativePath, FileHolder? resizedFileHolder, FileHolder sourceDirectoryFileHolder)
public Item(List<Face> faces, FilePath filePath, bool? fileSizeChanged, bool? isArchive, bool? isNotUniqueAndNeedsReview, bool isUniqueFileName, bool isValidImageFormatExtension, bool? lastWriteTimeChanged, bool? moved, ExifDirectory? exifDirectory, string relativePath, FileHolder? resizedFileHolder, FileHolder sourceDirectoryFileHolder)
{
_Faces = faces;
_FilePath = filePath;
@ -45,25 +45,28 @@ public class Item : Properties.IItem
_IsValidImageFormatExtension = isValidImageFormatExtension;
_LastWriteTimeChanged = lastWriteTimeChanged;
_Moved = moved;
_Property = property;
_ExifDirectory = exifDirectory;
_RelativePath = relativePath;
_ResizedFileHolder = resizedFileHolder;
_SourceDirectoryFileHolder = sourceDirectoryFileHolder;
}
public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool? isArchive, bool? isNotUniqueAndNeedsReview, bool isUniqueFileName, bool isValidImageFormatExtension, Property? property, bool? abandoned, bool? fileSizeChanged, bool? lastWriteTimeChanged)
public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool isValidImageFormatExtension) =>
Get(filePath, sourceDirectoryFileHolder, relativePath, null, null, false, isValidImageFormatExtension, null, null, null, null);
public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool? isArchive, bool? isNotUniqueAndNeedsReview, bool isUniqueFileName, bool isValidImageFormatExtension, ExifDirectory? exifDirectory, bool? abandoned, bool? fileSizeChanged, bool? lastWriteTimeChanged)
{
Item result;
if (relativePath.EndsWith(".json"))
throw new ArgumentException("Can not be a *.json file!");
if (filePath.ExtensionLowered is ".json")
throw new ArgumentException("Can not be a *.json file!");
result = new([], filePath, fileSizeChanged, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, lastWriteTimeChanged, null, property, relativePath, null, sourceDirectoryFileHolder);
result = new([], filePath, fileSizeChanged, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, lastWriteTimeChanged, null, exifDirectory, relativePath, null, sourceDirectoryFileHolder);
return result;
}
public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool isValidImageFormatExtension) =>
Get(filePath, sourceDirectoryFileHolder, relativePath, null, null, false, isValidImageFormatExtension, null, null, null, null);
public bool Any() =>
!_SourceDirectoryFileHolder.Exists || (_FileSizeChanged.HasValue && _FileSizeChanged.Value) || (_LastWriteTimeChanged.HasValue && _LastWriteTimeChanged.Value) || (_Moved.HasValue && _Moved.Value);
public override string ToString()
{
@ -71,6 +74,9 @@ public class Item : Properties.IItem
return result;
}
public void Update(ExifDirectory exifDirectory) =>
_ExifDirectory = exifDirectory;
public void SetMoved(bool moved) => _Moved = moved;
public void SetResizedFileHolder(string filenameExtension, FileHolder fileHolder)
@ -83,8 +89,4 @@ public class Item : Properties.IItem
_ResizedFileHolder = fileHolder;
}
public bool Any() => !_SourceDirectoryFileHolder.Exists || (_FileSizeChanged.HasValue && _FileSizeChanged.Value) || (_LastWriteTimeChanged.HasValue && _LastWriteTimeChanged.Value) || (_Moved.HasValue && _Moved.Value);
public void Update(Property property) => _Property = property;
}

View File

@ -1,16 +1,16 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record MappingFromItem(DateTime[] ContainerDateTimes,
DateTime? DateTimeDigitized,
DateTime? DateTimeOriginal,
int Id,
bool? IsArchive,
FilePath FilePath,
bool? IsWrongYear,
string[] Keywords,
ReadOnlyCollection<string> Keywords,
DateTime MinimumDateTime,
string? Model,
string RelativePath,
@ -29,14 +29,18 @@ public record MappingFromItem(DateTime[] ContainerDateTimes,
internal static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Item item, FileHolder? resizedFileHolder)
{
MappingFromItem result;
if (item.Property?.Id is null)
if (item.ExifDirectory?.FilePath.Id is null)
throw new NotSupportedException();
if (resizedFileHolder is null)
throw new NotSupportedException();
List<DateTime> dateTimes = item.Property.GetDateTimes();
DateTime minimumDateTime = Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
(bool? isWrongYear, _) = Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes);
result = new(containerDateTimes, item.Property.DateTimeDigitized, item.Property.DateTimeOriginal, item.Property.Id.Value, item.IsArchive, item.FilePath, isWrongYear, item.Property.Keywords ?? [], minimumDateTime, item.Property.Model, item.RelativePath, resizedFileHolder);
ExifDirectory exifDirectory = item.ExifDirectory;
DateTime minimumDateTime = Stateless.Methods.IDate.GetMinimum(exifDirectory);
DateTime? dateTime = Stateless.Methods.IDate.GetDateTimeOriginal(exifDirectory);
string? model = Stateless.Methods.MetaBase.GetModel(exifDirectory.ExifBaseDirectories);
ReadOnlyCollection<DateTime> dateTimes = Stateless.Methods.IDate.GetDateTimes(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

@ -1,8 +0,0 @@
namespace View_by_Distance.Shared.Models.Methods;
public interface IMetadataFile : Stateless.Methods.IMetadataFile
{
// ...
}

View File

@ -3,6 +3,7 @@ namespace View_by_Distance.Shared.Models.Properties;
public interface IItem
{
public ExifDirectory? ExifDirectory { get; }
public bool? FileSizeChanged { get; }
public List<Face> Faces { get; }
public FilePath FilePath { get; }
@ -11,7 +12,6 @@ public interface IItem
public bool IsUniqueFileName { get; }
public bool IsValidImageFormatExtension { get; }
public bool? Moved { get; }
public Property? Property { get; }
public string RelativePath { get; }
public FileHolder? ResizedFileHolder { get; }
public FileHolder SourceDirectoryFileHolder { get; }

View File

@ -0,0 +1,151 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract class FilePair
{
internal static ReadOnlyCollection<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++)
{
renamed = 0;
jsonFilesCollection = XDirectory.GetFilesCollection(jsonGroupDirectory, directorySearchFilter, extension, useCeilingAverage);
renamed += LookForAbandoned(propertyConfiguration, jsonFilesCollection, fileNamesToFiles, extension);
if (renamed > 0)
continue;
compareFileNamesToFiles = GetKeyValuePairs(propertyConfiguration, jsonFilesCollection);
results = XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles);
renamed += XDirectory.MaybeMove(propertyConfiguration, results, jsonGroupDirectory, extension);
if (renamed == 0)
break;
if (i > 10)
throw new NotImplementedException();
}
if (results is null || jsonFilesCollection is null || compareFileNamesToFiles is null)
throw new NullReferenceException(nameof(results));
return results.AsReadOnly();
}
private static int LookForAbandoned(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> jsonFilesCollection, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, string extension)
{
int result;
bool check;
bool moved = false;
List<string> renameCollection = [];
foreach (string[] files in jsonFilesCollection)
{
if (files.Length == 0)
continue;
check = AnyMoved(propertyConfiguration, fileNamesToFiles, extension, renameCollection, files);
if (!moved && check)
moved = true;
}
if (renameCollection.Count > 0)
XDirectory.MoveFiles(renameCollection, "{}", "{abd}");
result = renameCollection.Count;
if (moved && result == 0)
result = 1;
return result;
}
private static bool AnyMoved(IPropertyConfiguration propertyConfiguration, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, string extension, List<string> renameCollection, string[] files)
{
bool result = false;
string checkFile;
string directory;
FilePath filePath;
string fileNameWith;
string checkDirectory;
string directoryName;
List<FilePath>? collection;
Models.FileHolder fileHolder;
List<string> directoryNames = [];
foreach (string file in files)
{
if (!file.EndsWith(extension))
throw new Exception();
fileHolder = IFileHolder.Get(file);
if (!fileHolder.Exists)
continue;
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
renameCollection.Add(file);
else
{
directoryNames.Clear();
directoryName = Path.GetFileName(filePath.DirectoryFullPath) ?? throw new Exception();
foreach (FilePath f in collection)
directoryNames.Add(Path.GetFileName(f.DirectoryFullPath) ?? throw new Exception());
if (directoryNames.Count == 0 || directoryNames.Distinct().Count() != 1)
continue;
if (directoryName != directoryNames[0])
{
directory = Path.GetDirectoryName(filePath.DirectoryFullPath) ?? throw new Exception();
checkDirectory = Path.Combine(directory, directoryNames[0]);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
fileNameWith = collection.Count > 1 ? filePath.Name : $"{collection[0].Name}{extension}";
checkFile = Path.Combine(checkDirectory, fileNameWith);
if (!result)
result = true;
if (!File.Exists(checkFile))
File.Move(file, checkFile);
else
{
if (new FileInfo(file).LastWriteTime > new FileInfo(checkFile).LastWriteTime)
File.Delete(file);
else
File.Move(file, checkFile, true);
}
}
}
}
return result;
}
private static ReadOnlyDictionary<int, List<FilePath>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection)
{
Dictionary<int, List<FilePath>> results = [];
List<FilePath>? collection;
FilePath filePath;
Models.FileHolder fileHolder;
foreach (string[] files in filesCollection)
{
foreach (string file in files)
{
fileHolder = IFileHolder.Get(file);
if (!fileHolder.Exists)
continue;
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
if (!results.TryGetValue(filePath.Id.Value, out collection))
{
results.Add(filePath.Id.Value, []);
if (!results.TryGetValue(filePath.Id.Value, out collection))
throw new Exception();
}
collection.Add(filePath);
}
}
return new(results);
}
}

View File

@ -0,0 +1,38 @@
using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IDate
{
public static DateTime GetMinimum(ExifDirectory exifDirectory) =>
XDate.GetMinimum(exifDirectory);
public static (int Season, string seasonName) GetSeason(int dayOfYear) =>
XDate.GetSeason(dayOfYear);
public static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) =>
XDate.GetDateTimeOriginal(exifDirectory);
public static ReadOnlyCollection<DateTime> GetDateTimes(ExifDirectory exifDirectory) =>
XDate.GetDateTimes(exifDirectory);
public static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
XDate.IsWrongYear(directoryInfo, filePath, exifDirectory);
internal DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) =>
GetMinimum(exifDirectory);
internal (int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) =>
GetSeason(dayOfYear);
internal DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
GetDateTimeOriginal(exifDirectory);
internal ReadOnlyCollection<DateTime> TestStatic_GetDateTimes(ExifDirectory exifDirectory) =>
XDate.GetDateTimes(exifDirectory);
internal (bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
IsWrongYear(directoryInfo, filePath, exifDirectory);
}

View File

@ -11,32 +11,32 @@ public interface IDirectory
public static char GetDirectory(string fileName) =>
fileName.Split('-').Length > 2 ? '-' : fileName.Split('.')[0][^1];
public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> fileGroups, Action? tick) =>
XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, tick);
public static void MoveFiles(List<string> files, string find, string replace) =>
XDirectory.MoveFiles(files, find, replace);
public static List<string> CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) =>
XDirectory.CopyOrMove(toDoCollection, move, moveBack, tick);
public static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List<FilePair> filePairs, string jsonGroupDirectory, string extension) =>
XDirectory.MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension);
public static ReadOnlyCollection<string[]> GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) =>
XDirectory.GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
public static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection) =>
XDirectory.GetFilePathCollections(propertyConfiguration, filesCollection);
public static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List<Models.FilePair> filePairs, string jsonGroupDirectory, string extension) =>
XDirectory.MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension);
public static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) =>
XDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage);
public static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection, bool useIgnoreExtensions) =>
XDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
public static List<FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, List<string>> fileNamesToFiles, IReadOnlyDictionary<string, List<string>> compareFileNamesToFiles) =>
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) =>
XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles);
public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> fileGroups, Action? tick) =>
XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates: false, ifCanUseId: true, filePathsCollection, fileGroups, tick);
public static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<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) =>
XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates: false, ifCanUseId: true, filePathsCollection, fileGroups, exifDirectoriesById, tick);
internal int TestStatic_GetDirectory(char directory) =>
GetDirectory(directory);
@ -50,25 +50,25 @@ public interface IDirectory
internal List<string> TestStatic_CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) =>
CopyOrMove(toDoCollection, move, moveBack, tick);
internal int TestStatic_MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List<FilePair> filePairs, string jsonGroupDirectory, string extension) =>
MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension);
internal ReadOnlyCollection<string[]> TestStatic_GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) =>
GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
internal ReadOnlyCollection<ReadOnlyCollection<FilePath>> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection) =>
GetFilePathCollections(propertyConfiguration, filesCollection);
internal int TestStatic_MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List<Models.FilePair> filePairs, string jsonGroupDirectory, string extension) =>
MaybeMove(propertyConfiguration, filePairs, jsonGroupDirectory, extension);
internal ReadOnlyCollection<ReadOnlyCollection<FilePath>> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) =>
GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage);
internal ReadOnlyCollection<ReadOnlyCollection<FilePath>> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection, bool useIgnoreExtensions) =>
GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
internal List<FilePair> TestStatic_GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, List<string>> fileNamesToFiles, IReadOnlyDictionary<string, List<string>> compareFileNamesToFiles) =>
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) =>
GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, compareFileNamesToFiles);
internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> fileGroups, Action? tick) =>
GetToDoCollection(propertyConfiguration, filePathsCollection, fileGroups, tick);
internal (string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> fileGroups, Action? tick) =>
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) =>
GetToDoCollection(propertyConfiguration, filePathsCollection, fileGroups, exifDirectoriesById, tick);
}

View File

@ -5,6 +5,7 @@ public interface IDlibDotNet
void Tick();
(string, string) GetResultsFullGroupDirectories();
void ConstructProgressBar(int maxTicks, string message);
(string, string, string, string) GetResultsFullGroupDirectories(string outputResolution);
}

View File

@ -0,0 +1,21 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IFilePair
{
public static ReadOnlyCollection<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

@ -29,19 +29,19 @@ public interface IId
Id.NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder.NameWithoutExtension.Split('.')[0]);
public static bool NameWithoutExtensionIsIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) =>
fileNameFirstSegment.Length - 1 == propertyConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
fileNameFirstSegment.Length - 1 == propertyConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
public static string GetIntelligentId(IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
Id.GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
public static string GetPaddedId(IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
Id.GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
public static string GetIntelligentId(IPropertyConfiguration propertyConfiguration, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
Id.GetIntelligentId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
public static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
fileNameFirstSegment.Length == propertyConfiguration.IntMinValueLength + sortOrderOnlyLengthIndex + 1
&& fileNameFirstSegment[^1] is '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9'
&& fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9'
&& fileNameFirstSegment.All(char.IsNumber);
public static string GetPaddedId(IPropertyConfiguration propertyConfiguration, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
Id.GetPaddedId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
internal int TestStatic_GetDeterministicHashCode(byte[] value) =>
GetDeterministicHashCode(value);
@ -63,13 +63,13 @@ public interface IId
internal bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) =>
NameWithoutExtensionIsIntelligentIdFormat(propertyConfiguration, fileNameFirstSegment);
internal string TestStatic_GetIntelligentId(IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
internal string TestStatic_GetPaddedId(IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
internal string TestStatic_GetIntelligentId(IPropertyConfiguration propertyConfiguration, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
GetIntelligentId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
internal bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
NameWithoutExtensionIsPaddedIntelligentIdFormat(propertyConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment);
internal string TestStatic_GetPaddedId(IPropertyConfiguration propertyConfiguration, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
GetPaddedId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
}

View File

@ -1,16 +1,18 @@
namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IMappingFromItem
{ // ...
{
MappingFromItem TestStatic_GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder)
=> GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder)
public static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder)
=> MappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
MappingFromItem TestStatic_GetMappingFromItem(Models.Item item)
=> GetMappingFromItem(item);
static MappingFromItem GetMappingFromItem(Models.Item item)
public static MappingFromItem GetMappingFromItem(Models.Item item)
=> GetMappingFromItem(containerDateTimes: [], item, item.ResizedFileHolder);
internal MappingFromItem TestStatic_GetMappingFromItem(Models.Item item)
=> GetMappingFromItem(item);
internal MappingFromItem TestStatic_GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder)
=> GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
}

View File

@ -0,0 +1,32 @@
using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IMetaBase
{
public static string? GetMaker(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetMaker(exifBaseDirectories);
public static string? GetModel(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetModel(exifBaseDirectories);
public static int? GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetOrientation(exifBaseDirectories);
public static ReadOnlyCollection<string> GetKeywords(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetKeywords(exifBaseDirectories);
internal static string? TestStatic_GetMaker(ExifDirectoryBase[]? exifBaseDirectories) =>
GetMaker(exifBaseDirectories);
internal static string? TestStatic_GetModel(ExifDirectoryBase[]? exifBaseDirectories) =>
GetModel(exifBaseDirectories);
internal static int? TestStatic_GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) =>
GetOrientation(exifBaseDirectories);
internal static ReadOnlyCollection<string> TestStatic_GetKeywords(ExifDirectoryBase[]? exifBaseDirectories) =>
GetKeywords(exifBaseDirectories);
}

View File

@ -1,8 +0,0 @@
namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IMetadataFile
{
// ...
}

View File

@ -24,6 +24,9 @@ public interface IPath
public static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) =>
XPath.MakeHiddenIfAllItemsAreHidden(rootDirectory);
public static void CreateDirectories(ReadOnlyCollection<string> directories) =>
XPath.CreateDirectories(directories);
public static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
XPath.ChangeDateForEmptyDirectories(rootDirectory, ticks);
@ -65,6 +68,9 @@ public interface IPath
internal void TestStatic_MakeHiddenIfAllItemsAreHidden(string rootDirectory) =>
MakeHiddenIfAllItemsAreHidden(rootDirectory);
internal void TestStatic_CreateDirectories(ReadOnlyCollection<string> directories) =>
CreateDirectories(directories);
internal void TestStatic_ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
ChangeDateForEmptyDirectories(rootDirectory, ticks);

View File

@ -37,7 +37,7 @@ internal abstract class Id
_ = results.Append(intelligentId[i]);
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
result = int.Parse(results.ToString());
if (intelligentId[^1] is '1' or '2' or '3' or '4')
if (intelligentId[^1] is '0' or '1' or '2' or '3' or '4')
result *= -1;
else if (intelligentId[^1] is not '9' and not '8' and not '7' and not '6' and not '5')
throw new NotSupportedException();
@ -48,7 +48,7 @@ internal abstract class Id
(byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 9 : 1 : filePath.Id > -1 ? 6 : 4);
internal static byte GetMissingDateTimeOriginal(Properties.IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
(byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 7 : 3 : 5);
(byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 7 : 3 : filePath.Id > -1 ? 5 : 0);
internal static bool NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameWithoutExtension)
{
@ -63,25 +63,34 @@ internal abstract class Id
return result;
}
internal static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
internal static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
{
string result;
StringBuilder stringBuilder = new();
if (propertyConfiguration.IntMinValueLength < (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
if (hasDateTimeOriginal is null)
{ }
int key;
string value;
List<char> resultAllInOneSubdirectoryChars = [];
if (id > -1)
if (hasDateTimeOriginal is null)
{
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal is not null && hasDateTimeOriginal.Value ? 9 : 7;
key = 0;
value = id.ToString().PadLeft(propertyConfiguration.IntMinValueLength, '0');
}
else if (id > -1)
{
if (!propertyConfiguration.ValidVideoFormatExtensions.Contains(extensionLowered))
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal.Value ? 9 : 7;
else
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 6 : 5;
value = id.ToString().PadLeft(propertyConfiguration.IntMinValueLength, '0');
}
else
{
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal is not null && hasDateTimeOriginal.Value ? 1 : 3;
if (!propertyConfiguration.ValidVideoFormatExtensions.Contains(extensionLowered))
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal.Value ? 1 : 3;
else
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 4 : 0;
value = id.ToString()[1..].PadLeft(propertyConfiguration.IntMinValueLength, '0');
}
for (int i = value.Length - propertyConfiguration.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
@ -92,14 +101,14 @@ internal abstract class Id
return result;
}
internal static string GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
internal static string GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
{
string result;
if (propertyConfiguration.Offset < 0)
result = Guid.NewGuid().ToString();
else
{
string intelligentId = GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
string intelligentId = GetIntelligentId(propertyConfiguration, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
int check = GetId(propertyConfiguration, intelligentId);
if (check != id)
throw new NotSupportedException();

View File

@ -0,0 +1,83 @@
using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal static class MetaBase
{
internal static string? GetMaker(ExifDirectoryBase[]? exifBaseDirectories)
{
string? result = null;
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.Make is null ? string.Empty : exifDirectoryBase.Make.ToString().Trim();
if (string.IsNullOrEmpty(value))
result = null;
else
{
result = $"{value[0].ToString().ToUpper()}{value[1..].ToLower()}";
break;
}
}
}
return result;
}
internal static string? GetModel(ExifDirectoryBase[]? exifBaseDirectories)
{
string? result = null;
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.Model is null ? string.Empty : exifDirectoryBase.Model.ToString().Trim();
if (string.IsNullOrEmpty(value))
result = null;
else
{
result = value;
break;
}
}
}
return result;
}
internal static int? GetOrientation(ExifDirectoryBase[]? exifBaseDirectories)
{
int? result = null;
// public const int TagOrientation = 274;
if (exifBaseDirectories is not null)
{
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
result = exifDirectoryBase?.OrientationValue;
if (result is not null)
break;
}
}
return result;
}
internal static ReadOnlyCollection<string> GetKeywords(ExifDirectoryBase[]? exifBaseDirectories)
{
List<string> results = [];
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.WinKeywords is null ? string.Empty : exifDirectoryBase.WinKeywords.ToString().Trim();
if (string.IsNullOrEmpty(value))
continue;
results.Add(value);
}
}
return results.AsReadOnly();
}
}

View File

@ -0,0 +1,252 @@
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract class XDate
{
internal static ReadOnlyCollection<DateTime> GetDateTimes(ExifDirectory exifDirectory)
{
List<DateTime> results = [];
foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
{
if (exifDirectoryBase.DateTimeOriginal is not null)
results.Add(exifDirectoryBase.DateTimeOriginal.Value);
}
foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories)
{
if (aviDirectory.DateTimeOriginal is not null)
results.Add(aviDirectory.DateTimeOriginal.Value);
}
foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories)
{
if (quickTimeMovieHeaderDirectory.Created is not null)
{
if (quickTimeMovieHeaderDirectory.Created.Value.Year == 1904 && quickTimeMovieHeaderDirectory.Created.Value.Month == 1 && quickTimeMovieHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeMovieHeaderDirectory.Created.Value);
}
}
foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories)
{
if (quickTimeTrackHeaderDirectory.Created is not null)
{
if ((quickTimeTrackHeaderDirectory.Created.Value.Year is 1904 or 1970) && quickTimeTrackHeaderDirectory.Created.Value.Month == 1 && quickTimeTrackHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeTrackHeaderDirectory.Created.Value);
}
}
if (results.Count == 0)
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(exifDirectory.FilePath.Name);
DateTime? dateTime = GetDateTimeFromName(fileNameWithoutExtension);
if (dateTime is not null)
results.Add(dateTime.Value);
foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
{
if (exifDirectoryBase.DateTime is not null)
results.Add(exifDirectoryBase.DateTime.Value);
if (exifDirectoryBase.DateTimeDigitized is not null)
results.Add(exifDirectoryBase.DateTimeDigitized.Value);
}
}
if (results.Count == 0)
{
foreach (FileMetadataDirectory fileMetadataDirectory in exifDirectory.FileMetadataDirectories)
{
if (fileMetadataDirectory.FileModifiedDate is not null)
results.Add(fileMetadataDirectory.FileModifiedDate.Value);
}
}
return results.AsReadOnly();
}
private static DateTime? GetDateTimeFromName(string fileNameWithoutExtension)
{
DateTime? result = null;
int length;
string format;
string fullFormat;
StringBuilder value = new();
const string ticksExample = "##################";
string[][] dateFormats =
[
[string.Empty, "yyyyMMdd_HHmmss", string.Empty],
[string.Empty, "yyyyMMddHHmmssfff", string.Empty],
[string.Empty, "yyyyMMdd_", ticksExample],
[string.Empty, "yyyy-MM-dd_", ticksExample],
[string.Empty, "yyyy-MM-dd.", ticksExample],
// [string.Empty, "yyyy-MM-dd.", $"{ticksExample}.{fileHolder.Length}"],
[string.Empty, "yyyy-MM-dd HH.mm.ss", string.Empty],
[string.Empty, "yyyyMMdd_HHmmss", "_LLS"],
[string.Empty, "yyyyMMdd_HHmmss", "_HDR"],
["WIN_", "yyyyMMdd_HH_mm_ss", "_Pro"],
["IMG_", "yyyyMMdd_HHmmss", string.Empty],
["IMG#####-", "yyyyMMdd-HHmm", string.Empty],
["CameraZOOM-", "yyyyMMddHHmmss", string.Empty],
["VideoCapture_", "yyyyMMdd-HHmmss ", string.Empty]
];
foreach (string[] dateFormat in dateFormats)
{
_ = value.Clear();
if (dateFormat.Length != 3)
throw new Exception();
fullFormat = string.Join(string.Empty, dateFormat);
if (fileNameWithoutExtension.Length != fullFormat.Length)
continue;
format = dateFormat[1];
length = dateFormat[0].Length + dateFormat[1].Length;
for (int i = dateFormat[0].Length; i < length; i++)
_ = value.Append(fileNameWithoutExtension[i]);
if (value.Length != format.Length)
continue;
if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime))
{
if (fileNameWithoutExtension.Length < ticksExample.Length || !long.TryParse(fileNameWithoutExtension[^ticksExample.Length..], out long ticks))
result = checkDateTime;
else
result = new DateTime(ticks);
break;
}
}
return result;
}
internal static DateTime GetMinimum(ExifDirectory exifDirectory)
{
DateTime result;
ReadOnlyCollection<DateTime> results = GetDateTimes(exifDirectory);
result = results.Count == 0 ? DateTime.MinValue : results.Min();
return result;
}
internal static (int Season, string seasonName) GetSeason(int dayOfYear)
{
(int Season, string seasonName) result = dayOfYear switch
{
< 78 => new(0, "Winter"),
< 124 => new(1, "Spring"),
< 171 => new(2, "Spring"),
< 217 => new(3, "Summer"),
< 264 => new(4, "Summer"),
< 309 => new(5, "Fall"),
< 354 => new(6, "Fall"),
_ => new(7, "Winter")
};
return result;
}
internal static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory)
{
DateTime? result;
List<DateTime> results = [];
foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
{
if (exifDirectoryBase.DateTimeOriginal is not null)
results.Add(exifDirectoryBase.DateTimeOriginal.Value);
}
foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories)
{
if (aviDirectory.DateTimeOriginal is not null)
results.Add(aviDirectory.DateTimeOriginal.Value);
}
foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories)
{
if (quickTimeMovieHeaderDirectory.Created is not null)
{
if (quickTimeMovieHeaderDirectory.Created.Value.Year == 1904 && quickTimeMovieHeaderDirectory.Created.Value.Month == 1 && quickTimeMovieHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeMovieHeaderDirectory.Created.Value);
}
}
foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories)
{
if (quickTimeTrackHeaderDirectory.Created is not null)
{
if ((quickTimeTrackHeaderDirectory.Created.Value.Year is 1904 or 1970) && quickTimeTrackHeaderDirectory.Created.Value.Month == 1 && quickTimeTrackHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeTrackHeaderDirectory.Created.Value);
}
}
result = results.Count == 0 ? null : results.Min();
return result;
}
internal static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory)
{
string[] results = [];
bool? result = null;
string year;
string directoryName;
string[] directorySegments;
List<DateTime> collection = [];
string? check = Path.GetFullPath(filePath.FullName);
DateTime? dateTimeOriginal = GetDateTimeOriginal(exifDirectory);
if (dateTimeOriginal is not null)
collection.Add(dateTimeOriginal.Value);
else
{
ReadOnlyCollection<DateTime> dateTimes = GetDateTimes(exifDirectory);
foreach (DateTime dateTime in dateTimes)
collection.Add(dateTime);
}
foreach (DateTime dateTime in collection)
{
year = dateTime.ToString("yyyy");
for (int i = 0; i < int.MaxValue; i++)
{
check = Path.GetDirectoryName(check);
if (string.IsNullOrEmpty(check))
break;
directoryName = Path.GetFileName(check);
directorySegments = directoryName.Split(' ');
(result, results) = IsWrongYear(directorySegments, year);
if (result is not null)
break;
if (check == directoryInfo.FullName)
break;
}
if (result is not null && !result.Value)
break;
}
return new(result, results);
}
private static Record IsWrongYear(string[] segments, string year)
{
Record result;
bool? check;
string[] results = (
from l
in segments
where l?.Length > 2
&& (
l[..2] is "18" or "19" or "20"
|| (l.Length == 5 && l.Substring(1, 2) is "18" or "19" or "20" && (l[0] is '~' or '=' or '-' or '^' or '#'))
|| (l.Length == 6 && l[..2] is "18" or "19" or "20" && l[4] == '.')
|| (l.Length == 7 && l.Substring(1, 2) is "18" or "19" or "20" && l[5] == '.')
)
select l
).ToArray();
string[] matches = (
from l
in results
where l == year
|| (l.Length == 5 && l.Substring(1, 4) == year && (l[0] is '~' or '=' or '-' or '^' or '#'))
|| (l.Length == 6 && l[..4] == year && l[4] == '.')
|| (l.Length == 7 && l.Substring(1, 4) == year && l[5] == '.')
select l
).ToArray();
if (results.Length == 0)
check = null;
else
check = matches.Length == 0;
result = new(check, results);
return result;
}
private record Record(bool? IsWrongYear, string[] Years);
}

View File

@ -67,57 +67,6 @@ internal abstract partial class XDirectory
return results;
}
internal static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List<FilePair> filePairs, string jsonGroupDirectory, string extension)
{
FileInfo? toFileInfo;
FileInfo fromFileInfo;
string checkDirectory;
List<(string, string)> rename = [];
foreach (FilePair filePair in filePairs)
{
if (filePair.IsUnique)
continue;
IsNotUniqueLoop(propertyConfiguration, jsonGroupDirectory, extension, filePair, rename);
}
foreach ((string from, string to) in rename)
{
toFileInfo = null;
checkDirectory = to;
fromFileInfo = new(from);
if (!fromFileInfo.Exists)
continue;
for (int i = 0; i < int.MaxValue; i++)
{
toFileInfo = new(checkDirectory);
if (toFileInfo.Directory is null)
continue;
if (!toFileInfo.Directory.Exists)
_ = Directory.CreateDirectory(toFileInfo.Directory.FullName);
if (checkDirectory.Length > 199)
throw new Exception();
if (!toFileInfo.Exists)
break;
else if (fromFileInfo.Length == toFileInfo.Length && fromFileInfo.LastWriteTime == toFileInfo.LastWriteTime)
checkDirectory = string.Concat(checkDirectory, ".del");
else
checkDirectory = string.Concat(checkDirectory, ".j");
}
File.Move(from, checkDirectory);
}
return rename.Count;
}
private static void IsNotUniqueLoop(Properties.IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, FilePair filePair, List<(string, string)> rename)
{
int length = propertyConfiguration.RootDirectory.Length;
foreach (string path in filePair.Collection)
{
if (filePair.Match is null || path != filePair.Match)
continue;
rename.Add(new(path, string.Concat(jsonGroupDirectory, filePair.Path[length..], extension)));
}
}
internal static ReadOnlyCollection<string[]> GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage)
{
List<string[]> results = [];
@ -173,7 +122,54 @@ internal abstract partial class XDirectory
return results;
}
internal static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection)
internal static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List<Models.FilePair> filePairs, string jsonGroupDirectory, string extension)
{
FileInfo? toFileInfo;
string checkDirectory;
List<(FilePath, string)> rename = [];
foreach (Models.FilePair filePair in filePairs)
{
if (filePair.IsUnique)
continue;
IsNotUniqueLoop(propertyConfiguration, jsonGroupDirectory, extension, filePair, rename);
}
foreach ((FilePath from, string to) in rename)
{
toFileInfo = null;
checkDirectory = to;
for (int i = 0; i < int.MaxValue; i++)
{
toFileInfo = new(checkDirectory);
if (toFileInfo.Directory is null)
continue;
if (!toFileInfo.Directory.Exists)
_ = Directory.CreateDirectory(toFileInfo.Directory.FullName);
if (checkDirectory.Length > 199)
throw new Exception();
if (!toFileInfo.Exists)
break;
else if (from.Length == toFileInfo.Length && from.LastWriteTicks == toFileInfo.LastWriteTime.Ticks)
checkDirectory = string.Concat(checkDirectory, ".del");
else
checkDirectory = string.Concat(checkDirectory, ".j");
}
File.Move(from.FullName, checkDirectory);
}
return rename.Count;
}
private static void IsNotUniqueLoop(Properties.IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, Models.FilePair filePair, List<(FilePath, string)> rename)
{
int length = propertyConfiguration.RootDirectory.Length;
foreach (FilePath path in filePair.Collection)
{
if (filePair.Match is null || path != filePair.Match)
continue;
rename.Add(new(path, string.Concat(jsonGroupDirectory, filePair.FilePath.FullName[length..], extension)));
}
}
internal static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection, bool useIgnoreExtensions)
{
List<ReadOnlyCollection<FilePath>> results = [];
FilePath filePath;
@ -185,7 +181,9 @@ internal abstract partial class XDirectory
foreach (string file in files)
{
fileHolder = IFileHolder.Get(file);
if (propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
if (!fileHolder.Exists)
continue;
if (useIgnoreExtensions && propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
filePaths.Add(filePath);
@ -195,38 +193,38 @@ internal abstract partial class XDirectory
return results.AsReadOnly();
}
internal static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage)
internal static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useIgnoreExtensions, bool useCeilingAverage)
{
ReadOnlyCollection<ReadOnlyCollection<FilePath>> results;
ReadOnlyCollection<string[]> filesCollection = GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
results = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection);
results = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
return results;
}
internal static List<FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, List<string>> fileNamesToFiles, IReadOnlyDictionary<string, List<string>> compareFileNamesToFiles)
internal static List<Models.FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, List<FilePath>> compareFileNamesToFiles)
{
List<FilePair> results = [];
string? match;
FilePair filePair;
List<Models.FilePair> results = [];
FilePath? match;
bool uniqueFileName;
List<string>? collection;
List<FilePath>? collection;
Models.FilePair filePair;
bool? isNotUniqueAndNeedsReview;
string fileNameWithoutExtensionMinusOne;
foreach (ReadOnlyCollection<FilePath> filePaths in filePathsCollection)
{
foreach (FilePath filePath in filePaths)
{
if (filePath.Id is null)
continue;
isNotUniqueAndNeedsReview = null;
if (propertyConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered))
continue;
fileNameWithoutExtensionMinusOne = filePath.NameWithoutExtension[..^1];
if (!fileNamesToFiles.TryGetValue(fileNameWithoutExtensionMinusOne, out collection))
if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
throw new Exception();
uniqueFileName = collection.Count == 1;
if (!uniqueFileName)
isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(filePath.FullName, collection);
if (!compareFileNamesToFiles.TryGetValue(fileNameWithoutExtensionMinusOne, out collection))
filePair = new(Path: filePath.FullName,
isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(filePath, collection);
if (!compareFileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
filePair = new(FilePath: filePath,
IsUnique: uniqueFileName,
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
Collection: [],
@ -234,21 +232,21 @@ internal abstract partial class XDirectory
else
{
if (collection.Count == 0)
filePair = new(Path: filePath.FullName,
filePair = new(FilePath: filePath,
IsUnique: uniqueFileName,
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
Collection: collection,
Match: null);
else if (uniqueFileName && collection.Count == 1)
filePair = new(Path: filePath.FullName,
filePair = new(FilePath: filePath,
IsUnique: uniqueFileName,
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
Collection: collection,
Match: collection.First());
else
{
match = GetMatch(filePath.FullName, collection);
filePair = new(Path: filePath.FullName,
match = GetMatch(filePath, collection);
filePair = new(FilePath: filePath,
IsUnique: uniqueFileName,
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
Collection: collection,
@ -261,19 +259,20 @@ internal abstract partial class XDirectory
return results;
}
private static bool GetIsNotUniqueAndNeedsReview(string file, List<string> collection)
private static bool GetIsNotUniqueAndNeedsReview(FilePath filePath, List<FilePath> collection)
{
bool result = false;
FileInfo possibleFileInfo;
FileInfo fileInfo = new(file);
foreach (string possible in collection)
long max;
foreach (FilePath possible in collection)
{
if (possible == file)
if (possible.FullName == filePath.FullName)
continue;
possibleFileInfo = new(possible);
if (possibleFileInfo.LastWriteTime != fileInfo.LastWriteTime)
File.SetLastWriteTime(file, new DateTime[] { possibleFileInfo.LastWriteTime, fileInfo.LastWriteTime }.Max());
if (possibleFileInfo.LastWriteTime == fileInfo.LastWriteTime && possibleFileInfo.Length == fileInfo.Length)
if (possible.LastWriteTicks != filePath.LastWriteTicks)
{
max = new long[] { possible.LastWriteTicks, filePath.LastWriteTicks }.Max();
File.SetLastWriteTime(filePath.FullName, new DateTime(max));
}
if (possible.LastWriteTicks == filePath.LastWriteTicks && possible.Length == filePath.Length)
continue;
if (!result)
result = true;
@ -281,50 +280,48 @@ internal abstract partial class XDirectory
return result;
}
private static string? GetMatch(string file, List<string> collection)
private static FilePath? GetMatch(FilePath filePath, List<FilePath> collection)
{
string? result = null;
FileInfo possibleFileInfo;
FilePath? result = null;
List<long> lengths = [];
List<string> matches = [];
FileInfo fileInfo = new(file);
List<DateTime> creationTimes = [];
foreach (string possible in collection)
List<FilePath> matches = [];
List<long> lastWriteTicks = [];
foreach (FilePath possible in collection)
{
possibleFileInfo = new(possible);
lengths.Add(possibleFileInfo.Length);
creationTimes.Add(possibleFileInfo.CreationTime);
if (possibleFileInfo.CreationTime != fileInfo.LastWriteTime)
lengths.Add(possible.Length);
lastWriteTicks.Add(possible.LastWriteTicks);
if (possible.LastWriteTicks != filePath.LastWriteTicks)
continue;
matches.Add(possible);
}
if (matches.Count == 1 || (matches.Count > 0 && lengths.Distinct().Count() == 1 && creationTimes.Distinct().Count() == 1))
if (matches.Count == 1 || (matches.Count > 0 && lengths.Distinct().Count() == 1 && lastWriteTicks.Distinct().Count() == 1))
result = matches.First();
return result;
}
internal static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> fileGroups, 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, Dictionary<int, ExifDirectory>? exifDirectoriesById, Action? tick)
{
List<(FilePath, string)> results = [];
string paddedId;
string checkFile;
string directory;
FileInfo fileInfo;
FilePath filePath;
DateTime? dateTime;
string paddedIdFile;
bool wrapped = false;
string intelligentId;
CombinedEnumAndIndex cei;
bool? hasIgnoreKeyword;
bool paddedCheck = false;
CombinedEnumAndIndex cei;
string fileDirectoryName;
bool? hasDateTimeOriginal;
List<int> distinctIds = [];
List<string> distinct = [];
ExifDirectory? exifDirectory;
Models.FileHolder fileHolder;
ReadOnlyCollection<string> keywords;
List<string> distinctDirectories = [];
FilePath[] sortedRecords = GetSortedRecords(filePathsCollection);
ReadOnlyDictionary<byte, ReadOnlyCollection<string>>? keyValuePairs;
if (!fileGroups.TryGetValue(propertyConfiguration.ResultContent, out keyValuePairs))
throw new NotImplementedException();
bool isOffsetDeterministicHashCode = IId.IsOffsetDeterministicHashCode(propertyConfiguration);
for (int i = 0; i < sortedRecords.Length; i++)
{
@ -338,23 +335,45 @@ internal abstract partial class XDirectory
{
if (wrapped)
continue;
directory = keyValuePairs[cei.Enum][cei.Index];
if (cei.Enum == 0)
continue;
directory = fileGroups[cei.Enum][cei.Index];
}
else
{
if (!wrapped)
wrapped = true;
directory = Path.Combine(keyValuePairs[cei.Enum][cei.Index], fileDirectoryName);
directory = Path.Combine(fileGroups[cei.Enum][cei.Index], fileDirectoryName);
}
if (ifCanUseId && filePath.IsIntelligentIdFormat && filePath.Id is not null && filePath.DirectoryFullPath is not null)
{
paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i);
if (filePath.Id == -748161839 || filePath.FileNameFirstSegment == "740318810015")
{
if (filePath.ExtensionLowered == ".mov") // -748161839)
{ }
}
if (exifDirectoriesById is null || !exifDirectoriesById.TryGetValue(filePath.Id.Value, out exifDirectory))
{
hasIgnoreKeyword = filePath.HasIgnoreKeyword;
hasDateTimeOriginal = filePath.HasDateTimeOriginal;
}
else
{
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
hasDateTimeOriginal = dateTime is not null;
if (dateTime is null && propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
continue;
keywords = MetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories);
hasIgnoreKeyword = propertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains);
}
paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, i);
paddedIdFile = Path.Combine(filePath.DirectoryFullPath, $"{paddedId}{filePath.ExtensionLowered}");
if (!File.Exists(paddedIdFile))
{
File.Move(filePath.FullName, paddedIdFile);
fileInfo = new(paddedIdFile);
fileHolder = Models.FileHolder.Get(fileInfo);
fileHolder = IFileHolder.Get(paddedIdFile);
if (!fileHolder.Exists)
continue;
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (!paddedCheck)
paddedCheck = true;
@ -366,14 +385,14 @@ internal abstract partial class XDirectory
{
if (filePath.Id is null)
throw new NullReferenceException(nameof(filePath.Id));
intelligentId = IId.GetIntelligentId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal);
intelligentId = IId.GetIntelligentId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal);
if (!isOffsetDeterministicHashCode)
checkFile = Path.Combine(directory, $"{intelligentId}{filePath.ExtensionLowered}");
else
{
if (filePath.DirectoryFullPath is null)
continue;
paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i);
paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i);
paddedIdFile = Path.Combine(filePath.DirectoryFullPath, $"{paddedId}{filePath.ExtensionLowered}");
if (File.Exists(paddedIdFile))
continue;
@ -391,8 +410,8 @@ internal abstract partial class XDirectory
continue;
for (int j = 1; j < int.MaxValue; j++)
{
fileInfo = new(checkFile);
if (!fileInfo.Exists || filePath.Length == fileInfo.Length && filePath.LastWriteTicks == fileInfo.LastWriteTime.Ticks)
fileHolder = IFileHolder.Get(checkFile);
if (!fileHolder.Exists || fileHolder.LastWriteTime is null || filePath.Length == fileHolder.Length && filePath.LastWriteTicks == fileHolder.LastWriteTime.Value.Ticks)
checkFile = Path.Combine(directory, $"{filePath.NameWithoutExtension}.{j}dup{filePath.ExtensionLowered}");
else
checkFile = Path.Combine(directory, $"{filePath.NameWithoutExtension}.{j}why{filePath.ExtensionLowered}");

View File

@ -64,15 +64,20 @@ internal abstract class XPath
byte @enum;
int converted;
string combined;
if (filePath.HasIgnoreKeyword is null || filePath.HasDateTimeOriginal is null)
throw new NotImplementedException();
byte missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePath);
if (!filePath.IsIntelligentIdFormat)
@enum = missingDateTimeOriginal;
else
{
if (filePath.HasIgnoreKeyword is null || filePath.HasDateTimeOriginal is null)
throw new NotImplementedException("Chicken and Egg!");
if (filePath.HasIgnoreKeyword.Value)
@enum = IId.GetHasIgnoreKeyword(filePath);
else if (!filePath.HasDateTimeOriginal.Value)
@enum = missingDateTimeOriginal;
else
@enum = IId.GetHasDateTimeOriginal(propertyConfiguration, filePath);
}
string fileNameBeforeFirst = fileNameWithoutExtension.Split('.')[0];
string check = fileNameBeforeFirst.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength ?
new('-', propertyConfiguration.ResultAllInOneSubdirectoryLength) :
@ -228,6 +233,20 @@ internal abstract class XPath
}
}
internal static void CreateDirectories(ReadOnlyCollection<string> directories)
{
string checkDirectory;
foreach (string directory in directories)
{
for (int i = 0; i < 101; i++)
{
checkDirectory = Path.Combine(directory, i.ToString("000"));
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
}
}
}
internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks)
{
DateTime dateTime = new(ticks);
@ -411,6 +430,7 @@ internal abstract class XPath
private static byte[] GetBytes() =>
[
0,
1,
2,
3,

View File

@ -39,7 +39,6 @@
<ItemGroup>
<ProjectReference Include="..\BlurHash\BlurHash.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" />
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Resize\Resize.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
</ItemGroup>

View File

@ -54,6 +54,55 @@ public partial class UnitTestHardCoded
_PropertyConfiguration = propertyConfiguration;
}
[TestMethod]
public void TestMethodId()
{
int id;
id = 748161839;
string imageTrueTruePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: true, hasDateTimeOriginal: true, index: null);
Assert.AreEqual("816184700398", imageTrueTruePositive);
string imageTrueFalsePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: true, hasDateTimeOriginal: false, index: null);
Assert.AreEqual("816184700398", imageTrueFalsePositive);
string imageFalseFalsePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: false, hasDateTimeOriginal: false, index: null);
Assert.AreEqual("816184700397", imageFalseFalsePositive);
string imageFalseTruePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: false, hasDateTimeOriginal: true, index: null);
Assert.AreEqual("816184700399", imageFalseTruePositive);
try
{ string videoTrueTruePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: true, hasDateTimeOriginal: true, index: null); }
catch (Exception)
{ }
try
{ string videoTrueFalsePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: true, hasDateTimeOriginal: false, index: null); }
catch (Exception)
{ }
string videoFalseFalsePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: false, hasDateTimeOriginal: false, index: null);
Assert.AreEqual("816184700395", videoFalseFalsePositive);
string videoFalseTruePositive = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: false, hasDateTimeOriginal: true, index: null);
Assert.AreEqual("816184700396", videoFalseTruePositive);
id = -748161839;
string imageTrueTrueNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: true, hasDateTimeOriginal: true, index: null);
Assert.AreEqual("816184700392", imageTrueTrueNegative);
string imageTrueFalseNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: true, hasDateTimeOriginal: false, index: null);
Assert.AreEqual("816184700392", imageTrueFalseNegative);
string imageFalseFalseNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: false, hasDateTimeOriginal: false, index: null);
Assert.AreEqual("816184700393", imageFalseFalseNegative);
string imageFalseTrueNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".jpg", hasIgnoreKeyword: false, hasDateTimeOriginal: true, index: null);
Assert.AreEqual("816184700391", imageFalseTrueNegative);
try
{ string videoTrueTrueNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: true, hasDateTimeOriginal: true, index: null); }
catch (Exception)
{ }
try
{ string videoTrueFalseNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: true, hasDateTimeOriginal: false, index: null); }
catch (Exception)
{ }
string videoFalseFalseNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: false, hasDateTimeOriginal: false, index: null);
Assert.AreEqual("816184700390", videoFalseFalseNegative);
string videoFalseTrueNegative = IId.GetPaddedId(_PropertyConfiguration, id, ".mov", hasIgnoreKeyword: false, hasDateTimeOriginal: true, index: null);
Assert.AreEqual("816184700394", videoFalseTrueNegative);
NonThrowTryCatch();
}
private static void NonThrowTryCatch()
{
try
@ -61,18 +110,6 @@ public partial class UnitTestHardCoded
catch (Exception) { }
}
[TestMethod]
public void TestMethodNull()
{
Assert.IsFalse(_AppSettings is null);
Assert.IsFalse(_Configuration is null);
Assert.IsFalse(_IsEnvironment is null);
Assert.IsFalse(_WorkingDirectory is null);
Assert.IsFalse(_ConfigurationRoot is null);
Assert.IsFalse(_PropertyConfiguration is null);
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodDel()
{
@ -86,30 +123,6 @@ public partial class UnitTestHardCoded
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodGetApproximateYears()
{
string personDisplayDirectory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/^/Sydney Dupray^9");
if (Directory.Exists(Directory.GetDirectoryRoot(personDisplayDirectory)) && Directory.Exists(personDisplayDirectory))
{
char numberSign = '#';
string? minusOne = null;
char[] personCharacters = ['^'];
string personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory);
string personBirthdayFormat = _Configuration.PropertyConfiguration.PersonBirthdayFormat;
string[] personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
List<(string, PersonBirthday)> collection = IPersonBirthday.GetPersonBirthdays(personBirthdayFormat, personKeyDirectories, personDisplayDirectoryName);
int? approximateYears = IAge.GetApproximateYears(personCharacters, personDisplayDirectoryName);
if (approximateYears is null)
throw new NullReferenceException(nameof(approximateYears));
Assert.IsNotNull(approximateYears);
Assert.IsTrue(approximateYears.Value == 9);
string? change = IPersonContainer.VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, collection);
Assert.IsNull(change);
}
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodDel2()
{
@ -123,14 +136,35 @@ public partial class UnitTestHardCoded
NonThrowTryCatch();
}
private static (string?, string, string) Get(string[] segments)
[TestMethod]
public void TestMethodNull()
{
(string?, string, string) result;
if ((!segments[0].Contains('#') && (segments[3].Contains('~') || segments[3].Contains('#'))) || (segments[0].Contains('#') && !segments[3].Contains('#')))
result = new(null, segments[3], segments[4]);
else
result = new(segments[0], segments[3], segments[4]);
return result;
Assert.IsNotNull(_AppSettings);
Assert.IsNotNull(_Configuration);
Assert.IsNotNull(_IsEnvironment);
Assert.IsNotNull(_WorkingDirectory);
Assert.IsNotNull(_ConfigurationRoot);
Assert.IsNotNull(_PropertyConfiguration);
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodRename()
{
// string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}";
string directory = $"D:/1-Images-A/Images-{_Git}";
// string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}";
if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory))
{
string[] directories = Directory.GetDirectories(directory, "*;*", SearchOption.AllDirectories);
foreach (string subDirectory in directories.OrderByDescending(l => l.Length - l.Replace(@"\", string.Empty).Length))
{
if (!subDirectory.EndsWith(";9"))
continue;
Directory.Move(subDirectory, $"{subDirectory[..^2]} !9");
}
}
NonThrowTryCatch();
}
[TestMethod]
@ -189,64 +223,14 @@ public partial class UnitTestHardCoded
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodRenameAbandoned()
private static (string?, string, string) Get(string[] segments)
{
string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/!/Abandoned");
if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory))
{
string checkFile;
string[] files = Directory.GetFiles(directory, "*.abd", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
checkFile = file[..^4];
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
}
Assert.IsTrue(true);
}
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodRenameDelete()
{
string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A)Property/{_Git}", "/{}");
if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory))
{
string checkFile;
string[] files = Directory.GetFiles(directory, "*.del", SearchOption.AllDirectories);
foreach (string file in files)
{
checkFile = file[..^4];
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
}
Assert.IsTrue(true);
}
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodRenameOld()
{
string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/()";
if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory))
{
string checkFile;
string[] files = Directory.GetFiles(directory, "*.old", SearchOption.AllDirectories);
foreach (string file in files)
{
checkFile = file[..^4];
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
}
Assert.IsTrue(true);
}
NonThrowTryCatch();
(string?, string, string) result;
if ((!segments[0].Contains('#') && (segments[3].Contains('~') || segments[3].Contains('#'))) || (segments[0].Contains('#') && !segments[3].Contains('#')))
result = new(null, segments[3], segments[4]);
else
result = new(segments[0], segments[3], segments[4]);
return result;
}
[TestMethod]
@ -285,33 +269,59 @@ public partial class UnitTestHardCoded
}
[TestMethod]
public void TestMethodRename()
public void TestMethodRenameOld()
{
// string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}";
string directory = $"D:/1-Images-A/Images-{_Git}";
// string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}";
string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/()";
if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory))
{
string[] directories = Directory.GetDirectories(directory, "*;*", SearchOption.AllDirectories);
foreach (string subDirectory in directories.OrderByDescending(l => l.Length - l.Replace(@"\", string.Empty).Length))
string checkFile;
string[] files = Directory.GetFiles(directory, "*.old", SearchOption.AllDirectories);
foreach (string file in files)
{
if (!subDirectory.EndsWith(";9"))
checkFile = file[..^4];
if (File.Exists(checkFile))
continue;
Directory.Move(subDirectory, $"{subDirectory[..^2]} !9");
File.Move(file, checkFile);
}
Assert.IsTrue(true);
}
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodRenameForUnknown()
public void TestMethodImmichAsset()
{
string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/(RectInt-2023-06-19-less-0.99)";
if (!string.IsNullOrEmpty(_Configuration.ImmichAssetsFile) && File.Exists(_Configuration.ImmichAssetsFile))
{
Dictionary<string, ImmichAsset> keyValuePairs = [];
string json = File.ReadAllText(_Configuration.ImmichAssetsFile);
ImmichAsset[]? immichAssets = JsonSerializer.Deserialize(json, ImmichAssetCollectionSourceGenerationContext.Default.ImmichAssetArray);
if (immichAssets is not null)
{
foreach (ImmichAsset immichAsset in immichAssets)
keyValuePairs.Add(immichAsset.OriginalPath, immichAsset);
}
Assert.IsTrue(keyValuePairs.Count > 0);
}
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodRenameDelete()
{
string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A)Property/{_Git}", "/{}");
if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory))
{
string[] files = Directory.GetFiles(directory, "*.unk", SearchOption.AllDirectories);
string checkFile;
string[] files = Directory.GetFiles(directory, "*.del", SearchOption.AllDirectories);
foreach (string file in files)
File.Move(file, file[..^4]);
{
checkFile = file[..^4];
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
}
Assert.IsTrue(true);
}
NonThrowTryCatch();
}
@ -347,19 +357,58 @@ public partial class UnitTestHardCoded
}
[TestMethod]
public void TestMethodImmichAsset()
public void TestMethodRenameAbandoned()
{
if (!string.IsNullOrEmpty(_Configuration.ImmichAssetsFile) && File.Exists(_Configuration.ImmichAssetsFile))
string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/!/Abandoned");
if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory))
{
Dictionary<string, ImmichAsset> keyValuePairs = [];
string json = File.ReadAllText(_Configuration.ImmichAssetsFile);
ImmichAsset[]? immichAssets = JsonSerializer.Deserialize(json, ImmichAssetCollectionSourceGenerationContext.Default.ImmichAssetArray);
if (immichAssets is not null)
string checkFile;
string[] files = Directory.GetFiles(directory, "*.abd", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
foreach (ImmichAsset immichAsset in immichAssets)
keyValuePairs.Add(immichAsset.OriginalPath, immichAsset);
checkFile = file[..^4];
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
}
Assert.IsTrue(keyValuePairs.Count > 0);
Assert.IsTrue(true);
}
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodRenameForUnknown()
{
string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/(RectInt-2023-06-19-less-0.99)";
if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory))
{
string[] files = Directory.GetFiles(directory, "*.unk", SearchOption.AllDirectories);
foreach (string file in files)
File.Move(file, file[..^4]);
}
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodGetApproximateYears()
{
string personDisplayDirectory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/^/Sydney Dupray^9");
if (Directory.Exists(Directory.GetDirectoryRoot(personDisplayDirectory)) && Directory.Exists(personDisplayDirectory))
{
char numberSign = '#';
string? minusOne = null;
char[] personCharacters = ['^'];
string personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory);
string personBirthdayFormat = _Configuration.PropertyConfiguration.PersonBirthdayFormat;
string[] personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
List<(string, PersonBirthday)> collection = IPersonBirthday.GetPersonBirthdays(personBirthdayFormat, personKeyDirectories, personDisplayDirectoryName);
int? approximateYears = IAge.GetApproximateYears(personCharacters, personDisplayDirectoryName);
if (approximateYears is null)
throw new NullReferenceException(nameof(approximateYears));
Assert.IsNotNull(approximateYears);
Assert.AreEqual(9, approximateYears.Value);
string? change = IPersonContainer.VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, collection);
Assert.IsNull(change);
}
NonThrowTryCatch();
}

View File

@ -56,13 +56,6 @@ public class UnitTestResize
_PropertyConfiguration = propertyConfiguration;
}
private static void NonThrowTryCatch()
{
try
{ throw new Exception(); }
catch (Exception) { }
}
[TestMethod]
public void TestMethodNull()
{
@ -75,13 +68,80 @@ public class UnitTestResize
NonThrowTryCatch();
}
private A_Property GetPropertyLogic(bool reverse, string aResultsFullGroupDirectory)
private static void NonThrowTryCatch()
{
A_Property result;
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_PropertyConfiguration));
result = new(_AppSettings.MaxDegreeOfParallelism, _PropertyConfiguration, _Configuration.OutputExtension, reverse, aResultsFullGroupDirectory);
return result;
try
{ throw new Exception(); }
catch (Exception) { }
}
[TestMethod]
public void TestMethodResize()
{
if (_PropertyConfiguration.NumberOfJitters is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters));
if (_PropertyConfiguration.NumberOfTimesToUpsample is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample));
// string sourceFileName = "100000507001158650387.jpg";
// string sourceDirectoryName = "Facebook/2023.2 Summer Facebook";
string sourceFileName = "105131603001106320328.jpg";
string sourceDirectoryName = "Mike iCloud Have Date Taken 2022 !9";
Item item;
bool reverse = false;
bool isArchive = false;
FileHolder resizedFileHolder;
long ticks = DateTime.Now.Ticks;
List<string> parseExceptions = [];
const bool isValidImageFormatExtension = true;
List<Tuple<string, DateTime>> subFileTuples = [];
int length = _PropertyConfiguration.RootDirectory.Length;
string[] changesFrom = [nameof(A_Property)];
string outputResolution = _Configuration.OutputResolutions[0];
bool outputResolutionHasNumber = outputResolution.Any(char.IsNumber);
(string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution);
(string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories();
Shared.Models.Methods.IBlurHasher blurHasher = new BlurHash.Models.C2_BlurHasher(_PropertyConfiguration);
A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory);
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}");
if (!Directory.Exists(aPropertySingletonDirectory))
_ = Directory.CreateDirectory(aPropertySingletonDirectory);
(ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(_Configuration.OutputExtension, _Configuration.OutputQuality);
B_Metadata metadata = new(null, _PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, ticks, bResultsFullGroupDirectory);
_ = metadata.ToString();
C_Resize resize = new(_PropertyConfiguration, _Configuration.ForceResizeLastWriteTimeToCreationTime, _Configuration.OverrideForResizeImages, _Configuration.PropertiesChangedForResize, _Configuration.ValidResolutions, imageCodecInfo, encoderParameters, filenameExtension);
_ = resize.ToString();
bool isUniqueFileName = false;
bool? isNotUniqueAndNeedsReview = null;
FileHolder sourceDirectoryFileHolder = IFileHolder.Get(".json");
string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName));
FileHolder fileHolder = IFileHolder.Get(Path.Combine(sourceDirectory, sourceFileName));
FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null);
Assert.IsNotNull(filePath.Id);
string relativePath = IPath.GetRelativePath(fileHolder.FullName, length);
ExifDirectory? exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath);
string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName));
propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory);
if (outputResolutionHasNumber)
resize.SetAngleBracketCollection(cResultsFullGroupDirectory, sourceDirectory);
resize.Update(cResultsFullGroupDirectory);
blurHasher.Update(cResultsFullGroupDirectory);
item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, exifDirectory, false, false, false);
if (item.ExifDirectory is null)
throw new NullReferenceException(nameof(item.ExifDirectory));
resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value);
item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder);
MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item);
Dictionary<string, int[]> outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem);
Assert.IsNotNull(mappingFromItem.ResizedFileHolder);
resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize);
string blurHash = blurHasher.Encode(resizedFileHolder);
Assert.IsNotNull(blurHash);
exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem);
string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase);
File.WriteAllText("../../../.json", json);
MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory);
double? distance = geoLocation is null ? null : Metadata.Models.Stateless.Methods.IMetadata.GetDistance(1, 1, geoLocation.Latitude, geoLocation.Longitude);
NonThrowTryCatch();
}
private (string, string) GetResultsFullGroupDirectories()
@ -117,77 +177,13 @@ public class UnitTestResize
return new(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory);
}
[TestMethod]
public void TestMethodResize()
private A_Property GetPropertyLogic(bool reverse, string aResultsFullGroupDirectory)
{
if (_PropertyConfiguration.NumberOfJitters is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters));
if (_PropertyConfiguration.NumberOfTimesToUpsample is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample));
// string sourceFileName = "100000507001158650387.jpg";
// string sourceDirectoryName = "Facebook/2023.2 Summer Facebook";
string sourceFileName = "105131603001106320328.jpg";
string sourceDirectoryName = "Mike iCloud Have Date Taken 2022 !9";
Item item;
bool reverse = false;
bool isArchive = false;
FileHolder resizedFileHolder;
List<string> parseExceptions = [];
Shared.Models.Property? property = null;
const bool isValidImageFormatExtension = true;
List<Tuple<string, DateTime>> subFileTuples = [];
int length = _PropertyConfiguration.RootDirectory.Length;
string[] changesFrom = [nameof(A_Property)];
string outputResolution = _Configuration.OutputResolutions[0];
bool outputResolutionHasNumber = outputResolution.Any(char.IsNumber);
(string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution);
(string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories();
Shared.Models.Methods.IBlurHasher blurHasher = new BlurHash.Models.C2_BlurHasher(_PropertyConfiguration);
A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory);
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}");
if (!Directory.Exists(aPropertySingletonDirectory))
_ = Directory.CreateDirectory(aPropertySingletonDirectory);
(ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(_Configuration.OutputExtension, _Configuration.OutputQuality);
B_Metadata metadata = new(_PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory);
_ = metadata.ToString();
C_Resize resize = new(_PropertyConfiguration, _Configuration.ForceResizeLastWriteTimeToCreationTime, _Configuration.OverrideForResizeImages, _Configuration.PropertiesChangedForResize, _Configuration.ValidResolutions, imageCodecInfo, encoderParameters, filenameExtension);
_ = resize.ToString();
bool isUniqueFileName = false;
bool? isNotUniqueAndNeedsReview = null;
FileHolder sourceDirectoryFileHolder = IFileHolder.Get(".json");
string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName));
FileHolder fileHolder = IFileHolder.Get(Path.Combine(sourceDirectory, sourceFileName));
FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null);
Assert.IsNotNull(filePath.Id);
string relativePath = IPath.GetRelativePath(fileHolder.FullName, length);
string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName));
propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory);
if (outputResolutionHasNumber)
resize.SetAngleBracketCollection(cResultsFullGroupDirectory, sourceDirectory);
resize.Update(cResultsFullGroupDirectory);
blurHasher.Update(cResultsFullGroupDirectory);
item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, property, false, false, false);
if (item.Property is null)
{
property = propertyLogic.GetProperty(metadata, item, subFileTuples, parseExceptions);
item.Update(property);
}
if (property is null || item.Property is null)
throw new NullReferenceException(nameof(property));
resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value);
item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder);
MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item);
Dictionary<string, int[]> outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.Property, mappingFromItem);
Assert.IsNotNull(mappingFromItem.ResizedFileHolder);
resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize);
string blurHash = blurHasher.Encode(resizedFileHolder);
Assert.IsNotNull(blurHash);
ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem);
string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase);
File.WriteAllText("../../../.json", json);
MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory);
double? distance = geoLocation is null ? null : Metadata.Models.Stateless.Methods.IMetadata.GetDistance(1, 1, geoLocation.Latitude, geoLocation.Longitude);
NonThrowTryCatch();
A_Property result;
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_PropertyConfiguration));
result = new(_AppSettings.MaxDegreeOfParallelism, _PropertyConfiguration, _Configuration.OutputExtension, reverse, aResultsFullGroupDirectory);
return result;
}
}

View File

@ -39,7 +39,6 @@
<ProjectReference Include="..\BlurHash\BlurHash.csproj" />
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" />
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Resize\Resize.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
</ItemGroup>

View File

@ -58,33 +58,113 @@ public class UnitTestFace
_PropertyConfiguration = propertyConfiguration;
}
private static void NonThrowTryCatch()
{
try
{ throw new Exception(); }
catch (Exception) { }
}
[TestMethod]
public void TestConfiguration()
public void TestMethodFace()
{
if (_Configuration.LocationDigits != Shared.Models.Stateless.ILocation.Digits)
throw new Exception("Configuration has to match interface!");
if (_Configuration.LocationFactor != Shared.Models.Stateless.ILocation.Factor)
throw new Exception("Configuration has to match interface!");
if (_PropertyConfiguration.NumberOfJitters is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters));
if (_PropertyConfiguration.NumberOfTimesToUpsample is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample));
string sourceFileName = "100000507001158650387.jpg";
string sourceDirectoryName = "Facebook/2023.2 Summer Facebook";
Item item;
bool reverse = false;
bool isArchive = false;
FileHolder resizedFileHolder;
long ticks = DateTime.Now.Ticks;
List<string> parseExceptions = [];
const bool isValidImageFormatExtension = true;
List<Tuple<string, DateTime>> subFileTuples = [];
int length = _PropertyConfiguration.RootDirectory.Length;
string[] changesFrom = [nameof(A_Property)];
string outputResolution = _Configuration.OutputResolutions[0];
bool outputResolutionHasNumber = outputResolution.Any(char.IsNumber);
(string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution);
(string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories();
Shared.Models.Methods.IBlurHasher blurHasher = new BlurHash.Models.C2_BlurHasher(_PropertyConfiguration);
A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory);
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}");
if (!Directory.Exists(aPropertySingletonDirectory))
_ = Directory.CreateDirectory(aPropertySingletonDirectory);
(ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(_Configuration.OutputExtension, _Configuration.OutputQuality);
B_Metadata metadata = new(null, _PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, ticks, bResultsFullGroupDirectory);
_ = metadata.ToString();
C_Resize resize = new(_PropertyConfiguration, _Configuration.ForceResizeLastWriteTimeToCreationTime, _Configuration.OverrideForResizeImages, _Configuration.PropertiesChangedForResize, _Configuration.ValidResolutions, imageCodecInfo, encoderParameters, filenameExtension);
_ = resize.ToString();
bool isUniqueFileName = false;
bool? isNotUniqueAndNeedsReview = null;
FileHolder sourceDirectoryFileHolder = IFileHolder.Get(".json");
string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName));
FileHolder fileHolder = IFileHolder.Get(Path.Combine(sourceDirectory, sourceFileName));
FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null);
Assert.IsNotNull(filePath.Id);
string relativePath = IPath.GetRelativePath(fileHolder.FullName, length);
ExifDirectory? exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath);
string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName));
propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory);
if (outputResolutionHasNumber)
resize.SetAngleBracketCollection(cResultsFullGroupDirectory, sourceDirectory);
resize.Update(cResultsFullGroupDirectory);
blurHasher.Update(cResultsFullGroupDirectory);
item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, exifDirectory, false, false, false);
if (item.ExifDirectory is null)
throw new NullReferenceException(nameof(item.ExifDirectory));
resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value);
item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder);
MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item);
Dictionary<string, int[]> outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem);
Assert.IsNotNull(mappingFromItem.ResizedFileHolder);
resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize);
string blurHash = blurHasher.Encode(resizedFileHolder);
Assert.IsNotNull(blurHash);
exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem);
string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase);
File.WriteAllText("../../../.json", json);
Image image = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName);
Assert.IsNotNull(image);
(Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(_Configuration);
FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, model, modelParameter, predictorModel);
List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary<FacePart, FacePoint[]>? FaceParts)> collection;
collection = faceRecognition.GetCollection(image, locations: [], includeFaceEncoding: true, includeFaceParts: true);
Assert.IsTrue(collection.Count == 2);
List<FaceDistance> faceDistanceEncodings = (from l in collection where l.FaceEncoding is not null select new FaceDistance(l.FaceEncoding)).ToList();
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncodings[0]);
Assert.IsTrue(faceDistanceLengths.Count == 2);
Assert.IsNotNull(sourceFileName);
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodNull()
private (string, string) GetResultsFullGroupDirectories()
{
Assert.IsFalse(_AppSettings is null);
Assert.IsFalse(_Configuration is null);
Assert.IsFalse(_IsEnvironment is null);
Assert.IsFalse(_WorkingDirectory is null);
Assert.IsFalse(_ConfigurationRoot is null);
Assert.IsFalse(_PropertyConfiguration is null);
NonThrowTryCatch();
string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
_PropertyConfiguration,
nameof(A_Property),
string.Empty,
includeResizeGroup: false,
includeModel: false,
includePredictorModel: false);
string bResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
_PropertyConfiguration,
nameof(B_Metadata),
string.Empty,
includeResizeGroup: false,
includeModel: false,
includePredictorModel: false);
return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory);
}
private (string, string, string) GetResultsFullGroupDirectories(string outputResolution)
{
string cResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
_PropertyConfiguration,
nameof(C_Resize),
outputResolution,
includeResizeGroup: true,
includeModel: false,
includePredictorModel: false);
string dResultsFullGroupDirectory = string.Empty;
string d2ResultsFullGroupDirectory = string.Empty;
return new(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory);
}
private A_Property GetPropertyLogic(bool reverse, string aResultsFullGroupDirectory)
@ -137,6 +217,25 @@ public class UnitTestFace
return result;
}
private static void NonThrowTryCatch()
{
try
{ throw new Exception(); }
catch (Exception) { }
}
[TestMethod]
public void TestMethodNull()
{
Assert.IsFalse(_AppSettings is null);
Assert.IsFalse(_Configuration is null);
Assert.IsFalse(_IsEnvironment is null);
Assert.IsFalse(_WorkingDirectory is null);
Assert.IsFalse(_ConfigurationRoot is null);
Assert.IsFalse(_PropertyConfiguration is null);
NonThrowTryCatch();
}
[TestMethod]
public void TestMethodRoundB()
{
@ -159,116 +258,13 @@ public class UnitTestFace
NonThrowTryCatch();
}
private (string, string) GetResultsFullGroupDirectories()
{
string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
_PropertyConfiguration,
nameof(A_Property),
string.Empty,
includeResizeGroup: false,
includeModel: false,
includePredictorModel: false);
string bResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
_PropertyConfiguration,
nameof(B_Metadata),
string.Empty,
includeResizeGroup: false,
includeModel: false,
includePredictorModel: false);
return new(aResultsFullGroupDirectory, bResultsFullGroupDirectory);
}
private (string, string, string) GetResultsFullGroupDirectories(string outputResolution)
{
string cResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(
_PropertyConfiguration,
nameof(C_Resize),
outputResolution,
includeResizeGroup: true,
includeModel: false,
includePredictorModel: false);
string dResultsFullGroupDirectory = string.Empty;
string d2ResultsFullGroupDirectory = string.Empty;
return new(cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory);
}
[TestMethod]
public void TestMethodFace()
public void TestConfiguration()
{
if (_PropertyConfiguration.NumberOfJitters is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters));
if (_PropertyConfiguration.NumberOfTimesToUpsample is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample));
string sourceFileName = "100000507001158650387.jpg";
string sourceDirectoryName = "Facebook/2023.2 Summer Facebook";
Item item;
bool reverse = false;
bool isArchive = false;
FileHolder resizedFileHolder;
List<string> parseExceptions = [];
Shared.Models.Property? property = null;
const bool isValidImageFormatExtension = true;
List<Tuple<string, DateTime>> subFileTuples = [];
int length = _PropertyConfiguration.RootDirectory.Length;
string[] changesFrom = [nameof(A_Property)];
string outputResolution = _Configuration.OutputResolutions[0];
bool outputResolutionHasNumber = outputResolution.Any(char.IsNumber);
(string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution);
(string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories();
Shared.Models.Methods.IBlurHasher blurHasher = new BlurHash.Models.C2_BlurHasher(_PropertyConfiguration);
A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory);
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}");
if (!Directory.Exists(aPropertySingletonDirectory))
_ = Directory.CreateDirectory(aPropertySingletonDirectory);
(ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(_Configuration.OutputExtension, _Configuration.OutputQuality);
B_Metadata metadata = new(_PropertyConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata, bResultsFullGroupDirectory);
_ = metadata.ToString();
C_Resize resize = new(_PropertyConfiguration, _Configuration.ForceResizeLastWriteTimeToCreationTime, _Configuration.OverrideForResizeImages, _Configuration.PropertiesChangedForResize, _Configuration.ValidResolutions, imageCodecInfo, encoderParameters, filenameExtension);
_ = resize.ToString();
bool isUniqueFileName = false;
bool? isNotUniqueAndNeedsReview = null;
FileHolder sourceDirectoryFileHolder = IFileHolder.Get(".json");
string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName));
FileHolder fileHolder = IFileHolder.Get(Path.Combine(sourceDirectory, sourceFileName));
FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null);
Assert.IsNotNull(filePath.Id);
string relativePath = IPath.GetRelativePath(fileHolder.FullName, length);
string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName));
propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory);
if (outputResolutionHasNumber)
resize.SetAngleBracketCollection(cResultsFullGroupDirectory, sourceDirectory);
resize.Update(cResultsFullGroupDirectory);
blurHasher.Update(cResultsFullGroupDirectory);
item = Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, property, false, false, false);
if (item.Property is null)
{
property = propertyLogic.GetProperty(metadata, item, subFileTuples, parseExceptions);
item.Update(property);
}
if (property is null || item.Property is null)
throw new NullReferenceException(nameof(property));
resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value);
item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder);
MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item);
Dictionary<string, int[]> outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.Property, mappingFromItem);
Assert.IsNotNull(mappingFromItem.ResizedFileHolder);
resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize);
string blurHash = blurHasher.Encode(resizedFileHolder);
Assert.IsNotNull(blurHash);
ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem);
string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase);
File.WriteAllText("../../../.json", json);
Image image = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName);
Assert.IsNotNull(image);
(Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(_Configuration);
FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, model, modelParameter, predictorModel);
List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary<FacePart, FacePoint[]>? FaceParts)> collection;
collection = faceRecognition.GetCollection(image, locations: [], includeFaceEncoding: true, includeFaceParts: true);
Assert.IsTrue(collection.Count == 2);
List<FaceDistance> faceDistanceEncodings = (from l in collection where l.FaceEncoding is not null select new FaceDistance(l.FaceEncoding)).ToList();
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(new(faceDistanceEncodings), faceDistanceEncodings[0]);
Assert.IsTrue(faceDistanceLengths.Count == 2);
Assert.IsNotNull(sourceFileName);
if (_Configuration.LocationDigits != Shared.Models.Stateless.ILocation.Digits)
throw new Exception("Configuration has to match interface!");
if (_Configuration.LocationFactor != Shared.Models.Stateless.ILocation.Factor)
throw new Exception("Configuration has to match interface!");
NonThrowTryCatch();
}

View File

@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlurHash", "BlurHash\BlurHa
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Copy-Distinct", "Copy-Distinct\Copy-Distinct.csproj", "{E08CB662-FF25-48DF-A378-A770E1EFBA56}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Date-Group", "Date-Group\Date-Group.csproj", "{DFEDB5F9-AFFC-40A2-9FEC-9B84C83B63D9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delete-By-Distinct", "Delete-By-Distinct\Delete-By-Distinct.csproj", "{3F00BDD5-75F8-470C-ACED-1A26FDC8D7B3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delete-By-Relative", "Delete-By-Relative\Delete-By-Relative.csproj", "{9DFCA595-80AA-4E78-A9AF-5B4AB4D737C4}"
@ -23,8 +21,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Search", "Drag-Dr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Set-Property-Item", "Drag-Drop-Set-Property-Item\Drag-Drop-Set-Property-Item.csproj", "{BFF75D8C-48E6-4B84-B480-3E5A4F9B2DD8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicate-Search", "Duplicate-Search\Duplicate-Search.csproj", "{48E87D9B-B802-467A-BDC7-E86F7FD01D5C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Face", "Face\Face.csproj", "{A12E19E5-59C0-40D4-B807-DF1334D4906D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FaceParts", "FaceParts\FaceParts.csproj", "{919525B1-60BA-40C6-BA66-6F7F4C526E01}"
@ -51,14 +47,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrepareForOld", "PrepareFor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Property-Compare", "Property-Compare\Property-Compare.csproj", "{692AA058-F142-44B0-88BC-F22AB5BE5EDF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Property", "Property\Property.csproj", "{964B969A-719C-48AF-86C0-F97AF1397347}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rename", "Rename\Rename.csproj", "{83FD089F-8034-4597-B87F-87D343C0486B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resize", "Resize\Resize.csproj", "{27D0D869-394D-4B07-83DF-2095B16026FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Set-Created-Date", "Set-Created-Date\Set-Created-Date.csproj", "{B067643E-9F59-46A1-A001-ACF4661F059C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{B4FB6B43-36EC-404D-B934-5C695C6E32CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsWithFaceRecognitionDotNet", "TestsWithFaceRecognitionDotNet\TestsWithFaceRecognitionDotNet.csproj", "{A67D73C7-A1A1-4443-B681-776339CFA08A}"