Compare commits

...

9 Commits

Author SHA1 Message Date
c7ded16e50 Switch to ExifDirectory from Property 2025-04-06 18:23:57 -07:00
3f7affceef Enum change to last character 2025-03-22 17:12:26 -07:00
130e3b6528 Mostly Sorting
Video Merge as 4, 5, and 6
2025-03-22 16:50:47 -07:00
d5fa108f81 added-numbers-to-distinguish-has-ignore-keyword-and-has-date-time-original
moved-container-to-new-project-to-prepare-to-remove-property-file

added-logic-to-rename-to-3-and-7-like-should-ignore-for-missing-date-time-original
2025-03-16 21:20:46 -07:00
d9d55d9e4c hide-splat-nine-and-get-moved-duplicates-with-same-splat-nine 2025-03-08 20:40:51 -07:00
e728838d25 CombinedEnumAndIndex 2025-03-08 16:44:08 -07:00
ac298166e9 Exif Size 2025-03-02 08:38:01 -07:00
0b793904b3 Format 2025-03-02 08:37:36 -07:00
e053dd5746 Bump 2025-03-02 08:32:03 -07:00
114 changed files with 9191 additions and 7385 deletions

3
.gitignore vendored
View File

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

16
.vscode/mklink.md vendored
View File

@ -13,3 +13,19 @@ mklink /J "D:\1-Images-A\Images-4083e56a-Results\A2)People\4083e56a\{}\!" "D:\1-
```bash ```bash
mklink /J "L:\Git\View-by-Distance-MKLink-Console\.Immich" "D:\1-Images-A\Images-c9dbce3b-Results\F)Immich\c9dbce3b\{}" mklink /J "L:\Git\View-by-Distance-MKLink-Console\.Immich" "D:\1-Images-A\Images-c9dbce3b-Results\F)Immich\c9dbce3b\{}"
``` ```
```bash 1740946894364 = 638765436943640000 = 2025-0.Winter = Sun Mar 02 2025 13:21:33 GMT-0700 (Mountain Standard Time)
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"
```

80
.vscode/tasks.json vendored
View File

@ -72,11 +72,7 @@
"build", "build",
"${workspaceFolder}/Instance/Instance.csproj", "${workspaceFolder}/Instance/Instance.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary", "/consoleloggerparameters:NoSummary"
"/property:WarningLevel=0",
"--verbosity",
"quiet",
"--no-restore"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
@ -88,11 +84,7 @@
"build", "build",
"${workspaceFolder}/Shared/View-by-Distance.Shared.csproj", "${workspaceFolder}/Shared/View-by-Distance.Shared.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary", "/consoleloggerparameters:NoSummary"
"/property:WarningLevel=0",
"--verbosity",
"quiet",
"--no-restore"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
@ -104,13 +96,71 @@
"build", "build",
"${workspaceFolder}/Metadata/Metadata.csproj", "${workspaceFolder}/Metadata/Metadata.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary", "/consoleloggerparameters:NoSummary"
"/property:WarningLevel=0",
"--verbosity",
"quiet",
"--no-restore"
], ],
"problemMatcher": "$msCompile" "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",
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
"args": [
"s",
"X",
"L:/Git/View-by-Distance-MKLink-Console",
"Day-Helper-2025-03-20",
"false",
"4"
],
"problemMatcher": []
} }
] ]
} }

View File

@ -34,7 +34,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -11,20 +11,28 @@ public class C2_BlurHasher : IBlurHasher
{ {
private readonly IPropertyConfiguration _PropertyConfiguration; private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly Dictionary<string, ReadOnlyCollection<string>> _FileGroups; private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultContentFileGroups;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups;
public C2_BlurHasher(IPropertyConfiguration propertyConfiguration) public C2_BlurHasher(IPropertyConfiguration propertyConfiguration)
{ {
_FileGroups = [];
_PropertyConfiguration = propertyConfiguration; _PropertyConfiguration = propertyConfiguration;
_ResultContentFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
_ResultSingletonFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
} }
public void Update(string resultsFullGroupDirectory) public void Update(string resultsFullGroupDirectory)
{ {
_FileGroups.Clear(); ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, resultsFullGroupDirectory, [_PropertyConfiguration.ResultContent, _PropertyConfiguration.ResultSingleton]);
ReadOnlyDictionary<string, ReadOnlyCollection<string>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, resultsFullGroupDirectory, [_PropertyConfiguration.ResultContent, _PropertyConfiguration.ResultSingleton]); foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
foreach (KeyValuePair<string, ReadOnlyCollection<string>> keyValuePair in keyValuePairs) {
_FileGroups.Add(keyValuePair.Key, keyValuePair.Value); if (keyValuePair.Key == _PropertyConfiguration.ResultContent)
_ResultContentFileGroups[0] = keyValuePair.Value;
else if (keyValuePair.Key == _PropertyConfiguration.ResultSingleton)
_ResultSingletonFileGroups[0] = keyValuePair.Value;
else
throw new Exception();
}
} }
string IBlurHasher.Encode(FileHolder fileHolder) string IBlurHasher.Encode(FileHolder fileHolder)
@ -40,20 +48,39 @@ public class C2_BlurHasher : IBlurHasher
return result; return result;
} }
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, string fullFileName)
{
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 && Directory.Exists(checkDirectory))
{
string checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
File.Move(checkFile, fullFileName);
}
}
string IBlurHasher.GetFile(FilePath filePath) string IBlurHasher.GetFile(FilePath filePath)
{ {
string result; string result;
if (_FileGroups.Count == 0) if (_ResultSingletonFileGroups[0].Count == 0)
throw new Exception("Call Update first!"); throw new Exception("Call Update first!");
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath); string fileName = $"{filePath.Name}.csv";
result = Path.Combine(_FileGroups[_PropertyConfiguration.ResultSingleton][directoryIndex], $"{filePath.Name}.csv"); CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
string directory = _ResultSingletonFileGroups[0][cei.Enum][cei.Index];
result = Path.Combine(directory, fileName);
MoveIf(fileName, cei, directory, result);
return result; return result;
} }
string IBlurHasher.EncodeAndSave(FilePath filePath, FileHolder fileHolder) string IBlurHasher.EncodeAndSave(FilePath filePath, FileHolder fileHolder)
{ {
string result; string result;
if (_FileGroups.Count == 0) if (_ResultSingletonFileGroups[0].Count == 0)
throw new Exception("Call Update first!"); throw new Exception("Call Update first!");
int actualByte; int actualByte;
string extension = ".png"; string extension = ".png";
@ -70,15 +97,18 @@ public class C2_BlurHasher : IBlurHasher
byte[] blurHashBytes = Encoding.UTF8.GetBytes(result); byte[] blurHashBytes = Encoding.UTF8.GetBytes(result);
string joined = string.Join(string.Empty, blurHashBytes.Select(l => l.ToString("000"))); string joined = string.Join(string.Empty, blurHashBytes.Select(l => l.ToString("000")));
string fileNameWithoutExtension = $"{componentsX}x{componentsY}-{outputWidth}x{outputHeight}-{joined}"; string fileNameWithoutExtension = $"{componentsX}x{componentsY}-{outputWidth}x{outputHeight}-{joined}";
string fileName = $"{fileNameWithoutExtension}{extension}";
string contents = string.Concat(result, Environment.NewLine, fileNameWithoutExtension, Environment.NewLine, extension); string contents = string.Concat(result, Environment.NewLine, fileNameWithoutExtension, Environment.NewLine, extension);
_ = Shared.Models.Stateless.Methods.IPath.WriteAllText(file, contents, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(file, contents, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath); CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
file = Path.Combine(_FileGroups[_PropertyConfiguration.ResultContent][directoryIndex], $"{fileNameWithoutExtension}{extension}"); string directory = _ResultContentFileGroups[0][cei.Enum][cei.Index];
if (!File.Exists(file)) string fullFileName = Path.Combine(directory, fileName);
MoveIf(fileName, cei, directory, fullFileName);
if (!File.Exists(fullFileName))
{ {
try try
{ {
using FileStream fileStream = new(file, FileMode.CreateNew); using FileStream fileStream = new(fullFileName, FileMode.CreateNew);
actualImage.Save(fileStream, System.Drawing.Imaging.ImageFormat.Png); actualImage.Save(fileStream, System.Drawing.Imaging.ImageFormat.Png);
_ = fileStream.Seek(0, SeekOrigin.Begin); _ = fileStream.Seek(0, SeekOrigin.Begin);
actualByte = fileStream.ReadByte(); actualByte = fileStream.ReadByte();

View File

@ -160,7 +160,7 @@ public class Compare
} }
if (_IsEnvironment.Development && propertyConfiguration.PopulatePropertyId) if (_IsEnvironment.Development && propertyConfiguration.PopulatePropertyId)
throw new Exception("Copy keyValuePairs-####.json file"); throw new Exception("Copy keyValuePairs-####.json file");
(int j, int f, int t, Shared.Models.Container[] containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, propertyLogic); (int j, int f, int t, Shared.Models.Container[] containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, propertyLogic);
if (propertyLogic.ExceptionsDirectories.Any()) if (propertyLogic.ExceptionsDirectories.Any())
throw new Exception(); throw new Exception();
if (propertyConfiguration.PopulatePropertyId && Shared.Models.Stateless.Methods.IProperty.Any(containers)) if (propertyConfiguration.PopulatePropertyId && Shared.Models.Stateless.Methods.IProperty.Any(containers))
@ -242,6 +242,53 @@ public class Compare
_ = Directory.CreateDirectory(currentYearDirectory); _ = Directory.CreateDirectory(currentYearDirectory);
} }
private static void Verify(Models.Configuration configuration)
{
if (configuration.Spelling is null || !configuration.Spelling.Any())
throw new NullReferenceException(nameof(configuration.Spelling));
}
private A_Property GetPropertyLogic(bool reverse, string outputExtension, Map.Models.MapLogic mapLogic)
{
A_Property result;
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
result = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, outputExtension, reverse);
string fromPrepareForOld = "34720-637858334555170379.tsv";
string fromPrepareForOldFile = Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, fromPrepareForOld);
if (File.Exists(fromPrepareForOldFile))
{
string[] lines;
string[] columns;
List<string> debug = new();
long ticks = DateTime.Now.Ticks;
lines = File.ReadAllLines(fromPrepareForOldFile);
string resultsDirectory = $"{_Configuration.PropertyConfiguration.RootDirectory}-Results";
int[]? zeros = (from l in mapLogic.IndicesFromNew where l.Value.Any() select l.Value[0]).ToArray();
lines = (from l in mapLogic.IndicesFromNew select string.Concat(l.Key, '\t', string.Join('\t', l.Value))).ToArray();
if (!Directory.Exists(resultsDirectory))
_ = Directory.CreateDirectory(resultsDirectory);
File.WriteAllLines(Path.Combine(resultsDirectory, $"{ticks}.tsv"), lines);
string json = JsonSerializer.Serialize(mapLogic.IndicesFromNew, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(Path.Combine(resultsDirectory, $"{ticks}.json"), json);
foreach (string line in lines)
{
columns = line.Split('\t');
// select $"{l.Index}\t{l.PropertyId}\t{l.RegexResult}\t{new DateTime(l.Ticks):yyyy-MM-dd_HH-mm-ss}\t{l.PropertyTicks}\t{l.RelativeDirectory}\t{l.FileName}"
if (columns.Length != 7)
continue;
if (!int.TryParse(columns[1], out int propertyId))
continue;
if (!zeros.Contains(propertyId))
debug.Add(line);
else
debug.Add(propertyId.ToString());
}
File.WriteAllLines(Path.Combine(resultsDirectory, $"{ticks}-{fromPrepareForOld}"), debug);
}
return result;
}
private string GetRename(string renameA) private string GetRename(string renameA)
{ {
string result; string result;
@ -376,12 +423,6 @@ public class Compare
return result; return result;
} }
private static void Verify(Models.Configuration configuration)
{
if (configuration.Spelling is null || !configuration.Spelling.Any())
throw new NullReferenceException(nameof(configuration.Spelling));
}
private long LogDelta(long ticks, string? methodName) private long LogDelta(long ticks, string? methodName)
{ {
long result; long result;
@ -391,121 +432,6 @@ public class Compare
return result; return result;
} }
private A_Property GetPropertyLogic(bool reverse, string outputExtension, Map.Models.MapLogic mapLogic)
{
A_Property result;
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
result = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, outputExtension, reverse);
string fromPrepareForOld = "34720-637858334555170379.tsv";
string fromPrepareForOldFile = Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, fromPrepareForOld);
if (File.Exists(fromPrepareForOldFile))
{
string[] lines;
string[] columns;
List<string> debug = new();
long ticks = DateTime.Now.Ticks;
lines = File.ReadAllLines(fromPrepareForOldFile);
string resultsDirectory = $"{_Configuration.PropertyConfiguration.RootDirectory}-Results";
int[]? zeros = (from l in mapLogic.IndicesFromNew where l.Value.Any() select l.Value[0]).ToArray();
lines = (from l in mapLogic.IndicesFromNew select string.Concat(l.Key, '\t', string.Join('\t', l.Value))).ToArray();
if (!Directory.Exists(resultsDirectory))
_ = Directory.CreateDirectory(resultsDirectory);
File.WriteAllLines(Path.Combine(resultsDirectory, $"{ticks}.tsv"), lines);
string json = JsonSerializer.Serialize(mapLogic.IndicesFromNew, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(Path.Combine(resultsDirectory, $"{ticks}.json"), json);
foreach (string line in lines)
{
columns = line.Split('\t');
// select $"{l.Index}\t{l.PropertyId}\t{l.RegexResult}\t{new DateTime(l.Ticks):yyyy-MM-dd_HH-mm-ss}\t{l.PropertyTicks}\t{l.RelativeDirectory}\t{l.FileName}"
if (columns.Length != 7)
continue;
if (!int.TryParse(columns[1], out int propertyId))
continue;
if (!zeros.Contains(propertyId))
debug.Add(line);
else
debug.Add(propertyId.ToString());
}
File.WriteAllLines(Path.Combine(resultsDirectory, $"{ticks}-{fromPrepareForOld}"), debug);
}
return result;
}
private void SaveDiffFilesOrSaveLogAndMoveFiles(Property.Models.Configuration configuration)
{
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}");
_Logger?.LogInformation(aPropertySingletonDirectory);
_Logger?.LogInformation("to");
_Logger?.LogInformation(_Configuration.DiffPropertyDirectory);
for (int y = 0; y < int.MaxValue; y++)
{
_Logger?.LogInformation("Press \"Y\" key to continue or close console if compare not needed");
if (Console.ReadKey().Key == ConsoleKey.Y)
break;
}
_Logger?.LogInformation(". . .");
int loadLessThan = 7;
string diffRootDirectory;
ConsoleKey? consoleKey = null;
List<PropertyCompare.Models.PropertyCompare>? duplicates = null;
PropertyCompare.Models.PropertyCompare[] diffPropertyCompareCollection;
if (string.IsNullOrEmpty(_Configuration.DiffPropertyDirectory))
diffRootDirectory = string.Empty;
else
{
if (!_Configuration.DiffPropertyDirectory.EndsWith("{}"))
throw new Exception("Invalid directory should end with {}!");
diffRootDirectory = Shared.Models.Stateless.Methods.IProperty.GetDiffRootDirectory(_Configuration.DiffPropertyDirectory);
}
PropertyCompare.Models.PropertyCompareLogic propertyCompareLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _SpellingFindReplace, diffRootDirectory);
if (string.IsNullOrEmpty(_Configuration.DiffPropertyDirectory) || !Directory.Exists(_Configuration.DiffPropertyDirectory))
diffPropertyCompareCollection = Array.Empty<PropertyCompare.Models.PropertyCompare>();
else
{
diffPropertyCompareCollection = propertyCompareLogic.Get(_Configuration.DiffPropertyDirectory, loadLessThan, duplicates, deleteExtension: false);
if (!diffPropertyCompareCollection.Any())
throw new Exception("Invalid directory!");
}
string aPropertyCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "[{}]");
PropertyCompare.Models.PropertyCompare[] propertyCompareCollection = propertyCompareLogic.Get(aPropertySingletonDirectory, loadLessThan, duplicates, deleteExtension: false);
{
long ticks = DateTime.Now.Ticks;
string[] lines = (from l in propertyCompareCollection select l.GetSelect()).ToArray();
File.WriteAllLines(Path.Join(aPropertyCollectionDirectory, $". . . Ids - {ticks}.txt"), lines);
string json = JsonSerializer.Serialize(propertyCompareCollection, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(Path.Join(aPropertyCollectionDirectory, $". . . Ids - {ticks}.nosj"), json);
}
for (int x = 0; x < int.MaxValue; x++)
{
_Logger?.LogInformation($"Press \"D\" key to {nameof(PropertyCompare.Models.PropertyCompareLogic.SaveDiffFiles)}");
_Logger?.LogInformation($"Press \"M\" key to {nameof(PropertyCompare.Models.PropertyCompareLogic.SaveLogAndMoveFiles)}");
_Logger?.LogInformation("Press \"End\" key when ready to skip");
consoleKey = Console.ReadKey().Key;
if (consoleKey is ConsoleKey.D or ConsoleKey.M or ConsoleKey.End)
break;
}
_Logger?.LogInformation(". . .");
if (consoleKey.HasValue && consoleKey.Value == ConsoleKey.D)
propertyCompareLogic.SaveDiffFiles(aPropertyCollectionDirectory, loadLessThan, propertyCompareCollection, diffPropertyCompareCollection);
else if (consoleKey.HasValue && consoleKey.Value == ConsoleKey.M)
{
for (int x = 0; x < int.MaxValue; x++)
{
_Logger?.LogInformation($"Press \"0 - {loadLessThan}\" key when ready to continue");
_Logger?.LogInformation("Press \"End\" key when ready to skip");
consoleKey = Console.ReadKey().Key;
if (consoleKey.Value is ConsoleKey.D0 or ConsoleKey.D1 or ConsoleKey.D2 or ConsoleKey.D3 or ConsoleKey.D4 or ConsoleKey.D5 or ConsoleKey.D6 or ConsoleKey.End)
break;
}
_Logger?.LogInformation(". . .");
int i = int.Parse(consoleKey.Value.ToString()[1..]);
propertyCompareLogic.SaveLogAndMoveFiles(aPropertyCollectionDirectory, loadLessThan, propertyCompareCollection, diffPropertyCompareCollection, i);
}
}
private void ChangeExtensionFromDeleteToJson(string aPropertySingletonDirectory) private void ChangeExtensionFromDeleteToJson(string aPropertySingletonDirectory)
{ {
string searchPattern = "*.delete"; string searchPattern = "*.delete";
@ -524,227 +450,6 @@ public class Compare
} }
} }
private bool PossiblyRename(List<string> topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
bool result = false;
string replaceFile;
string replaceDirectory;
int remainingDirectories = 0;
IEnumerable<(string Find, string Replace)>? found;
foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection)
{
if (!topDirectories.Any())
continue;
found = from l in _RenameFindReplace where sourceDirectory == l.Find select l;
if (!found.Any())
continue;
if (!result)
result = true;
replaceDirectory = found.First().Replace;
if (!Directory.Exists(replaceDirectory))
Directory.Move(sourceDirectory, replaceDirectory);
else
{
if (Directory.EnumerateDirectories(sourceDirectory).Any())
remainingDirectories += 1;
else
{
foreach (string sourceDirectoryFile in sourceDirectoryFiles)
{
replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile));
if (File.Exists(replaceFile))
{
if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg"));
else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg"));
}
if (File.Exists(replaceFile))
continue;
File.Move(sourceDirectoryFile, replaceFile);
}
}
}
}
return result;
}
private bool PossiblyRenameB(List<string> topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
bool result = false;
string replaceFile;
string replaceDirectory;
IEnumerable<(string Find, string Replace)>? found;
foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection)
{
if (!topDirectories.Any())
continue;
found = from l in _RenameBFindReplace where sourceDirectory == l.Find select l;
if (!found.Any())
continue;
if (!result)
result = true;
replaceDirectory = found.First().Replace;
if (!Directory.Exists(replaceDirectory))
_ = Directory.CreateDirectory(replaceDirectory);
foreach (string sourceDirectoryFile in sourceDirectoryFiles)
{
replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile));
if (File.Exists(replaceFile))
{
if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg"));
else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg"));
}
if (File.Exists(replaceFile))
continue;
File.Move(sourceDirectoryFile, replaceFile);
}
}
return result;
}
private bool PossiblyRenameC(List<string> topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
bool result = false;
string replaceFile;
string replaceDirectory;
IEnumerable<(string Find, string Replace)>? found;
foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection)
{
if (!topDirectories.Any())
continue;
found = from l in _RenameCFindReplace where sourceDirectory == l.Find select l;
if (!found.Any())
continue;
if (!result)
result = true;
replaceDirectory = found.First().Replace;
if (!Directory.Exists(replaceDirectory))
_ = Directory.CreateDirectory(replaceDirectory);
foreach (string sourceDirectoryFile in sourceDirectoryFiles)
{
replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile));
if (File.Exists(replaceFile))
{
if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg"));
else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg"));
}
if (File.Exists(replaceFile))
continue;
File.Move(sourceDirectoryFile, replaceFile);
}
}
return result;
}
private bool PossiblyCorrect(List<string> topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
bool result = false;
string corrected;
string correctedMoveTo;
string? correctedDirectory;
string filteredSourceDirectoryFile;
string[] filteredSourceDirectoryFiles;
(string Find, string Replace) findReplace;
IEnumerable<(string Find, string Replace)>? found;
foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection)
{
if (!topDirectories.Any())
continue;
filteredSourceDirectoryFiles = (from l in sourceDirectoryFiles where !_Configuration.PropertyConfiguration.IgnoreExtensions.Contains(Path.GetExtension(l)) select l).ToArray();
if (!filteredSourceDirectoryFiles.Any())
continue;
for (int i = 0; i < filteredSourceDirectoryFiles.Length; i++)
{
found = null;
for (int z = 0; z < int.MaxValue; z++)
{
filteredSourceDirectoryFile = filteredSourceDirectoryFiles[i];
found = from l in _SpellingFindReplace where filteredSourceDirectoryFile.Contains(l.Find) select l;
if (!found.Any())
break;
findReplace = found.First();
corrected = filteredSourceDirectoryFile.Replace(findReplace.Find, findReplace.Replace);
correctedDirectory = Path.GetDirectoryName(corrected);
if (string.IsNullOrEmpty(correctedDirectory))
break;
correctedMoveTo = Path.Combine(correctedDirectory, Path.GetFileName(corrected));
if (File.Exists(correctedMoveTo))
break;
if (!Directory.Exists(correctedDirectory))
_ = Directory.CreateDirectory(correctedDirectory);
if (!result)
result = true;
File.Move(filteredSourceDirectoryFile, correctedMoveTo);
filteredSourceDirectoryFiles[i] = corrected;
}
}
}
return result;
}
private List<string> GetMissingVerifyToSeasonCollection(List<string> _, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
List<string> results = new();
string check;
foreach ((int _, string sourceDirectory, string[] _) in groupCollection)
{
if (sourceDirectory == _Configuration.PropertyConfiguration.RootDirectory)
continue;
check = sourceDirectory[(_Configuration.PropertyConfiguration.RootDirectory.Length + 1)..];
if (check[0] is '=' || check.StartsWith("zzz ="))
{
if (!_Configuration.PropertyConfiguration.VerifyToSeason.Contains(check))
results.Add(check);
}
}
return results;
}
private void CreateWindowsShortcuts((long Ticks, string FilteredSourceDirectoryFile, string PropertyDirectory, int PropertyId)[] collection, bool keepAll)
{
int z = 0;
string fileName;
WindowsShortcut windowsShortcut;
foreach ((long ticks, string filteredSourceDirectoryFile, string propertyDirectory, int propertyId) in collection)
{
z += 1;
if (z % 1000 == 0)
_Log.Debug($"{z}) Loop {propertyDirectory}");
if (!keepAll)
{
fileName = Path.Combine(propertyDirectory, $"{propertyId}.lnk");
if (File.Exists(fileName))
continue;
}
else
{
fileName = string.Empty;
for (short c = 65; c < short.MaxValue; c++)
{
if (c > 95)
break;
fileName = Path.Combine(propertyDirectory, $"{(char)c}", $"{propertyId}.lnk");
if (File.Exists(fileName))
continue;
}
}
if (string.IsNullOrEmpty(fileName))
continue;
windowsShortcut = new() { Path = filteredSourceDirectoryFile };
windowsShortcut.Save(fileName);
windowsShortcut.Dispose();
}
}
private void ThirdPassToMove(Property.Models.Configuration configuration, Map.Models.MapLogic mapLogic, A_Property propertyLogic, Shared.Models.Container[] containers, string aPropertyContentCollectionDirectory) private void ThirdPassToMove(Property.Models.Configuration configuration, Map.Models.MapLogic mapLogic, A_Property propertyLogic, Shared.Models.Container[] containers, string aPropertyContentCollectionDirectory)
{ {
if (_Configuration?.PropertyConfiguration is null) if (_Configuration?.PropertyConfiguration is null)
@ -865,4 +570,299 @@ public class Compare
CreateWindowsShortcuts(collection, keepAll); CreateWindowsShortcuts(collection, keepAll);
} }
private void CreateWindowsShortcuts((long Ticks, string FilteredSourceDirectoryFile, string PropertyDirectory, int PropertyId)[] collection, bool keepAll)
{
int z = 0;
string fileName;
WindowsShortcut windowsShortcut;
foreach ((long ticks, string filteredSourceDirectoryFile, string propertyDirectory, int propertyId) in collection)
{
z += 1;
if (z % 1000 == 0)
_Log.Debug($"{z}) Loop {propertyDirectory}");
if (!keepAll)
{
fileName = Path.Combine(propertyDirectory, $"{propertyId}.lnk");
if (File.Exists(fileName))
continue;
}
else
{
fileName = string.Empty;
for (short c = 65; c < short.MaxValue; c++)
{
if (c > 95)
break;
fileName = Path.Combine(propertyDirectory, $"{(char)c}", $"{propertyId}.lnk");
if (File.Exists(fileName))
continue;
}
}
if (string.IsNullOrEmpty(fileName))
continue;
windowsShortcut = new() { Path = filteredSourceDirectoryFile };
windowsShortcut.Save(fileName);
windowsShortcut.Dispose();
}
}
private void SaveDiffFilesOrSaveLogAndMoveFiles(Property.Models.Configuration configuration)
{
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}");
_Logger?.LogInformation(aPropertySingletonDirectory);
_Logger?.LogInformation("to");
_Logger?.LogInformation(_Configuration.DiffPropertyDirectory);
for (int y = 0; y < int.MaxValue; y++)
{
_Logger?.LogInformation("Press \"Y\" key to continue or close console if compare not needed");
if (Console.ReadKey().Key == ConsoleKey.Y)
break;
}
_Logger?.LogInformation(". . .");
int loadLessThan = 7;
string diffRootDirectory;
ConsoleKey? consoleKey = null;
List<PropertyCompare.Models.PropertyCompare>? duplicates = null;
PropertyCompare.Models.PropertyCompare[] diffPropertyCompareCollection;
if (string.IsNullOrEmpty(_Configuration.DiffPropertyDirectory))
diffRootDirectory = string.Empty;
else
{
if (!_Configuration.DiffPropertyDirectory.EndsWith("{}"))
throw new Exception("Invalid directory should end with {}!");
diffRootDirectory = Shared.Models.Stateless.Methods.IProperty.GetDiffRootDirectory(_Configuration.DiffPropertyDirectory);
}
PropertyCompare.Models.PropertyCompareLogic propertyCompareLogic = new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _SpellingFindReplace, diffRootDirectory);
if (string.IsNullOrEmpty(_Configuration.DiffPropertyDirectory) || !Directory.Exists(_Configuration.DiffPropertyDirectory))
diffPropertyCompareCollection = Array.Empty<PropertyCompare.Models.PropertyCompare>();
else
{
diffPropertyCompareCollection = propertyCompareLogic.Get(_Configuration.DiffPropertyDirectory, loadLessThan, duplicates, deleteExtension: false);
if (!diffPropertyCompareCollection.Any())
throw new Exception("Invalid directory!");
}
string aPropertyCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "[{}]");
PropertyCompare.Models.PropertyCompare[] propertyCompareCollection = propertyCompareLogic.Get(aPropertySingletonDirectory, loadLessThan, duplicates, deleteExtension: false);
{
long ticks = DateTime.Now.Ticks;
string[] lines = (from l in propertyCompareCollection select l.GetSelect()).ToArray();
File.WriteAllLines(Path.Join(aPropertyCollectionDirectory, $". . . Ids - {ticks}.txt"), lines);
string json = JsonSerializer.Serialize(propertyCompareCollection, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(Path.Join(aPropertyCollectionDirectory, $". . . Ids - {ticks}.nosj"), json);
}
for (int x = 0; x < int.MaxValue; x++)
{
_Logger?.LogInformation($"Press \"D\" key to {nameof(PropertyCompare.Models.PropertyCompareLogic.SaveDiffFiles)}");
_Logger?.LogInformation($"Press \"M\" key to {nameof(PropertyCompare.Models.PropertyCompareLogic.SaveLogAndMoveFiles)}");
_Logger?.LogInformation("Press \"End\" key when ready to skip");
consoleKey = Console.ReadKey().Key;
if (consoleKey is ConsoleKey.D or ConsoleKey.M or ConsoleKey.End)
break;
}
_Logger?.LogInformation(". . .");
if (consoleKey.HasValue && consoleKey.Value == ConsoleKey.D)
propertyCompareLogic.SaveDiffFiles(aPropertyCollectionDirectory, loadLessThan, propertyCompareCollection, diffPropertyCompareCollection);
else if (consoleKey.HasValue && consoleKey.Value == ConsoleKey.M)
{
for (int x = 0; x < int.MaxValue; x++)
{
_Logger?.LogInformation($"Press \"0 - {loadLessThan}\" key when ready to continue");
_Logger?.LogInformation("Press \"End\" key when ready to skip");
consoleKey = Console.ReadKey().Key;
if (consoleKey.Value is ConsoleKey.D0 or ConsoleKey.D1 or ConsoleKey.D2 or ConsoleKey.D3 or ConsoleKey.D4 or ConsoleKey.D5 or ConsoleKey.D6 or ConsoleKey.End)
break;
}
_Logger?.LogInformation(". . .");
int i = int.Parse(consoleKey.Value.ToString()[1..]);
propertyCompareLogic.SaveLogAndMoveFiles(aPropertyCollectionDirectory, loadLessThan, propertyCompareCollection, diffPropertyCompareCollection, i);
}
}
private bool PossiblyRename(List<string> topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
bool result = false;
string replaceFile;
string replaceDirectory;
int remainingDirectories = 0;
IEnumerable<(string Find, string Replace)>? found;
foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection)
{
if (!topDirectories.Any())
continue;
found = from l in _RenameFindReplace where sourceDirectory == l.Find select l;
if (!found.Any())
continue;
if (!result)
result = true;
replaceDirectory = found.First().Replace;
if (!Directory.Exists(replaceDirectory))
Directory.Move(sourceDirectory, replaceDirectory);
else
{
if (Directory.EnumerateDirectories(sourceDirectory).Any())
remainingDirectories += 1;
else
{
foreach (string sourceDirectoryFile in sourceDirectoryFiles)
{
replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile));
if (File.Exists(replaceFile))
{
if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg"));
else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg"));
}
if (File.Exists(replaceFile))
continue;
File.Move(sourceDirectoryFile, replaceFile);
}
}
}
}
return result;
}
private bool PossiblyCorrect(List<string> topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
bool result = false;
string corrected;
string correctedMoveTo;
string? correctedDirectory;
string filteredSourceDirectoryFile;
string[] filteredSourceDirectoryFiles;
(string Find, string Replace) findReplace;
IEnumerable<(string Find, string Replace)>? found;
foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection)
{
if (!topDirectories.Any())
continue;
filteredSourceDirectoryFiles = (from l in sourceDirectoryFiles where !_Configuration.PropertyConfiguration.IgnoreExtensions.Contains(Path.GetExtension(l)) select l).ToArray();
if (!filteredSourceDirectoryFiles.Any())
continue;
for (int i = 0; i < filteredSourceDirectoryFiles.Length; i++)
{
found = null;
for (int z = 0; z < int.MaxValue; z++)
{
filteredSourceDirectoryFile = filteredSourceDirectoryFiles[i];
found = from l in _SpellingFindReplace where filteredSourceDirectoryFile.Contains(l.Find) select l;
if (!found.Any())
break;
findReplace = found.First();
corrected = filteredSourceDirectoryFile.Replace(findReplace.Find, findReplace.Replace);
correctedDirectory = Path.GetDirectoryName(corrected);
if (string.IsNullOrEmpty(correctedDirectory))
break;
correctedMoveTo = Path.Combine(correctedDirectory, Path.GetFileName(corrected));
if (File.Exists(correctedMoveTo))
break;
if (!Directory.Exists(correctedDirectory))
_ = Directory.CreateDirectory(correctedDirectory);
if (!result)
result = true;
File.Move(filteredSourceDirectoryFile, correctedMoveTo);
filteredSourceDirectoryFiles[i] = corrected;
}
}
}
return result;
}
private bool PossiblyRenameB(List<string> topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
bool result = false;
string replaceFile;
string replaceDirectory;
IEnumerable<(string Find, string Replace)>? found;
foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection)
{
if (!topDirectories.Any())
continue;
found = from l in _RenameBFindReplace where sourceDirectory == l.Find select l;
if (!found.Any())
continue;
if (!result)
result = true;
replaceDirectory = found.First().Replace;
if (!Directory.Exists(replaceDirectory))
_ = Directory.CreateDirectory(replaceDirectory);
foreach (string sourceDirectoryFile in sourceDirectoryFiles)
{
replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile));
if (File.Exists(replaceFile))
{
if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg"));
else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg"));
}
if (File.Exists(replaceFile))
continue;
File.Move(sourceDirectoryFile, replaceFile);
}
}
return result;
}
private bool PossiblyRenameC(List<string> topDirectories, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
bool result = false;
string replaceFile;
string replaceDirectory;
IEnumerable<(string Find, string Replace)>? found;
foreach ((int g, string sourceDirectory, string[] sourceDirectoryFiles) in groupCollection)
{
if (!topDirectories.Any())
continue;
found = from l in _RenameCFindReplace where sourceDirectory == l.Find select l;
if (!found.Any())
continue;
if (!result)
result = true;
replaceDirectory = found.First().Replace;
if (!Directory.Exists(replaceDirectory))
_ = Directory.CreateDirectory(replaceDirectory);
foreach (string sourceDirectoryFile in sourceDirectoryFiles)
{
replaceFile = Path.Combine(replaceDirectory, Path.GetFileName(sourceDirectoryFile));
if (File.Exists(replaceFile))
{
if (replaceFile.EndsWith(".jpg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpeg"));
else if (replaceFile.EndsWith(".jpeg", ignoreCase: true, CultureInfo.CurrentCulture))
replaceFile = Path.Combine(replaceDirectory, Path.ChangeExtension(sourceDirectoryFile, ".jpg"));
}
if (File.Exists(replaceFile))
continue;
File.Move(sourceDirectoryFile, replaceFile);
}
}
return result;
}
private List<string> GetMissingVerifyToSeasonCollection(List<string> _, List<(int g, string sourceDirectory, string[] sourceDirectoryFiles)> groupCollection)
{
if (_Configuration?.PropertyConfiguration is null)
throw new NullReferenceException(nameof(_Configuration.PropertyConfiguration));
List<string> results = new();
string check;
foreach ((int _, string sourceDirectory, string[] _) in groupCollection)
{
if (sourceDirectory == _Configuration.PropertyConfiguration.RootDirectory)
continue;
check = sourceDirectory[(_Configuration.PropertyConfiguration.RootDirectory.Length + 1)..];
if (check[0] is '=' || check.StartsWith("zzz ="))
{
if (!_Configuration.PropertyConfiguration.VerifyToSeason.Contains(check))
results.Add(check);
}
}
return results;
}
} }

View File

@ -33,14 +33,14 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Map\Map.csproj" /> <ProjectReference Include="..\Map\Map.csproj" />

1
Container/.vscode/format-report.json vendored Normal file
View File

@ -0,0 +1 @@
[]

8
Container/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"cSpell.words": [
"dlib",
"Exif",
"nosj",
"Serilog"
]
}

View File

@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Container</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
<PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">
true
</IsWindows>
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
true
</IsOSX>
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">
true
</IsLinux>
</PropertyGroup>
<PropertyGroup Condition="'$(IsWindows)'=='true'">
<DefineConstants>Windows</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsOSX)'=='true'">
<DefineConstants>OSX</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsLinux)'=='true'">
<DefineConstants>Linux</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" />
</ItemGroup>
</Project>

View File

@ -1,7 +1,8 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Text.Json; using System.Text.Json;
using View_by_Distance.Shared.Models;
namespace View_by_Distance.Shared.Models; namespace View_by_Distance.Container.Models;
public record Container(string SourceDirectory, ReadOnlyCollection<Item> Items) public record Container(string SourceDirectory, ReadOnlyCollection<Item> Items)
{ {

View File

@ -0,0 +1,487 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Container.Models.Stateless.Methods;
internal abstract class Container
{
private record FilePair(bool IsUnique,
List<FilePath> Collection,
FilePath FilePath,
Item Item);
internal static ReadOnlyCollection<Item> GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container)
{
List<Item> results = [];
foreach (Item item in container.Items)
{
if (!item.IsValidImageFormatExtension || propertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered))
continue;
results.Add(item);
}
return container.Items.Count == results.Count ? container.Items : results.AsReadOnly();
}
internal static DateTime[] GetContainerDateTimes(ReadOnlyCollection<Item> items)
{
DateTime[] results;
long containerMinimumTicks = (from l in items select l.FilePath.LastWriteTicks).Min();
long containerMaximumTicks = (from l in items select l.FilePath.LastWriteTicks).Max();
results = [new(containerMinimumTicks), new(containerMaximumTicks)];
return results;
}
internal static List<int> GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers)
{
List<int> results = [];
ReadOnlyCollection<Item> filteredItems;
foreach (Models.Container container in readOnlyContainers)
{
if (container.Items.Count == 0)
continue;
filteredItems = GetValidImageItems(propertyConfiguration, container);
if (filteredItems.Count == 0)
continue;
foreach (Item item in filteredItems)
{
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
continue;
if (results.Contains(item.ExifDirectory.FilePath.Id.Value))
continue;
results.Add(item.ExifDirectory.FilePath.Id.Value);
}
}
return results;
}
internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string _)
{
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, 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, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, string directorySearchFilter)
{
List<Models.Container> results = [];
string directory;
List<Item>? items;
Models.Container container;
List<string> directories = [];
Dictionary<string, List<Item>> directoryToItems = [];
foreach (ReadOnlyCollection<FilePath> filePaths in filePathsCollection)
{
if (filePaths.Count == 0)
continue;
directory = filePaths[0].DirectoryFullPath;
if (directory is null)
continue;
if (!directories.Contains(directory))
directories.Add(directory);
if (!directoryToItems.TryGetValue(directory, out items))
{
directoryToItems.Add(directory, []);
if (!directoryToItems.TryGetValue(directory, out items))
throw new Exception();
}
}
(string aResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories();
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, propertyConfiguration.ResultSingleton);
if (!Directory.Exists(aPropertySingletonDirectory))
_ = Directory.CreateDirectory(aPropertySingletonDirectory);
List<FilePair> filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter);
foreach (FilePair filePair in filePairs)
{
if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items))
{
directoryToItems.Add(filePair.FilePath.DirectoryFullPath, []);
if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items))
throw new Exception();
}
items.Add(filePair.Item);
}
foreach (KeyValuePair<string, List<Item>> keyValuePair in directoryToItems)
{
if (keyValuePair.Value.Count == 0)
continue;
container = new(keyValuePair.Key, new(keyValuePair.Value));
results.Add(container);
}
return (filePairs.Count, results.ToArray());
}
private static List<FilePair> GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, string directorySearchFilter)
{
List<FilePair> results = [];
const string extension = ".json";
ReadOnlyCollection<Shared.Models.FilePair> filePairs;
string jsonGroupDirectory = aPropertySingletonDirectory;
int maxDegreeOfParallelism = Environment.ProcessorCount;
int filesCollectionDirectoryLength = filesCollectionDirectory.Length;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection);
_ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) =>
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, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, Shared.Models.FilePair filePair, List<FilePair> results)
{
dlibDotNet?.Tick();
bool abandoned = false;
FileHolder sourceDirectoryFileHolder;
if (exifDirectoriesById is null || filePair.FilePath.Id is null || !exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out ExifDirectory? exifDirectory))
exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath);
ReadOnlyCollection<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;
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 (filePair.FilePath.FileNameFirstSegment[^1] == hasIgnoreKeyword)
change = null;
else
{
change = hasIgnoreKeyword;
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) && dateTime is null)
{
if (filePair.FilePath.FileNameFirstSegment[^1] == missingDateTimeOriginal)
change = null;
else
{
change = missingDateTimeOriginal;
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>");
}
}
else if (filePair.FilePath.FileNameFirstSegment[^1] != hasDateTimeOriginal)
{
change = hasDateTimeOriginal;
if (keyValuePairs is null || !keyValuePairs.TryGetValue(filePair.FilePath.Id.Value, out filePaths) || filePaths is null)
throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>");
}
else
change = null;
if (filePaths is not null && change is not null)
RenameFile(filePair, filePair.FilePath, change.Value, filePaths);
}
string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.FilePath.FullName, rootDirectoryLength, forceExtensionToLower: true);
bool? lastWriteTimeChanged = exifDirectory is not null ? propertyConfiguration.PropertiesChangedForProperty || exifDirectory.FilePath.LastWriteTicks != filePair.FilePath.LastWriteTicks : null;
if (filePair.Match is not null)
sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match);
else if (!filePair.IsUnique)
sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(jsonGroupDirectory, relativePath, extension)));
else
{
string fileName = Path.GetFileName(filePair.FilePath.FullName);
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePair.FilePath);
string directory = Path.Combine(jsonGroupDirectory, cei.Combined);
string jsonFileName = $"{fileName}{extension}";
string fullFileName = Path.Combine(directory, jsonFileName);
MoveIf(jsonFileName, cei, directory, fullFileName);
sourceDirectoryFileHolder = IFileHolder.Get(fullFileName);
}
if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePair.FilePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks)
{
File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePair.FilePath.LastWriteTicks));
File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value);
}
Item item = Item.Get(filePair.FilePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, exifDirectory, abandoned, fileSizeChanged, lastWriteTimeChanged);
lock (results)
results.Add(new(filePair.IsUnique, filePair.Collection, filePair.FilePath, item));
}
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 (filePair.Match is not null)
{
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.FullName, checkFile);
}
foreach (FilePath f in filePaths)
{
checkFile = Path.Combine(f.DirectoryFullPath, $"{f.NameWithoutExtension[..^1]}{change}{f.ExtensionLowered}");
if (File.Exists(checkFile))
continue;
File.Move(f.FullName, checkFile);
}
}
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, string fullFileName)
{
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 && Directory.Exists(checkDirectory))
{
string checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
File.Move(checkFile, fullFileName);
}
}
internal static List<string> GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers)
{
List<string> results = [];
ReadOnlyCollection<Item> filteredItems;
foreach (Models.Container container in readOnlyContainers)
{
if (container.Items.Count == 0)
continue;
filteredItems = GetValidImageItems(propertyConfiguration, container);
if (filteredItems.Count == 0)
continue;
foreach (Item item in filteredItems)
{
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
continue;
if (results.Contains(item.FilePath.FileNameFirstSegment))
continue;
results.Add(item.FilePath.FileNameFirstSegment);
}
}
return results;
}
internal static ReadOnlyCollection<Item> GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems)
{
List<Item> results = [];
List<int> distinct = [];
ReadOnlyCollection<Item> filteredItems;
foreach (Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
if (!filterItems)
filteredItems = container.Items;
else
{
filteredItems = GetValidImageItems(propertyConfiguration, container);
if (filteredItems.Count == 0)
continue;
}
foreach (Item item in filteredItems)
{
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null)
continue;
if (distinctItems)
{
if (distinct.Contains(item.ExifDirectory.FilePath.Id.Value))
continue;
distinct.Add(item.ExifDirectory.FilePath.Id.Value);
}
results.Add(item);
}
}
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

@ -0,0 +1,59 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Container.Models.Stateless.Methods;
public interface IContainer
{
public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string aPropertySingletonDirectory) =>
Container.GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory);
public static DateTime[] GetContainerDateTimes(ReadOnlyCollection<Item> items) =>
Container.GetContainerDateTimes(items);
public static ReadOnlyCollection<Item> GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) =>
Container.GetValidImageItems(propertyConfiguration, container);
public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) =>
GetContainers(propertyConfiguration, null, aPropertySingletonDirectory);
public static List<int> GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers);
public static List<string> GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
Container.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers);
public static ReadOnlyCollection<Item> GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems) =>
Container.GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems);
public static ReadOnlyCollection<Models.Container> GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById) =>
Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById);
internal DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection<Item> items) =>
GetContainerDateTimes(items);
internal ReadOnlyCollection<Item> TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) =>
GetValidImageItems(propertyConfiguration, container);
internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) =>
GetContainers(propertyConfiguration, aPropertySingletonDirectory);
internal List<int> TestStatic_GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers);
internal List<string> TestStatic_GetFilteredDistinctFileNameFirstSegments(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers);
internal ReadOnlyCollection<Item> TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems) =>
GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems);
internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string aPropertySingletonDirectory) =>
GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory);
internal 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

@ -35,9 +35,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />

View File

@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Phares.Shared; using Phares.Shared;
using ShellProgressBar; using ShellProgressBar;
@ -19,7 +19,7 @@ public class CopyDistinct
private readonly IsEnvironment _IsEnvironment; private readonly IsEnvironment _IsEnvironment;
private readonly IConfigurationRoot _ConfigurationRoot; private readonly IConfigurationRoot _ConfigurationRoot;
private readonly Property.Models.Configuration _PropertyConfiguration; private readonly Property.Models.Configuration _PropertyConfiguration;
private readonly ReadOnlyDictionary<string, 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) 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; _Configuration = configuration;
logger?.LogInformation(propertyConfiguration.RootDirectory); logger?.LogInformation(propertyConfiguration.RootDirectory);
(bool move, ReadOnlyCollection<string[]> filesCollection, bool anyLenFiles, bool moveBack) = Verify(); (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); List<string> lines = CopyDistinctFilesInDirectories(logger, move, filesCollection, anyLenFiles, moveBack);
if (lines.Count != 0) if (lines.Count != 0)
File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines);
@ -93,6 +95,76 @@ public class CopyDistinct
return (move, new(filesCollection), anyLenFiles, moveBack); 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) private static (string[], List<(FilePath, string)>) GetMoveBackToDoCollection(Property.Models.Configuration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection)
{ {
List<(FilePath, string)> results = []; List<(FilePath, string)> results = [];
@ -159,74 +231,4 @@ public class CopyDistinct
return (distinctDirectories.ToArray(), results); 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

@ -34,15 +34,15 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Phares.Shared; using Phares.Shared;
using System.Globalization; using System.Globalization;
@ -57,11 +57,11 @@ public class DateGroup
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}"); string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}");
if (!Directory.Exists(aPropertySingletonDirectory)) if (!Directory.Exists(aPropertySingletonDirectory))
_ = Directory.CreateDirectory(aPropertySingletonDirectory); _ = Directory.CreateDirectory(aPropertySingletonDirectory);
(int t, Container[] containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, aPropertySingletonDirectory); (int t, Container.Models.Container[] containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(propertyConfiguration, aPropertySingletonDirectory);
A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory, aResultsFullGroupDirectory); A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory, aResultsFullGroupDirectory);
if (propertyLogic.ExceptionsDirectories.Count != 0) if (propertyLogic.ExceptionsDirectories.Count != 0)
throw new Exception(); throw new Exception();
if (propertyConfiguration.PopulatePropertyId && (configuration.ByCreateDateShortcut || configuration.ByHash) && Shared.Models.Stateless.Methods.IProperty.Any(containers)) if (propertyConfiguration.PopulatePropertyId && (configuration.ByCreateDateShortcut || configuration.ByHash) && Property.Models.Stateless.IProperty.Any(containers))
{ {
propertyLogic.SavePropertyParallelWork(ticks, metadata, t, containers); propertyLogic.SavePropertyParallelWork(ticks, metadata, t, containers);
if (propertyLogic.ExceptionsDirectories.Count != 0) if (propertyLogic.ExceptionsDirectories.Count != 0)
@ -96,203 +96,6 @@ public class DateGroup
throw new Exception("Change configuration!"); 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(new string[] { $"{destinationDirectoryName} {year} {seasonName}" });
else if (_Configuration.ByDay)
directoryNames.AddRange(new string[] { $"{destinationDirectoryName} {year}", $"{weekOfYear}) {year}-{day}" });
else if (_Configuration.ByWeek)
directoryNames.AddRange(new string[] { $"{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) private A_Property GetPropertyLogic(bool reverse, string outputExtension, string aResultsFullGroupDirectory)
{ {
A_Property result; A_Property result;
@ -302,57 +105,67 @@ public class DateGroup
return result; return result;
} }
private static Item[] GetFilterItems(Container container) private static void CreateDateShortcut(Property.Models.Configuration configuration, Container.Models.Container[] containers)
{ {
List<Item> results = []; string path;
foreach (Item item in container.Items) string fileName;
{ string directory;
if (item.FilePath is not null) DateTime dateTime;
results.Add(item); int selectedTotal;
} const int minimum = 3;
return results.ToArray(); List<DateTime> dateTimes;
} List<Item> selectedItems;
const int maximumHours = 24;
private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container[] containers) string? relativePathDirectory;
{ WindowsShortcut windowsShortcut;
(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results; TimeSpan threeStandardDeviationHigh;
Item[] filteredItems; string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()");
string? topDirectory; foreach (Container.Models.Container container in containers)
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 container in containers)
{ {
if (container.Items.Count == 0) if (container.Items.Count == 0)
continue; continue;
if (!_Configuration.KeepFullPath) selectedTotal = 0;
destinationDirectory = destinationRoot; threeStandardDeviationHigh = Property.Models.Stateless.IProperty.GetThreeStandardDeviationHigh(minimum, container);
else if (threeStandardDeviationHigh.TotalHours > maximumHours)
destinationDirectory = string.Concat(destinationRoot, container.SourceDirectory[configuration.RootDirectory.Length..]); threeStandardDeviationHigh = new(maximumHours, 0, 0);
checkDirectory = Path.GetFullPath(container.SourceDirectory); for (int i = 0; i < container.Items.Count; i++)
for (int z = 0; z < int.MaxValue; z++)
{ {
if (checkDirectory == configuration.RootDirectory) (i, dateTimes, selectedItems) = Property.Models.Stateless.IProperty.Get(container, threeStandardDeviationHigh, i);
break; selectedTotal += selectedItems.Count;
checkDirectory = Path.GetDirectoryName(checkDirectory); foreach (Item item in selectedItems)
if (string.IsNullOrEmpty(checkDirectory)) {
break; if (item.ExifDirectory is null)
continue;
relativePathDirectory = Path.GetDirectoryName(item.RelativePath);
if (string.IsNullOrEmpty(relativePathDirectory))
continue;
if (item.ExifDirectory is null)
dateTime = new(item.FilePath.LastWriteTicks);
else if (item.ExifDirectory is not null && item.ExifDirectory.DateTimeOriginal is not null)
dateTime = item.ExifDirectory.DateTimeOriginal.Value;
else
dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.ExifDirectory);
path = Path.GetFullPath($"{configuration.RootDirectory}{item.RelativePath[..^5]}");
directory = Path.Combine($"{aPropertyContentDirectory}{relativePathDirectory}", $"{dateTimes.Min():yyyy-MM-dd_HH-mm-ss}---{dateTimes.Max():yyyy-MM-dd_HH-mm-ss}");
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
fileName = Path.Combine(directory, $"{Path.GetFileName(item.RelativePath[..^5])}.lnk");
if (File.Exists(fileName))
continue;
windowsShortcut = new() { Path = path };
windowsShortcut.Save(fileName);
windowsShortcut.Dispose();
if (!File.Exists(fileName))
continue;
File.SetLastWriteTime(fileName, dateTime);
}
} }
if (string.IsNullOrEmpty(checkDirectory)) if (selectedTotal < container.Items.Count && selectedTotal < (from l in container.Items where l.Property is not null select true).Count())
continue; continue;
topDirectory = checkDirectory;
filteredItems = GetFilterItems(container);
if (filteredItems.Length == 0)
continue;
fileMoveCollectionDirectory = GetMoveFileCollection(destinationDirectory, topDirectory, filteredItems);
fileMoveCollection.AddRange(fileMoveCollectionDirectory);
} }
results = (from l in fileMoveCollection orderby l.MinimumDateTimeTicks descending, l.LastWriteTimeTicks descending select l).ToArray();
return results;
} }
private void MoveFiles(Property.Models.Configuration configuration, string destinationRoot, Container[] containers) private void MoveFiles(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers)
{ {
string checkDirectory; string checkDirectory;
bool hasDuplicate; bool hasDuplicate;
@ -453,64 +266,251 @@ public class DateGroup
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(configuration.RootDirectory); _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(configuration.RootDirectory);
} }
private static void CreateDateShortcut(Property.Models.Configuration configuration, Container[] containers) private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers)
{ {
string path; (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results;
string fileName; Item[] filteredItems;
string directory; string? topDirectory;
DateTime dateTime; string? checkDirectory;
int selectedTotal; string destinationDirectory;
const int minimum = 3; List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollection = [];
List<DateTime> dateTimes; List<(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)> fileMoveCollectionDirectory;
List<Item> selectedItems; foreach (Container.Models.Container container in containers)
const int maximumHours = 24;
string? relativePathDirectory;
WindowsShortcut windowsShortcut;
TimeSpan threeStandardDeviationHigh;
string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()");
foreach (Container container in containers)
{ {
if (container.Items.Count == 0) if (container.Items.Count == 0)
continue; continue;
selectedTotal = 0; if (!_Configuration.KeepFullPath)
threeStandardDeviationHigh = Shared.Models.Stateless.Methods.IProperty.GetThreeStandardDeviationHigh(minimum, container); destinationDirectory = destinationRoot;
if (threeStandardDeviationHigh.TotalHours > maximumHours) else
threeStandardDeviationHigh = new(maximumHours, 0, 0); destinationDirectory = string.Concat(destinationRoot, container.SourceDirectory[configuration.RootDirectory.Length..]);
for (int i = 0; i < container.Items.Count; i++) checkDirectory = Path.GetFullPath(container.SourceDirectory);
for (int z = 0; z < int.MaxValue; z++)
{ {
(i, dateTimes, selectedItems) = Shared.Models.Stateless.Methods.IProperty.Get(container, threeStandardDeviationHigh, i); if (checkDirectory == configuration.RootDirectory)
selectedTotal += selectedItems.Count; break;
foreach (Item item in selectedItems) checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory))
break;
}
if (string.IsNullOrEmpty(checkDirectory))
continue;
topDirectory = checkDirectory;
filteredItems = GetFilterItems(container);
if (filteredItems.Length == 0)
continue;
fileMoveCollectionDirectory = GetMoveFileCollection(destinationDirectory, topDirectory, filteredItems);
fileMoveCollection.AddRange(fileMoveCollectionDirectory);
}
results = (from l in fileMoveCollection orderby l.MinimumDateTimeTicks descending, l.LastWriteTimeTicks descending select l).ToArray();
return results;
}
private static Item[] GetFilterItems(Container.Models.Container container)
{
List<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.Property is null) 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)
continue; flag = '^';
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;
else else
dateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); flag = '=';
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()) (season, seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
continue; 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

@ -34,14 +34,14 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />

View File

@ -34,14 +34,14 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />

View File

@ -36,7 +36,7 @@
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" /> <ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />

View File

@ -358,7 +358,7 @@ public partial class E_Distance : IDistance
} }
results.Add(keyValuePair.Key, new(keyValuePairs)); results.Add(keyValuePair.Key, new(keyValuePairs));
} }
return new(results); return results.AsReadOnly();
} }
public static List<LocationContainer> GetPreFilterLocationContainer(int maxDegreeOfParallelism, Configuration configuration, string focusDirectory, string focusModel, int? skipPersonWithMoreThen, long ticks, MapLogic mapLogic, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mapped, List<LocationContainer> available) public static List<LocationContainer> GetPreFilterLocationContainer(int maxDegreeOfParallelism, Configuration configuration, string focusDirectory, string focusModel, int? skipPersonWithMoreThen, long ticks, MapLogic mapLogic, long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mapped, List<LocationContainer> available)
@ -509,7 +509,7 @@ public partial class E_Distance : IDistance
faceDistanceContainer = new(face, faceDistance); faceDistanceContainer = new(face, faceDistance);
collection.Add(faceDistanceContainer); collection.Add(faceDistanceContainer);
} }
results = new(collection.ToArray()); results = collection.AsReadOnly();
return results; return results;
} }
@ -568,7 +568,7 @@ public partial class E_Distance : IDistance
foreach (KeyValuePair<int, LocationContainer> keyValue in keyValuePair.Value) foreach (KeyValuePair<int, LocationContainer> keyValue in keyValuePair.Value)
results.Add(keyValue.Value); results.Add(keyValue.Value);
} }
return new(results); return results.AsReadOnly();
} }
public static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(IDlibDotNet dlibDotNet, Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding, List<LocationContainer> preFiltered, DistanceLimits distanceLimits, List<LocationContainer> postFiltered) public static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(IDlibDotNet dlibDotNet, Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding, List<LocationContainer> preFiltered, DistanceLimits distanceLimits, List<LocationContainer> postFiltered)
@ -624,7 +624,7 @@ public partial class E_Distance : IDistance
results = ISortingContainer.Sort(results); results = ISortingContainer.Sort(results);
else else
results = ISortingContainer.SortUsingDaysDelta(results); results = ISortingContainer.SortUsingDaysDelta(results);
return new(results); return results.AsReadOnly();
} }
private static ReadOnlyCollection<RelationContainer> GetRelationCollections(IDistanceLimits distanceLimits, int faceDistancePermyriad, int locationContainerDistanceTake, float distanceTolerance, List<Record> records) private static ReadOnlyCollection<RelationContainer> GetRelationCollections(IDistanceLimits distanceLimits, int faceDistancePermyriad, int locationContainerDistanceTake, float distanceTolerance, List<Record> records)
@ -672,7 +672,7 @@ public partial class E_Distance : IDistance
mappedRelations = (from l in mappedRelations orderby l.DistancePermyriad select l).Take(locationContainerDistanceTake).ToList(); mappedRelations = (from l in mappedRelations orderby l.DistancePermyriad select l).Take(locationContainerDistanceTake).ToList();
results.Add(new(fileHolder, new(mappedRelations))); results.Add(new(fileHolder, new(mappedRelations)));
} }
return new(results); return results.AsReadOnly();
} }
ReadOnlyCollection<RelationContainer> IDistance.GetRelationContainers(IDistanceLimits distanceLimits, int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection<LocationContainer> locationContainers) ReadOnlyCollection<RelationContainer> IDistance.GetRelationContainers(IDistanceLimits distanceLimits, int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection<LocationContainer> locationContainers)

View File

@ -26,9 +26,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -6,10 +6,8 @@ public class Program
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
private static void Main() private static void Main() =>
{
// ApplicationConfiguration.Initialize(); // ApplicationConfiguration.Initialize();
Application.Run(new DragDropExplorer()); Application.Run(new DragDropExplorer());
}
} }

View File

@ -26,9 +26,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -6,10 +6,8 @@ public class Program
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
private static void Main() private static void Main() =>
{
// ApplicationConfiguration.Initialize(); // ApplicationConfiguration.Initialize();
Application.Run(new DragDropMove()); Application.Run(new DragDropMove());
}
} }

View File

@ -27,13 +27,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" /> <ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" /> <ProjectReference Include="..\Metadata\Metadata.csproj" />
<ProjectReference Include="..\Container\Container.csproj" />
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Resize\Resize.csproj" /> <ProjectReference Include="..\Resize\Resize.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -64,63 +64,6 @@ public partial class DragDropSearch : Form
Controls.Add(_TextBox); 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[] containers;
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}");
(_, containers) = 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) public static string? GetFaceEncoding(string file)
{ {
string? result; string? result;
@ -147,6 +90,37 @@ public partial class DragDropSearch : Form
return result; 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) private void GetDirectoriesOrDoDragDrop(string[] paths)
{ {
string name; 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) private void Form1_DragDrop(object? sender, DragEventArgs e)
{ {
try 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

@ -8,13 +8,11 @@ public class Program
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
private static void Main() private static void Main() =>
{
// ApplicationConfiguration.Initialize(); // ApplicationConfiguration.Initialize();
Application.Run(new DragDropSearch()); Application.Run(new DragDropSearch());
}
private static Item[] GetFilterItems(Models.Configuration configuration, Container container) private static Item[] GetFilterItems(Models.Configuration configuration, Container.Models.Container container)
{ {
List<Item> results = []; List<Item> results = [];
foreach (Item item in container.Items) foreach (Item item in container.Items)
@ -26,11 +24,11 @@ public class Program
return results.ToArray(); return results.ToArray();
} }
public static List<Item> GetItemCollection(Models.Configuration configuration, Container[] containers) public static List<Item> GetItemCollection(Models.Configuration configuration, Container.Models.Container[] containers)
{ {
List<Item> results = []; List<Item> results = [];
Item[] filteredItems; Item[] filteredItems;
foreach (Container container in containers) foreach (Container.Models.Container container in containers)
{ {
if (container.Items.Count == 0) if (container.Items.Count == 0)
continue; continue;

View File

@ -27,9 +27,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Metadata\Metadata.csproj" /> <ProjectReference Include="..\Metadata\Metadata.csproj" />

View File

@ -6,10 +6,8 @@ public class Program
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
private static void Main() private static void Main() =>
{
// ApplicationConfiguration.Initialize(); // ApplicationConfiguration.Initialize();
Application.Run(new DragDropSetPropertyItem()); Application.Run(new DragDropSetPropertyItem());
}
} }

View File

@ -33,14 +33,14 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />

View File

@ -1,4 +1,4 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Phares.Shared; using Phares.Shared;
using ShellProgressBar; using ShellProgressBar;
@ -30,7 +30,7 @@ public class DuplicateSearch
else else
{ {
Configuration.Verify(configuration, requireExist: false); Configuration.Verify(configuration, requireExist: false);
Container[] containers = GetContainers(ticks, configuration); Container.Models.Container[] containers = GetContainers(ticks, configuration);
string argZero = args.Count > 0 ? Path.GetFullPath(args[0]) : Path.GetFullPath(configuration.RootDirectory); string argZero = args.Count > 0 ? Path.GetFullPath(args[0]) : Path.GetFullPath(configuration.RootDirectory);
bool argZeroIsConfigurationRootDirectory = configuration.RootDirectory == argZero; bool argZeroIsConfigurationRootDirectory = configuration.RootDirectory == argZero;
string destinationRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(configuration, "Z) Moved"); string destinationRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(configuration, "Z) Moved");
@ -61,6 +61,144 @@ public class DuplicateSearch
File.WriteAllText(Path.Combine(alongSideDirectory, $"{directoryName}-{ticks}.json"), json); 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) private static void Move(ILogger<Program>? logger, long ticks, string destinationRoot, List<(FilePath FilePath, string Destination)> collection)
{ {
StringBuilder stringBuilder = new(); StringBuilder stringBuilder = new();
@ -107,142 +245,4 @@ public class DuplicateSearch
logger?.LogInformation(". . ."); 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[] GetContainers(long ticks, Configuration configuration)
{
int f;
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) = Shared.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory);
}
return containers;
}
private static Dictionary<int, List<MappingFromItem?>> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, 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 container in containers)
{
if (container.Items.Count == 0)
continue;
if (!argZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero))
continue;
validImageItems = Shared.Models.Stateless.Methods.IContainer.GetValidImageItems(configuration, container);
if (validImageItems.Count == 0)
continue;
containerDateTimes = Shared.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

@ -33,7 +33,7 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" /> <ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />

View File

@ -46,7 +46,8 @@ public class D_Face : IFaceD
private readonly EncoderParameters _HiddenEncoderParameters; private readonly EncoderParameters _HiddenEncoderParameters;
private readonly IPropertyConfiguration _PropertyConfiguration; private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly JsonSerializerOptions _WriteIndentedAndWhenWritingNull; private readonly JsonSerializerOptions _WriteIndentedAndWhenWritingNull;
private readonly Dictionary<string, ReadOnlyCollection<string>> _FileGroups; private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultContentFileGroups;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultCollectionFileGroups;
public D_Face( public D_Face(
string argZero, string argZero,
@ -69,7 +70,6 @@ public class D_Face : IFaceD
float[] rectangleIntersectMinimums) float[] rectangleIntersectMinimums)
{ {
_ArgZero = argZero; _ArgZero = argZero;
_FileGroups = [];
_ImageCodecInfo = imageCodecInfo; _ImageCodecInfo = imageCodecInfo;
_EncoderParameters = encoderParameters; _EncoderParameters = encoderParameters;
_FileNameExtension = filenameExtension; _FileNameExtension = filenameExtension;
@ -84,6 +84,8 @@ public class D_Face : IFaceD
_RectangleIntersectMinimum = rectangleIntersectMinimums.Min(); _RectangleIntersectMinimum = rectangleIntersectMinimums.Min();
_FaceDistanceHiddenImageFactor = faceDistanceHiddenImageFactor; _FaceDistanceHiddenImageFactor = faceDistanceHiddenImageFactor;
_ForceFaceLastWriteTimeToCreationTime = forceFaceLastWriteTimeToCreationTime; _ForceFaceLastWriteTimeToCreationTime = forceFaceLastWriteTimeToCreationTime;
_ResultContentFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
_ResultCollectionFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
(Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(modelDirectory, modelName, predictorModelName); (Model model, PredictorModel predictorModel, ModelParameter modelParameter) = GetModel(modelDirectory, modelName, predictorModelName);
_Model = model; _Model = model;
_PredictorModel = predictorModel; _PredictorModel = predictorModel;
@ -93,6 +95,73 @@ public class D_Face : IFaceD
_WriteIndentedAndWhenWritingNull = new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; _WriteIndentedAndWhenWritingNull = new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull };
} }
void IFaceD.ReSaveFace(ExifDirectory exifDirectory, FilePath filePath, Shared.Models.Face face, bool mappedFile)
{
FileInfo fileInfo = new(filePath.FullName);
if (fileInfo.Exists)
{
string? json;
short type = 2;
string? model;
string? maker;
string checkFile = $"{filePath.FullName}.exif";
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist;
// const int author = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinAuthor; // 40093
if (mappedFile)
{
json = IMetadata.GetOutputResolution(exifDirectory);
if (json is not null && json.Contains(nameof(DateTime)))
return;
(maker, model) = Get(json);
}
else
{
maker = IMetadata.GetMaker(exifDirectory);
model = IMetadata.GetModel(exifDirectory);
ExifDirectory? faceExifDirectory = IMetadata.GetExifDirectory(filePath);
json = IMetadata.GetOutputResolution(faceExifDirectory);
if (json is not null && json.Contains(nameof(DateTime)))
return;
}
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
FaceFile faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
geoLocation?.ToDmsString(),
face.DateTime,
null,
face.FaceParts,
face.Location,
maker,
null,
model,
face.OutputResolution);
string faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
PropertyItem? propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(constructorInfo, artist, type, faceFileJson);
#pragma warning disable CA1416
Bitmap bitmap = new(fileInfo.FullName);
bitmap.SetPropertyItem(propertyItem);
bitmap.Save(checkFile);
bitmap.Dispose();
#pragma warning restore CA1416
File.SetLastWriteTime(checkFile, fileInfo.LastWriteTime);
File.Delete(fileInfo.FullName);
File.Move(checkFile, fileInfo.FullName);
}
}
private static (string?, string?) Get(string? json)
{
string? model;
string? maker;
FaceFile? faceFile = json is null ? null : JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
if (faceFile is null || faceFile.Location is null)
(maker, model) = (null, null);
else
(maker, model) = (faceFile.Maker, faceFile.Model);
return (maker, model);
}
public override string ToString() public override string ToString()
{ {
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
@ -101,10 +170,234 @@ public class D_Face : IFaceD
public void Update(string dResultsFullGroupDirectory) public void Update(string dResultsFullGroupDirectory)
{ {
_FileGroups.Clear(); ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, dResultsFullGroupDirectory, [_PropertyConfiguration.ResultCollection, _PropertyConfiguration.ResultContent]);
ReadOnlyDictionary<string, ReadOnlyCollection<string>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, dResultsFullGroupDirectory, [_PropertyConfiguration.ResultCollection, _PropertyConfiguration.ResultContent]); foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
foreach (KeyValuePair<string, ReadOnlyCollection<string>> keyValuePair in keyValuePairs) {
_FileGroups.Add(keyValuePair.Key, keyValuePair.Value); if (keyValuePair.Key == _PropertyConfiguration.ResultContent)
_ResultContentFileGroups[0] = keyValuePair.Value;
else if (keyValuePair.Key == _PropertyConfiguration.ResultCollection)
_ResultCollectionFileGroups[0] = keyValuePair.Value;
else
throw new Exception();
}
}
public List<(Shared.Models.Face, FileHolder?, string, bool)> SaveFaces(FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
{
List<(Shared.Models.Face, FileHolder?, string, bool Save)> results = [];
bool save;
FileInfo fileInfo;
FileHolder fileHolder;
string deterministicHashCodeKey;
string fileName = mappingFromItem.FilePath.NameWithoutExtension;
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
string directory = _ResultContentFileGroups[0][cei.Enum][cei.Index];
DirectoryInfo directoryInfo = new(Path.Combine(directory, fileName));
MoveIf(fileName, cei, directory, directoryInfo);
foreach (Shared.Models.Face face in faces)
{
save = false;
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
{
results.Add(new(face, null, string.Empty, save));
continue;
}
deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
fileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}"));
fileHolder = FileHolder.Get(fileInfo);
if (!directoryInfo.Exists)
save = true;
else if (_OverrideForFaceImages)
save = true;
else if (!fileHolder.Exists)
save = true;
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime)
save = true;
results.Add(new(face, fileHolder, Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_HiddenFileNameExtension}"), save));
}
if (results.Any(l => l.Save))
{
if (!directoryInfo.Exists)
_ = Directory.CreateDirectory(directoryInfo.FullName);
SaveFaces(mappingFromItem.ResizedFileHolder, exifDirectory, results);
}
return results;
}
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 && Directory.Exists(checkDirectory))
{
string checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
{
File.Move(checkFile, fileInfo.FullName);
fileInfo.Refresh();
}
}
}
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, DirectoryInfo directoryInfo)
{
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 && Directory.Exists(checkDirectory))
{
string checkFile = Path.Combine(checkDirectory, fileName);
if (Directory.Exists(checkFile))
{
Directory.Move(checkFile, directoryInfo.FullName);
directoryInfo.Refresh();
}
}
}
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;
List<Location> locations;
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
string fileName = $"{mappingFromItem.FilePath.NameWithoutExtension}{mappingFromItem.FilePath.ExtensionLowered}.json";
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
string directory = _ResultCollectionFileGroups[0][cei.Enum][cei.Index];
FileInfo fileInfo = new(Path.Combine(directory, fileName));
MoveIf(fileName, cei, directory, fileInfo);
if (_ForceFaceLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
fileInfo.Refresh();
}
if (_ForceFaceLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
}
if (_PropertiesChangedForFaces)
results = null;
else if (!fileInfo.Exists)
results = null;
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime)
results = null;
else
{
json = Shared.Models.Stateless.Methods.IFace.GetJson(fileInfo.FullName);
try
{
results = JsonSerializer.Deserialize<List<Shared.Models.Face>>(json);
if (results is null)
throw new NullReferenceException(nameof(results));
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.LastWriteTime));
}
catch (Exception)
{
results = null;
parseExceptions.Add(nameof(D_Face));
}
}
if (!_LoadPhotoPrismLocations || mappingFromPhotoPrismCollection is null || results is null)
locations = [];
else
locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum);
if (results is null || locations.Count > 0)
{
results = GetFaces(outputResolution, cResultsFullGroupDirectory, exifDirectory, mappingFromItem, outputResolutionToResize, locations);
if (results.Count == 0)
File.Move(mappingFromItem.ResizedFileHolder.FullName, $"{mappingFromItem.ResizedFileHolder.FullName}.err");
else
{
bool updateDateWhenMatches = dateTimes.Count > 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
json = JsonSerializer.Serialize(results, _WriteIndentedAndWhenWritingNull);
if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime))
{
if (!_ForceFaceLastWriteTimeToCreationTime)
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), DateTime.Now));
else
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.CreationTime));
}
}
}
}
return results;
}
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));
if (_PropertyConfiguration.NumberOfTimesToUpsample is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample));
List<Shared.Models.Face> results = [];
FaceRecognitionDotNet.Image? unknownImage;
try
{
if (mappingFromItem.ResizedFileHolder.ExtensionLowered != ".tif")
unknownImage = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName);
else
{
int outputQuality = 100;
string extension = ".png";
string file = Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{extension}");
(ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(extension, outputQuality);
#pragma warning disable CA1416
System.Drawing.Image image = System.Drawing.Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
image.Save(Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{filenameExtension}"), imageCodecInfo, encoderParameters);
image.Dispose();
#pragma warning restore CA1416
unknownImage = FaceRecognition.LoadImageFile(file);
File.Delete(file);
}
}
catch (Exception)
{ unknownImage = null; }
if (unknownImage is not null)
{
(int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize);
List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary<FacePart, FacePoint[]>? FaceParts)> collection;
FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, _Model, _ModelParameter, _PredictorModel);
collection = faceRecognition.GetCollection(unknownImage, locations, includeFaceEncoding: true, includeFaceParts: true);
if (collection.Count == 0)
results.Add(new(exifDirectory, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null));
else
{
double[] rawEncoding;
Shared.Models.Face face;
Shared.Models.FaceEncoding convertedFaceEncoding;
foreach ((Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary<FacePart, FacePoint[]>? faceParts) in collection)
{
face = new(exifDirectory, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location);
if (faceEncoding is not null)
{
rawEncoding = faceEncoding.GetRawEncoding();
convertedFaceEncoding = new(rawEncoding, faceEncoding.Size);
face.SetFaceEncoding(convertedFaceEncoding);
}
if (faceParts is not null)
face.SetFaceParts(faceParts);
results.Add(face);
}
}
unknownImage.Dispose();
faceRecognition.Dispose();
}
return results;
} }
private static (Model model, PredictorModel predictorModel, ModelParameter modelParameter) GetModel(string modelDirectory, string modelName, string predictorModelName) private static (Model model, PredictorModel predictorModel, ModelParameter modelParameter) GetModel(string modelDirectory, string modelName, string predictorModelName)
@ -148,8 +441,6 @@ public class D_Face : IFaceD
return result; return result;
} }
#pragma warning disable CA1416
private void SaveFaces(FileHolder resizedFileHolder, ExifDirectory exifDirectory, List<(Shared.Models.Face, FileHolder?, string, bool)> collection) private void SaveFaces(FileHolder resizedFileHolder, ExifDirectory exifDirectory, List<(Shared.Models.Face, FileHolder?, string, bool)> collection)
{ {
int width; int width;
@ -165,6 +456,7 @@ public class D_Face : IFaceD
PropertyItem? propertyItem; PropertyItem? propertyItem;
string? maker = IMetadata.GetMaker(exifDirectory); string? maker = IMetadata.GetMaker(exifDirectory);
string? model = IMetadata.GetModel(exifDirectory); string? model = IMetadata.GetModel(exifDirectory);
#pragma warning disable CA1416
using Bitmap source = new(resizedFileHolder.FullName); using Bitmap source = new(resizedFileHolder.FullName);
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory); MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315 const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
@ -227,254 +519,9 @@ public class D_Face : IFaceD
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel); graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
bitmap.Save(fileName, _HiddenImageCodecInfo, _HiddenEncoderParameters); bitmap.Save(fileName, _HiddenImageCodecInfo, _HiddenEncoderParameters);
} }
#pragma warning restore CA1416
File.SetAttributes(fileName, FileAttributes.Hidden); File.SetAttributes(fileName, FileAttributes.Hidden);
} }
} }
private List<Shared.Models.Face> GetFaces(string outputResolution, string cResultsFullGroupDirectory, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<Location> locations)
{
if (_PropertyConfiguration.NumberOfJitters is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfJitters));
if (_PropertyConfiguration.NumberOfTimesToUpsample is null)
throw new NullReferenceException(nameof(_PropertyConfiguration.NumberOfTimesToUpsample));
List<Shared.Models.Face> results = [];
FaceRecognitionDotNet.Image? unknownImage;
try
{
if (mappingFromItem.ResizedFileHolder.ExtensionLowered != ".tif")
unknownImage = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName);
else
{
int outputQuality = 100;
string extension = ".png";
string file = Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{extension}");
(ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) = C_Resize.GetTuple(extension, outputQuality);
#pragma warning disable CA1416
System.Drawing.Image image = System.Drawing.Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
image.Save(Path.Combine(cResultsFullGroupDirectory, $"{mappingFromItem.ResizedFileHolder.Name}{filenameExtension}"), imageCodecInfo, encoderParameters);
image.Dispose();
#pragma warning restore CA1416
unknownImage = FaceRecognition.LoadImageFile(file);
File.Delete(file);
}
}
catch (Exception)
{ unknownImage = null; }
if (unknownImage is not null)
{
(int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize);
List<(Location Location, FaceRecognitionDotNet.FaceEncoding? FaceEncoding, Dictionary<FacePart, FacePoint[]>? FaceParts)> collection;
FaceRecognition faceRecognition = new(_PropertyConfiguration.NumberOfJitters.Value, _PropertyConfiguration.NumberOfTimesToUpsample.Value, _Model, _ModelParameter, _PredictorModel);
collection = faceRecognition.GetCollection(unknownImage, locations, includeFaceEncoding: true, includeFaceParts: true);
if (collection.Count == 0)
results.Add(new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location: null));
else
{
double[] rawEncoding;
Shared.Models.Face face;
Shared.Models.FaceEncoding convertedFaceEncoding;
foreach ((Location location, FaceRecognitionDotNet.FaceEncoding? faceEncoding, Dictionary<FacePart, FacePoint[]>? faceParts) in collection)
{
face = new(property, outputResolutionWidth, outputResolutionHeight, outputResolutionOrientation, location);
if (faceEncoding is not null)
{
rawEncoding = faceEncoding.GetRawEncoding();
convertedFaceEncoding = new(rawEncoding, faceEncoding.Size);
face.SetFaceEncoding(convertedFaceEncoding);
}
if (faceParts is not null)
face.SetFaceParts(faceParts);
results.Add(face);
}
}
unknownImage.Dispose();
faceRecognition.Dispose();
}
return results;
}
#pragma warning restore CA1416
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)
{
List<Shared.Models.Face>? results;
string? json;
List<Location> locations;
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath);
FileInfo fileInfo = new(Path.Combine(_FileGroups[_PropertyConfiguration.ResultCollection][directoryIndex], $"{mappingFromItem.FilePath.NameWithoutExtension}{mappingFromItem.FilePath.ExtensionLowered}.json"));
if (_ForceFaceLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
fileInfo.Refresh();
}
if (_ForceFaceLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
}
if (_PropertiesChangedForFaces)
results = null;
else if (!fileInfo.Exists)
results = null;
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime)
results = null;
else
{
json = Shared.Models.Stateless.Methods.IFace.GetJson(fileInfo.FullName);
try
{
results = JsonSerializer.Deserialize<List<Shared.Models.Face>>(json);
if (results is null)
throw new NullReferenceException(nameof(results));
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.LastWriteTime));
}
catch (Exception)
{
results = null;
parseExceptions.Add(nameof(D_Face));
}
}
if (!_LoadPhotoPrismLocations || mappingFromPhotoPrismCollection is null || results is null)
locations = [];
else
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);
if (results.Count == 0)
File.Move(mappingFromItem.ResizedFileHolder.FullName, $"{mappingFromItem.ResizedFileHolder.FullName}.err");
else
{
bool updateDateWhenMatches = dateTimes.Count > 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
json = JsonSerializer.Serialize(results, _WriteIndentedAndWhenWritingNull);
if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime))
{
if (!_ForceFaceLastWriteTimeToCreationTime)
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), DateTime.Now));
else
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
subFileTuples.Add(new Tuple<string, DateTime>(nameof(D_Face), fileInfo.CreationTime));
}
}
}
}
return results;
}
public List<(Shared.Models.Face, FileHolder?, string, bool)> SaveFaces(FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
{
List<(Shared.Models.Face, FileHolder?, string, bool Save)> results = [];
bool save;
FileInfo fileInfo;
FileHolder fileHolder;
string deterministicHashCodeKey;
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath);
string directory = Path.Combine(_FileGroups[_PropertyConfiguration.ResultContent][directoryIndex], mappingFromItem.FilePath.NameWithoutExtension);
bool directoryExists = Directory.Exists(directory);
foreach (Shared.Models.Face face in faces)
{
save = false;
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
{
results.Add(new(face, null, string.Empty, save));
continue;
}
deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
fileInfo = new FileInfo(Path.Combine(directory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}"));
fileHolder = FileHolder.Get(fileInfo);
if (!directoryExists)
save = true;
else if (_OverrideForFaceImages)
save = true;
else if (!fileHolder.Exists)
save = true;
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime)
save = true;
results.Add(new(face, fileHolder, Path.Combine(directory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_HiddenFileNameExtension}"), save));
}
if (results.Any(l => l.Save))
{
if (!directoryExists)
_ = Directory.CreateDirectory(directory);
SaveFaces(mappingFromItem.ResizedFileHolder, exifDirectory, results);
}
return results;
}
#pragma warning disable CA1416
private static (string?, string?) Get(string? json)
{
string? model;
string? maker;
FaceFile? faceFile = json is null ? null : JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
if (faceFile is null || faceFile.Location is null)
(maker, model) = (null, null);
else
(maker, model) = (faceFile.Maker, faceFile.Model);
return (maker, model);
}
void IFaceD.ReSaveFace(ExifDirectory exifDirectory, FilePath filePath, Shared.Models.Face face, bool mappedFile)
{
FileInfo fileInfo = new(filePath.FullName);
if (fileInfo.Exists)
{
string? json;
short type = 2;
string? model;
string? maker;
string checkFile = $"{filePath.FullName}.exif";
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist;
// const int author = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinAuthor; // 40093
if (mappedFile)
{
json = IMetadata.GetOutputResolution(exifDirectory);
if (json is not null && json.Contains(nameof(DateTime)))
return;
(maker, model) = Get(json);
}
else
{
maker = IMetadata.GetMaker(exifDirectory);
model = IMetadata.GetModel(exifDirectory);
ExifDirectory? faceExifDirectory = IMetadata.GetExifDirectory(filePath);
json = IMetadata.GetOutputResolution(faceExifDirectory);
if (json is not null && json.Contains(nameof(DateTime)))
return;
}
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
FaceFile faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
geoLocation?.ToDmsString(),
face.DateTime,
null,
face.FaceParts,
face.Location,
maker,
null,
model,
face.OutputResolution);
string faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
PropertyItem? propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(constructorInfo, artist, type, faceFileJson);
Bitmap bitmap = new(fileInfo.FullName);
bitmap.SetPropertyItem(propertyItem);
bitmap.Save(checkFile);
bitmap.Dispose();
File.SetLastWriteTime(checkFile, fileInfo.LastWriteTime);
File.Delete(fileInfo.FullName);
File.Move(checkFile, fileInfo.FullName);
}
}
#pragma warning restore CA1416
} }

View File

@ -33,7 +33,7 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" /> <ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />

View File

@ -32,17 +32,17 @@ public class D2_FaceParts
private readonly bool _OverrideForFaceLandmarkImages; private readonly bool _OverrideForFaceLandmarkImages;
private readonly EncoderParameters _EncoderParameters; private readonly EncoderParameters _EncoderParameters;
private readonly IPropertyConfiguration _PropertyConfiguration; private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly Dictionary<string, ReadOnlyCollection<string>> _FileGroups; private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultContentFileGroups;
public D2_FaceParts(IPropertyConfiguration propertyConfiguration, ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension, bool checkDFaceAndUpWriteDates, bool overrideForFaceLandmarkImages) public D2_FaceParts(IPropertyConfiguration propertyConfiguration, ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension, bool checkDFaceAndUpWriteDates, bool overrideForFaceLandmarkImages)
{ {
_FileGroups = [];
_ImageCodecInfo = imageCodecInfo; _ImageCodecInfo = imageCodecInfo;
_EncoderParameters = encoderParameters; _EncoderParameters = encoderParameters;
_FileNameExtension = filenameExtension; _FileNameExtension = filenameExtension;
_PropertyConfiguration = propertyConfiguration; _PropertyConfiguration = propertyConfiguration;
_CheckDFaceAndUpWriteDates = checkDFaceAndUpWriteDates; _CheckDFaceAndUpWriteDates = checkDFaceAndUpWriteDates;
_OverrideForFaceLandmarkImages = overrideForFaceLandmarkImages; _OverrideForFaceLandmarkImages = overrideForFaceLandmarkImages;
_ResultContentFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception(); ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
_ConstructorInfo = constructorInfo; _ConstructorInfo = constructorInfo;
} }
@ -55,32 +55,241 @@ public class D2_FaceParts
public void Update(string dResultsFullGroupDirectory) public void Update(string dResultsFullGroupDirectory)
{ {
_FileGroups.Clear(); ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, dResultsFullGroupDirectory, [_PropertyConfiguration.ResultContent]);
ReadOnlyDictionary<string, ReadOnlyCollection<string>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, dResultsFullGroupDirectory, [_PropertyConfiguration.ResultContent]); foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
foreach (KeyValuePair<string, ReadOnlyCollection<string>> keyValuePair in keyValuePairs)
_FileGroups.Add(keyValuePair.Key, keyValuePair.Value);
}
private static void GetPointBounds(PointF[] points, out float xMinimum, out float xMaximum, out float yMinimum, out float yMaximum)
{
xMinimum = points[0].X;
xMaximum = xMinimum;
yMinimum = points[0].Y;
yMaximum = yMinimum;
foreach (PointF point in points)
{ {
if (xMinimum > point.X) if (keyValuePair.Key == _PropertyConfiguration.ResultContent)
xMinimum = point.X; _ResultContentFileGroups[0] = keyValuePair.Value;
if (xMaximum < point.X) else
xMaximum = point.X; throw new Exception();
if (yMinimum > point.Y)
yMinimum = point.Y;
if (yMaximum < point.Y)
yMaximum = point.Y;
} }
} }
public void SaveFaceLandmarkImages(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
{
bool any = false;
foreach (Shared.Models.Face face in faces)
{
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0)
continue;
if (!any)
any = true;
}
if (any)
SaveAllFaceParts(d2ResultsFullGroupDirectory, mappingFromItem, exifDirectory, faces);
}
private void SaveAllFaceParts(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
{
int x;
int y;
Brush brush;
int pointSize;
bool any = false;
FaceFile faceFile;
bool? isDefaultName;
List<long> personKeys = [];
List<FaceFile> faceFiles = [];
StringBuilder stringBuilder = new();
MappingFromPerson? mappingFromPerson;
string? maker = IMetadata.GetMaker(exifDirectory);
string? model = IMetadata.GetModel(exifDirectory);
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
#pragma warning disable CA1416 #pragma warning disable CA1416
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
using Graphics graphics = Graphics.FromImage(image);
foreach (Shared.Models.Face face in faces)
{
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0)
continue;
if (!any && face.Mapping?.MappingFromPerson is null)
any = true;
mappingFromPerson = face.Mapping?.MappingFromPerson;
brush = mappingFromPerson is null ? Brushes.Red : Brushes.GreenYellow;
isDefaultName = mappingFromPerson is null ? null : Shared.Models.Stateless.Methods.IPerson.IsDefaultName(mappingFromPerson);
if (mappingFromPerson is not null && isDefaultName is not null && !isDefaultName.Value)
personKeys.Add(mappingFromPerson.PersonKey);
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
geoLocation?.ToDmsString(),
face.DateTime,
face.FaceEncoding,
face.FaceParts,
face.Location,
maker,
mappingFromPerson,
model,
face.OutputResolution);
faceFiles.Add(faceFile);
pointSize = GetPointSize(face.FaceParts, defaultPointSize: 2);
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
{
foreach (FacePoint facePoint in facePoints)
graphics.FillEllipse(brush, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2);
if (facePart == FacePart.Chin)
continue;
if (facePoints.Length < 3)
continue;
x = (int)(from l in facePoints select l.X).Average();
y = (int)(from l in facePoints select l.Y).Average();
graphics.FillEllipse(Brushes.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2);
}
}
_ = graphics.Save();
#pragma warning restore CA1416
string directory = GetSeasonDirectory(d2ResultsFullGroupDirectory, mappingFromItem, any);
SaveImage(mappingFromItem, directory, image, faceFiles);
}
private int GetPointSize(Dictionary<FacePart, FacePoint[]> faceParts, int defaultPointSize)
{
int result;
FacePoint[]? facePoints;
if (faceParts.TryGetValue(FacePart.LeftEye, out facePoints))
result = (int)Math.Ceiling((facePoints.Max(l => l.X) - facePoints.Min(l => l.X)) * .05);
else
{
if (faceParts.TryGetValue(FacePart.RightEye, out facePoints))
result = (int)Math.Ceiling((facePoints.Max(l => l.X) - facePoints.Min(l => l.X)) * .05);
else
result = defaultPointSize;
}
return result;
}
private string GetSeasonDirectory(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, bool any)
{
string result;
string minimumDateYear = mappingFromItem.MinimumDateTime.ToString("yyyy");
DateTime dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value;
(int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
string year = mappingFromItem.DateTimeOriginal is null ? $"{minimumDateYear[1..]}{minimumDateYear[0]}" : mappingFromItem.DateTimeOriginal.Value.ToString("yyyy");
string directory = Path.Combine(d2ResultsFullGroupDirectory, $"[{_PropertyConfiguration.ResultContent}]", $"{year}.{season} {seasonName}");
result = any ? Path.Combine(directory, "---") : Path.Combine(directory, "Complete");
if (!Directory.Exists(result))
_ = Directory.CreateDirectory(result);
return result;
}
private void SaveImage(MappingFromItem mappingFromItem, string directory, Image image, List<FaceFile> faceFiles)
{
short type = 2;
string faceFileJson;
PropertyItem? propertyItem;
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
string fileName = Path.Combine(directory, $"{mappingFromItem.FilePath.Name}{_FileNameExtension}");
try
{
#pragma warning disable CA1416
foreach (int propertyId in image.PropertyIdList)
{
if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation)
continue;
image.RemovePropertyItem(propertyId);
}
faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray);
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson);
image.SetPropertyItem(propertyItem);
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
#pragma warning restore CA1416
}
catch (Exception ex)
{
if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName))
File.Delete(fileName);
faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray);
if (!string.IsNullOrEmpty(faceFileJson))
File.WriteAllText($"{fileName}.json", faceFileJson);
}
}
public void SaveFaceLandmarkImages(Configuration configuration, string d2ResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces, bool saveRotated)
{
FileInfo fileInfo;
bool check = false;
FileInfo rotatedFileInfo;
DateTime? dateTime = null;
long ticks = DateTime.Now.Ticks;
string deterministicHashCodeKey;
bool updateDateWhenMatches = false;
List<(Shared.Models.Face, string, string)> collection = [];
string fileName = mappingFromItem.FilePath.NameWithoutExtension;
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
string directory = _ResultContentFileGroups[0][cei.Enum][cei.Index];
DirectoryInfo directoryInfo = new(Path.Combine(directory, mappingFromItem.FilePath.NameWithoutExtension));
MoveIf(fileName, cei, directory, directoryInfo);
foreach (Shared.Models.Face face in faces)
{
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
{
collection.Add(new(face, string.Empty, string.Empty));
continue;
}
deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
fileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}"));
if (string.IsNullOrEmpty(fileInfo.DirectoryName))
continue;
rotatedFileInfo = new FileInfo(Path.Combine(fileInfo.DirectoryName, $"{deterministicHashCodeKey} - R{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}"));
collection.Add(new(face, fileInfo.FullName, rotatedFileInfo.FullName));
if (check)
continue;
else if (!directoryInfo.Exists)
check = true;
else if (_OverrideForFaceLandmarkImages)
check = true;
else if (!fileInfo.Exists)
check = true;
else if (saveRotated && !rotatedFileInfo.Exists)
check = true;
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime)
check = true;
if (check && !updateDateWhenMatches)
{
updateDateWhenMatches = dateTimes.Count != 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
}
}
if (check)
{
if (!directoryInfo.Exists)
_ = Directory.CreateDirectory(directoryInfo.FullName);
SaveFaceParts(mappingFromItem, exifDirectory, collection);
if (saveRotated)
SaveRotated(mappingFromItem, collection);
}
}
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, DirectoryInfo directoryInfo)
{
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 && Directory.Exists(checkDirectory))
{
string checkFile = Path.Combine(checkDirectory, fileName);
if (Directory.Exists(checkFile))
{
Directory.Move(checkFile, directoryInfo.FullName);
directoryInfo.Refresh();
}
}
}
private static Bitmap RotateBitmap(Image image, float angle)
{
Bitmap result;
#pragma warning disable CA1416
Bitmap bitmap = new(image);
result = RotateBitmap(bitmap, angle);
bitmap?.Dispose();
#pragma warning restore CA1416
return result;
}
private static Bitmap RotateBitmap(Bitmap bitmap, float angle) private static Bitmap RotateBitmap(Bitmap bitmap, float angle)
{ {
@ -92,6 +301,7 @@ public class D2_FaceParts
#elif Windows #elif Windows
// Make save Matrix to represent rotation // Make save Matrix to represent rotation
// by this angle. // by this angle.
#pragma warning disable CA1416
Matrix rotate_at_origin = new(); Matrix rotate_at_origin = new();
rotate_at_origin.Rotate(angle); rotate_at_origin.Rotate(angle);
@ -138,301 +348,28 @@ public class D2_FaceParts
int y = (hgt - bitmap.Height) / 2; int y = (hgt - bitmap.Height) / 2;
gr.DrawImage(bitmap, x, y); gr.DrawImage(bitmap, x, y);
} }
#pragma warning restore CA1416
#endif #endif
// Return the result bitmap. // Return the result bitmap.
return result; return result;
} }
private static Bitmap RotateBitmap(Image image, float angle) private static void GetPointBounds(PointF[] points, out float xMinimum, out float xMaximum, out float yMinimum, out float yMaximum)
{ {
Bitmap result; xMinimum = points[0].X;
Bitmap bitmap = new(image); xMaximum = xMinimum;
result = RotateBitmap(bitmap, angle); yMinimum = points[0].Y;
bitmap?.Dispose(); yMaximum = yMinimum;
return result; foreach (PointF point in points)
}
private void SaveRotated(MappingFromItem mappingFromItem, List<(Shared.Models.Face, string, string)> collection)
{
double? α;
Bitmap rotated;
foreach ((Shared.Models.Face face, string _, string rotatedFileName) in collection)
{ {
if (face.FaceParts is null) if (xMinimum > point.X)
continue; xMinimum = point.X;
(_, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts); if (xMaximum < point.X)
if (α is null) xMaximum = point.X;
continue; if (yMinimum > point.Y)
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName); yMinimum = point.Y;
rotated = RotateBitmap(image, (float)α.Value); if (yMaximum < point.Y)
if (rotated is not null) yMaximum = point.Y;
{
rotated.Save(rotatedFileName, _ImageCodecInfo, _EncoderParameters);
rotated.Dispose();
}
}
}
private string GetSeasonDirectory(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, bool any)
{
string result;
string minimumDateYear = mappingFromItem.MinimumDateTime.ToString("yyyy");
DateTime dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value;
(int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
string year = mappingFromItem.DateTimeOriginal is null ? $"{minimumDateYear[1..]}{minimumDateYear[0]}" : mappingFromItem.DateTimeOriginal.Value.ToString("yyyy");
string directory = Path.Combine(d2ResultsFullGroupDirectory, $"[{_PropertyConfiguration.ResultContent}]", $"{year}.{season} {seasonName}");
result = any ? Path.Combine(directory, "---") : Path.Combine(directory, "Complete");
if (!Directory.Exists(result))
_ = Directory.CreateDirectory(result);
return result;
}
private void SaveImage(MappingFromItem mappingFromItem, string directory, Image image, List<FaceFile> faceFiles)
{
short type = 2;
string faceFileJson;
PropertyItem? propertyItem;
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
string fileName = Path.Combine(directory, $"{mappingFromItem.FilePath.Name}{_FileNameExtension}");
try
{
foreach (int propertyId in image.PropertyIdList)
{
if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation)
continue;
image.RemovePropertyItem(propertyId);
}
faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray);
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson);
image.SetPropertyItem(propertyItem);
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
}
catch (Exception ex)
{
if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName))
File.Delete(fileName);
faceFileJson = JsonSerializer.Serialize(faceFiles.ToArray(), FaceFileCollectionGenerationContext.Default.FaceFileArray);
if (!string.IsNullOrEmpty(faceFileJson))
File.WriteAllText($"{fileName}.json", faceFileJson);
}
}
private void SaveAllFaceParts(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces)
{
int x;
int y;
Brush brush;
int pointSize;
bool any = false;
FaceFile faceFile;
bool? isDefaultName;
List<long> personKeys = [];
List<FaceFile> faceFiles = [];
StringBuilder stringBuilder = new();
MappingFromPerson? mappingFromPerson;
string? maker = IMetadata.GetMaker(exifDirectory);
string? model = IMetadata.GetModel(exifDirectory);
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
using Graphics graphics = Graphics.FromImage(image);
foreach (Shared.Models.Face face in faces)
{
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0)
continue;
if (!any && face.Mapping?.MappingFromPerson is null)
any = true;
mappingFromPerson = face.Mapping?.MappingFromPerson;
brush = mappingFromPerson is null ? Brushes.Red : Brushes.GreenYellow;
isDefaultName = mappingFromPerson is null ? null : Shared.Models.Stateless.Methods.IPerson.IsDefaultName(mappingFromPerson);
if (mappingFromPerson is not null && isDefaultName is not null && !isDefaultName.Value)
personKeys.Add(mappingFromPerson.PersonKey);
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
geoLocation?.ToDmsString(),
face.DateTime,
face.FaceEncoding,
face.FaceParts,
face.Location,
maker,
mappingFromPerson,
model,
face.OutputResolution);
faceFiles.Add(faceFile);
pointSize = GetPointSize(face.FaceParts, defaultPointSize: 2);
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
{
foreach (FacePoint facePoint in facePoints)
graphics.FillEllipse(brush, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2);
if (facePart == FacePart.Chin)
continue;
if (facePoints.Length < 3)
continue;
x = (int)(from l in facePoints select l.X).Average();
y = (int)(from l in facePoints select l.Y).Average();
graphics.FillEllipse(Brushes.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2);
}
}
_ = graphics.Save();
string directory = GetSeasonDirectory(d2ResultsFullGroupDirectory, mappingFromItem, any);
SaveImage(mappingFromItem, directory, image, faceFiles);
}
private void SaveImage(string fileName, Image image, FaceFile faceFile)
{
short type = 2;
string faceFileJson;
PropertyItem? propertyItem;
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
try
{
foreach (int propertyId in image.PropertyIdList)
{
if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation)
continue;
image.RemovePropertyItem(propertyId);
}
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson);
image.SetPropertyItem(propertyItem);
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
}
catch (Exception ex)
{
if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName))
File.Delete(fileName);
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
if (!string.IsNullOrEmpty(faceFileJson))
File.WriteAllText($"{fileName}.json", faceFileJson);
}
}
private void SaveFaceParts(MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<(Shared.Models.Face, string, string)> collection)
{
int x;
int y;
Brush brush;
int pointSize;
FaceFile faceFile;
MappingFromPerson? mappingFromPerson;
string? maker = IMetadata.GetMaker(exifDirectory);
string? model = IMetadata.GetModel(exifDirectory);
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
foreach ((Shared.Models.Face face, string fileName, string _) in collection)
{
try
{
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0)
continue;
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
mappingFromPerson = face.Mapping?.MappingFromPerson;
brush = mappingFromPerson is null ? Brushes.Red : Brushes.GreenYellow;
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
geoLocation?.ToDmsString(),
face.DateTime,
face.FaceEncoding,
face.FaceParts,
face.Location,
maker,
mappingFromPerson,
model,
face.OutputResolution);
using Graphics graphics = Graphics.FromImage(image);
pointSize = GetPointSize(face.FaceParts, defaultPointSize: 2);
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
{
foreach (FacePoint facePoint in facePoints)
graphics.FillEllipse(brush, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2);
if (facePart == FacePart.Chin)
continue;
if (facePoints.Length < 3)
continue;
x = (int)(from l in facePoints select l.X).Average();
y = (int)(from l in facePoints select l.Y).Average();
graphics.FillEllipse(Brushes.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2);
}
_ = graphics.Save();
SaveImage(fileName, image, faceFile);
}
catch (Exception)
{
if (File.Exists(fileName))
File.Delete(fileName);
}
}
}
private int GetPointSize(Dictionary<FacePart, FacePoint[]> faceParts, int defaultPointSize)
{
int result;
FacePoint[]? facePoints;
if (faceParts.TryGetValue(FacePart.LeftEye, out facePoints))
result = (int)Math.Ceiling((facePoints.Max(l => l.X) - facePoints.Min(l => l.X)) * .05);
else
{
if (faceParts.TryGetValue(FacePart.RightEye, out facePoints))
result = (int)Math.Ceiling((facePoints.Max(l => l.X) - facePoints.Min(l => l.X)) * .05);
else
result = defaultPointSize;
}
return result;
}
#pragma warning restore CA1416
public void SaveFaceLandmarkImages(Configuration configuration, string d2ResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces, bool saveRotated)
{
FileInfo fileInfo;
bool check = false;
FileInfo rotatedFileInfo;
DateTime? dateTime = null;
long ticks = DateTime.Now.Ticks;
string deterministicHashCodeKey;
bool updateDateWhenMatches = false;
List<(Shared.Models.Face, string, string)> collection = [];
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize), nameof(D_Face)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath);
string directory = Path.Combine(_FileGroups[_PropertyConfiguration.ResultContent][directoryIndex], mappingFromItem.FilePath.NameWithoutExtension);
bool directoryExists = Directory.Exists(directory);
foreach (Shared.Models.Face face in faces)
{
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
{
collection.Add(new(face, string.Empty, string.Empty));
continue;
}
deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
fileInfo = new FileInfo(Path.Combine(directory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}"));
if (string.IsNullOrEmpty(fileInfo.DirectoryName))
continue;
rotatedFileInfo = new FileInfo(Path.Combine(fileInfo.DirectoryName, $"{deterministicHashCodeKey} - R{mappingFromItem.FilePath.ExtensionLowered}{_FileNameExtension}"));
collection.Add(new(face, fileInfo.FullName, rotatedFileInfo.FullName));
if (check)
continue;
else if (!directoryExists)
check = true;
else if (_OverrideForFaceLandmarkImages)
check = true;
else if (!fileInfo.Exists)
check = true;
else if (saveRotated && !rotatedFileInfo.Exists)
check = true;
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime)
check = true;
if (check && !updateDateWhenMatches)
{
updateDateWhenMatches = dateTimes.Count != 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
}
}
if (check)
{
if (!directoryExists)
_ = Directory.CreateDirectory(directory);
SaveFaceParts(mappingFromItem, exifDirectory, collection);
if (saveRotated)
SaveRotated(mappingFromItem, collection);
} }
} }
@ -469,18 +406,116 @@ public class D2_FaceParts
return results; return results;
} }
public void SaveFaceLandmarkImages(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Shared.Models.Face> faces) private void SaveImage(string fileName, Image image, FaceFile faceFile)
{ {
bool any = false; short type = 2;
foreach (Shared.Models.Face face in faces) string faceFileJson;
PropertyItem? propertyItem;
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
try
{ {
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0) #pragma warning disable CA1416
continue; foreach (int propertyId in image.PropertyIdList)
if (!any) {
any = true; if (propertyId == MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation)
continue;
image.RemovePropertyItem(propertyId);
}
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson);
image.SetPropertyItem(propertyItem);
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
#pragma warning restore CA1416
}
catch (Exception ex)
{
if (ex is not null && !string.IsNullOrEmpty(fileName) && File.Exists(fileName))
File.Delete(fileName);
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
if (!string.IsNullOrEmpty(faceFileJson))
File.WriteAllText($"{fileName}.json", faceFileJson);
}
}
private void SaveRotated(MappingFromItem mappingFromItem, List<(Shared.Models.Face, string, string)> collection)
{
double? α;
Bitmap rotated;
foreach ((Shared.Models.Face face, string _, string rotatedFileName) in collection)
{
#pragma warning disable CA1416
if (face.FaceParts is null)
continue;
(_, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts);
if (α is null)
continue;
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
rotated = RotateBitmap(image, (float)α.Value);
if (rotated is not null)
{
rotated.Save(rotatedFileName, _ImageCodecInfo, _EncoderParameters);
rotated.Dispose();
}
#pragma warning restore CA1416
}
}
private void SaveFaceParts(MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<(Shared.Models.Face, string, string)> collection)
{
int x;
int y;
Brush brush;
int pointSize;
FaceFile faceFile;
MappingFromPerson? mappingFromPerson;
string? maker = IMetadata.GetMaker(exifDirectory);
string? model = IMetadata.GetModel(exifDirectory);
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
foreach ((Shared.Models.Face face, string fileName, string _) in collection)
{
try
{
#pragma warning disable CA1416
if (face.Location is null || face.FaceEncoding is null || face.FaceParts is null || face.FaceParts.Count == 0)
continue;
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
mappingFromPerson = face.Mapping?.MappingFromPerson;
brush = mappingFromPerson is null ? Brushes.Red : Brushes.GreenYellow;
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
geoLocation?.ToDmsString(),
face.DateTime,
face.FaceEncoding,
face.FaceParts,
face.Location,
maker,
mappingFromPerson,
model,
face.OutputResolution);
using Graphics graphics = Graphics.FromImage(image);
pointSize = GetPointSize(face.FaceParts, defaultPointSize: 2);
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
{
foreach (FacePoint facePoint in facePoints)
graphics.FillEllipse(brush, facePoint.X - pointSize, facePoint.Y - pointSize, pointSize * 2, pointSize * 2);
if (facePart == FacePart.Chin)
continue;
if (facePoints.Length < 3)
continue;
x = (int)(from l in facePoints select l.X).Average();
y = (int)(from l in facePoints select l.Y).Average();
graphics.FillEllipse(Brushes.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2);
}
_ = graphics.Save();
SaveImage(fileName, image, faceFile);
#pragma warning restore CA1416
}
catch (Exception)
{
if (File.Exists(fileName))
File.Delete(fileName);
}
} }
if (any)
SaveAllFaceParts(d2ResultsFullGroupDirectory, mappingFromItem, exifDirectory, faces);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -34,12 +34,12 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
@ -51,10 +51,10 @@
<ProjectReference Include="..\FaceParts\FaceParts.csproj" /> <ProjectReference Include="..\FaceParts\FaceParts.csproj" />
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" /> <ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />
<ProjectReference Include="..\Map\Map.csproj" /> <ProjectReference Include="..\Map\Map.csproj" />
<ProjectReference Include="..\Container\Container.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" /> <ProjectReference Include="..\Metadata\Metadata.csproj" />
<ProjectReference Include="..\PhotoPrism\PhotoPrism.csproj" /> <ProjectReference Include="..\PhotoPrism\PhotoPrism.csproj" />
<ProjectReference Include="..\Property-Compare\Property-Compare.csproj" /> <ProjectReference Include="..\Property-Compare\Property-Compare.csproj" />
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Resize\Resize.csproj" /> <ProjectReference Include="..\Resize\Resize.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -54,7 +54,7 @@ public class Place
results.Add(Get(place)); results.Add(Get(place));
} }
} }
return new(results); return results.AsReadOnly();
} }
} }

View File

@ -63,7 +63,7 @@ internal class F_Random
relativePaths.Add(immichAsset.Path.Split(immichOwnerId)[1]); relativePaths.Add(immichAsset.Path.Split(immichOwnerId)[1]);
} }
} }
return new(results); return results.AsReadOnly();
} }
private static Dictionary<string, ImmichAsset> GetImmichAssets(string immichAssetsFile) private static Dictionary<string, ImmichAsset> GetImmichAssets(string immichAssetsFile)

View File

@ -37,10 +37,12 @@
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
<ProjectReference Include="..\Container\Container.csproj" />
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" /> <ProjectReference Include="..\Metadata\Metadata.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

File diff suppressed because it is too large Load Diff

View File

@ -22,62 +22,190 @@ internal abstract class DistanceLogic
internal record TicksDirectory(DateTime AlternateDirectoryDateTime, internal record TicksDirectory(DateTime AlternateDirectoryDateTime,
string Directory, string Directory,
DateTime DirectoryDateTime, DateTime DirectoryDateTime,
string DirectoryName,
bool? IsLocationContainerDebugDirectory, bool? IsLocationContainerDebugDirectory,
float? TotalDays); float? TotalDays);
private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames) internal static List<Record> DeleteEmptyDirectoriesAndGetCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, string eDistanceContentDirectory, ReadOnlyDictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted, ReadOnlyCollection<string> personKeyFormattedCollection)
{
string checkFile;
string actionDirectoryName = Path.GetFileName(actionDirectory);
string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
foreach (string file in files)
{
if (facesFileNames.Contains(file))
{
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
continue;
}
File.Delete(file);
}
}
private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
{ {
List<Record> results = [];
bool check;
string message;
string[] files; string[] files;
string checkFile; bool? isDefault;
int? linksCount;
int totalSeconds;
DateTime dateTime;
TimeSpan timeSpan;
int directoryNumber;
List<Record> records;
string? checkDirectory; string? checkDirectory;
string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); ProgressBar progressBar;
foreach (string directory in directories) string[] yearDirectories;
string personKeyFormatted;
List<string> distinct = [];
string? personFirstInitial;
bool isReservedDirectoryName;
string[] personNameDirectories;
string? newestPersonKeyFormatted;
string? personDisplayDirectoryName;
string[] personNameLinkDirectories;
string? personFirstInitialDirectory;
List<TicksDirectory> ticksDirectories;
string[] personKeyFormattedDirectories;
string manualCopyHumanized = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy).Humanize(LetterCasing.Title);
string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
for (int i = 1; i < 6; i++)
{ {
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory)); check = false;
if (!Directory.Exists(checkDirectory)) results.Clear();
Directory.Move(directory, checkDirectory); distinct.Clear();
else directoryNumber = 0;
ticksDirectories = UpdateDateVerifyAndGetTicksDirectories(configuration, eDistanceContentDirectory);
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{i}) {ticksDirectories.Count:000} compile from and clean ticks Director(ies) - B - {totalSeconds} total second(s)";
progressBar = new(ticksDirectories.Count, message, options);
foreach (TicksDirectory ticksDirectory in ticksDirectories)
{ {
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories); progressBar.Tick();
foreach (string file in files) personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly);
foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories)
{ {
if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted)) personKeyFormatted = Path.GetFileName(personKeyFormattedDirectory);
continue; isReservedDirectoryName = personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Sorting)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Mapping)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.ManualCopy));
checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted); if (!isReservedDirectoryName && personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Individually)))
checkDirectory = Path.GetDirectoryName(checkFile); {
if (checkDirectory is null) Individually(configuration, ticksDirectory, personKeyFormattedDirectory);
continue; throw new Exception($"B) Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!");
if (File.Exists(checkFile)) }
continue; _ = personKeyFormattedToNewestPersonKeyFormatted.TryGetValue(personKeyFormatted, out newestPersonKeyFormatted);
if (!Directory.Exists(checkDirectory)) if (personKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
_ = Directory.CreateDirectory(checkDirectory); {
File.Move(file, checkFile); timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks);
if (timeSpan.TotalDays > 6)
throw new Exception($"{configuration.MappingDefaultName} <{ticksDirectory.DirectoryDateTime}> are only allowed within x days!");
}
yearDirectories = Directory.GetDirectories(personKeyFormattedDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string yearDirectory in yearDirectories)
{
if (check && !Directory.Exists(yearDirectory))
continue;
if (ticksDirectory.IsLocationContainerDebugDirectory is null || !ticksDirectory.IsLocationContainerDebugDirectory.Value)
linksCount = null;
else
linksCount = GetLinksCount(yearDirectory);
personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
if (personNameDirectories.Length > 1)
throw new NotSupportedException("Try deleting *.lnk files!");
foreach (string personNameDirectory in personNameDirectories)
{
directoryNumber++;
personDisplayDirectoryName = Path.GetFileName(personNameDirectory);
isDefault = IPerson.IsDefaultName(personDisplayDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]);
if (isDefault.Value && personDisplayDirectoryName.Length == 1)
{
if (personKeyFormatted.Length != configuration.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, configuration.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
continue;
checkDirectory = Path.Combine(yearDirectory, $"X+{dateTime.Ticks}");
if (Directory.Exists(checkDirectory))
{
Directory.Delete(yearDirectory, recursive: true);
continue;
}
Directory.Move(personNameDirectory, checkDirectory);
if (!check)
check = true;
continue;
}
if (isDefault.Value && (ticksDirectory.DirectoryDateTime.Hour != 0 || ticksDirectory.DirectoryDateTime.Minute != 0 || ticksDirectory.DirectoryDateTime.Second != 0))
{
checkDirectory = Path.GetDirectoryName(ticksDirectory.Directory);
if (checkDirectory is null)
continue;
checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString());
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
checkDirectory = Path.Combine(checkDirectory, personKeyFormatted);
if (!Directory.Exists(checkDirectory))
{
Directory.Move(personKeyFormattedDirectory, checkDirectory);
if (!check)
check = true;
break;
}
}
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly);
if (isReservedDirectoryName && files.Length > 0)
throw new Exception($"Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!");
if (personKeyFormatted == manualCopyHumanized && files.Length > 0)
throw new Exception($"Move personKey directories up one from {manualCopyHumanized} and delete {manualCopyHumanized} directory!");
if (personKeyFormatted == forceSingleImageHumanized && files.Length > 0)
throw new Exception($"Move personKey directories up one from {forceSingleImageHumanized} and delete {forceSingleImageHumanized} directory!");
if (!isDefault.Value)
{
if (personKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
files = RenameBirth(files);
else if (newestPersonKeyFormatted is not null && personKeyFormatted != newestPersonKeyFormatted)
{
if (!check)
check = true;
MovedToNewestPersonKeyFormatted(personKeyFormatted, newestPersonKeyFormatted, ticksDirectory, personKeyFormattedDirectory);
continue;
}
}
if (personKeyFormatted.Length != configuration.PersonBirthdayFormat.Length)
continue;
if (personDisplayDirectoryName.Length == 1 || isDefault.Value || !personKeyFormattedCollection.Contains(personKeyFormatted))
personFirstInitialDirectory = personNameDirectory;
else
{
personFirstInitial = personDisplayDirectoryName[..1];
if (personFirstInitial.All(char.IsDigit))
{
foreach (string file in files)
File.Delete(file);
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.AllDirectories);
foreach (string file in files)
File.Delete(file);
_ = IPath.DeleteEmptyDirectories(personNameDirectory);
continue;
}
personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial.ToString());
if (Directory.Exists(personFirstInitialDirectory))
throw new Exception("Forgot to ...");
Directory.Move(personNameDirectory, personFirstInitialDirectory);
files = Directory.GetFiles(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
}
records = GetRecords(propertyConfiguration, configuration, ticksDirectory, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName);
if (records.Count > 0)
results.AddRange(records);
personNameLinkDirectories = Directory.GetDirectories(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string personNameLinkDirectory in personNameLinkDirectories)
{
files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
if (!file.EndsWith(".lnk"))
continue;
File.Delete(file);
}
_ = IPath.DeleteEmptyDirectories(personNameLinkDirectory);
}
_ = IPath.DeleteEmptyDirectories(personFirstInitialDirectory);
}
_ = IPath.DeleteEmptyDirectories(yearDirectory);
}
_ = IPath.DeleteEmptyDirectories(personKeyFormattedDirectory);
} }
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
} }
progressBar.Dispose();
if (check)
continue;
break;
} }
_ = IPath.DeleteEmptyDirectories(personKeyDirectory); return results;
} }
private static List<TicksDirectory> UpdateDateVerifyAndGetTicksDirectories(Configuration configuration, string eDistanceContentDirectory) private static List<TicksDirectory> UpdateDateVerifyAndGetTicksDirectories(Configuration configuration, string eDistanceContentDirectory)
@ -86,53 +214,55 @@ internal abstract class DistanceLogic
float? totalDays; float? totalDays;
long? next = null; long? next = null;
string? checkDirectory; string? checkDirectory;
string ticksDirectoryName;
DateTime directoryDateTime; DateTime directoryDateTime;
DirectoryInfo directoryInfo; DirectoryInfo directoryInfo;
TicksDirectory ticksDirectory;
long? lastDirectoryTicks = null; long? lastDirectoryTicks = null;
DateTime dateTime = DateTime.Now; DateTime dateTime = DateTime.Now;
DateTime alternateDirectoryDateTime; DateTime alternateDirectoryDateTime;
string ticksDirectoryNameFirstSegment;
bool? isLocationContainerDebugDirectory; bool? isLocationContainerDebugDirectory;
long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks; long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks;
for (int i = 1; i < 5; i++) for (int i = 1; i < 5; i++)
_ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory);
if (!Directory.Exists(eDistanceContentDirectory)) if (!Directory.Exists(eDistanceContentDirectory))
_ = Directory.CreateDirectory(eDistanceContentDirectory); _ = Directory.CreateDirectory(eDistanceContentDirectory);
string[] ticksDirectories = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly); string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string ticksDirectory in ticksDirectories) foreach (string ticksFullPath in ticksFullPaths)
{ {
ticksDirectoryName = Path.GetFileName(ticksDirectory); ticksDirectoryNameFirstSegment = Path.GetFileName(ticksFullPath).Split('.')[0];
if (ticksDirectoryName.Length < 3) if (ticksDirectoryNameFirstSegment.Length < 3)
continue; continue;
if (!long.TryParse(ticksDirectoryName, out long directoryTicks)) if (!long.TryParse(ticksDirectoryNameFirstSegment, out long directoryTicks))
throw new NotSupportedException(); throw new NotSupportedException();
if (next is null) if (next is null)
next = new DateTime(directoryTicks).Ticks; next = new DateTime(directoryTicks).Ticks;
else else
{ {
next += month; next += month;
checkDirectory = Path.GetDirectoryName(ticksDirectory); checkDirectory = Path.GetDirectoryName(ticksFullPath);
if (string.IsNullOrEmpty(checkDirectory)) if (string.IsNullOrEmpty(checkDirectory))
{ {
if (string.IsNullOrEmpty(checkDirectory)) if (string.IsNullOrEmpty(checkDirectory))
continue; continue;
checkDirectory = Path.Combine(checkDirectory, next.Value.ToString()); checkDirectory = Path.Combine(checkDirectory, next.Value.ToString());
if (ticksDirectory == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern)) if (ticksFullPath == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern))
continue; continue;
Directory.Move(ticksDirectory, checkDirectory); Directory.Move(ticksFullPath, checkDirectory);
continue; continue;
} }
} }
directoryInfo = new(ticksDirectory); directoryInfo = new(ticksFullPath);
directoryDateTime = new DateTime(directoryTicks); directoryDateTime = new DateTime(directoryTicks);
if (directoryInfo.CreationTime.Ticks != directoryTicks) if (directoryInfo.CreationTime.Ticks != directoryTicks)
Directory.SetCreationTime(ticksDirectory, new DateTime(directoryTicks)); Directory.SetCreationTime(ticksFullPath, new DateTime(directoryTicks));
if (directoryInfo.LastWriteTime.Ticks != directoryTicks) if (directoryInfo.LastWriteTime.Ticks != directoryTicks)
Directory.SetLastWriteTime(ticksDirectory, new DateTime(directoryTicks)); Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks));
alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1); alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1);
isLocationContainerDebugDirectory = configuration.LocationContainerDebugDirectory is null ? null : ticksDirectoryName.EndsWith(configuration.LocationContainerDebugDirectory); isLocationContainerDebugDirectory = configuration.LocationContainerDebugDirectory is null ? null : ticksDirectoryNameFirstSegment.EndsWith(configuration.LocationContainerDebugDirectory);
totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays; totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays;
results.Add(new(alternateDirectoryDateTime, ticksDirectory, new(directoryTicks), ticksDirectoryName, isLocationContainerDebugDirectory, totalDays)); ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), isLocationContainerDebugDirectory, totalDays);
results.Add(ticksDirectory);
if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0) if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0)
continue; continue;
lastDirectoryTicks = directoryTicks; lastDirectoryTicks = directoryTicks;
@ -216,38 +346,42 @@ internal abstract class DistanceLogic
} }
} }
private static List<Record> GetRecords(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List<string> distinct, string? personDisplayDirectoryName) private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames)
{ {
List<Record> results = [];
string fileName;
string checkFile; string checkFile;
FilePath filePath; string actionDirectoryName = Path.GetFileName(actionDirectory);
FileHolder fileHolder; string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName);
int? wholePercentages; if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
foreach (string file in files) foreach (string file in files)
{ {
if (file.EndsWith(".lnk")) if (facesFileNames.Contains(file))
continue;
fileHolder = IFileHolder.Get(file);
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath);
if (wholePercentages is null)
continue;
fileName = Path.GetFileName(file);
if (distinct.Contains(fileName))
{ {
checkFile = $"{file}.dup"; checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
if (File.Exists(checkFile)) if (File.Exists(checkFile))
continue; continue;
File.Move(file, checkFile); File.Move(file, checkFile);
continue; continue;
} }
distinct.Add(fileName); File.Delete(file);
results.Add(new(directoryNumber, isDefault, linksCount, filePath, personDisplayDirectoryName, personKeyFormatted));
} }
return results; }
private static int? GetLinksCount(string yearDirectory)
{
int? result;
string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-');
if (yearDirectoryNameSegments.Length != 3)
result = null;
else
{
string lastSegment = yearDirectoryNameSegments[^1];
if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0]))
result = null;
else
result = lastSegment[0] - 65;
}
return result;
} }
private static string[] RenameBirth(string[] files) private static string[] RenameBirth(string[] files)
@ -282,219 +416,117 @@ internal abstract class DistanceLogic
Directory.Move(personKeyDirectory, newestPersonKeyDirectory); Directory.Move(personKeyDirectory, newestPersonKeyDirectory);
} }
private static int? GetLinksCount(string yearDirectory) private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
{ {
int? result; string[] files;
string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-'); string checkFile;
if (yearDirectoryNameSegments.Length != 3) string? checkDirectory;
result = null; string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
else foreach (string directory in directories)
{ {
string lastSegment = yearDirectoryNameSegments[^1]; checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory));
if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0])) if (!Directory.Exists(checkDirectory))
result = null; Directory.Move(directory, checkDirectory);
else else
result = lastSegment[0] - 65; {
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted))
continue;
checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted);
checkDirectory = Path.GetDirectoryName(checkFile);
if (checkDirectory is null)
continue;
if (File.Exists(checkFile))
continue;
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
File.Move(file, checkFile);
}
}
} }
return result; _ = IPath.DeleteEmptyDirectories(personKeyDirectory);
} }
internal static List<Record> DeleteEmptyDirectoriesAndGetCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, string eDistanceContentDirectory, ReadOnlyDictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted, ReadOnlyCollection<string> personKeyFormattedCollection) private static List<Record> GetRecords(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, TicksDirectory ticksDirectory, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List<string> distinct, string? personDisplayDirectoryName)
{ {
List<Record> results = []; List<Record> results = [];
bool check; string @enum;
string message; Record record;
string[] files; string fileName;
bool? isDefault; string checkFile;
int? linksCount; FilePath filePath;
int totalSeconds; FileHolder fileHolder;
DateTime dateTime; int? wholePercentages;
TimeSpan timeSpan; foreach (string file in files)
int directoryNumber;
string? checkDirectory;
ProgressBar progressBar;
string[] yearDirectories;
string personKeyFormatted;
List<string> distinct = [];
string? personFirstInitial;
bool isReservedDirectoryName;
string[] personNameDirectories;
string? newestPersonKeyFormatted;
string? personDisplayDirectoryName;
string[] personNameLinkDirectories;
string? personFirstInitialDirectory;
List<TicksDirectory> ticksDirectories;
string[] personKeyFormattedDirectories;
string manualCopyHumanized = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy).Humanize(LetterCasing.Title);
string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
for (int i = 1; i < 6; i++)
{ {
check = false; if (file.EndsWith(".lnk"))
results.Clear();
distinct.Clear();
directoryNumber = 0;
ticksDirectories = UpdateDateVerifyAndGetTicksDirectories(configuration, eDistanceContentDirectory);
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{i}) {ticksDirectories.Count:000} compile from and clean ticks Director(ies) - B - {totalSeconds} total second(s)";
progressBar = new(ticksDirectories.Count, message, options);
foreach (TicksDirectory ticksDirectory in ticksDirectories)
{
if (i == 1)
progressBar.Tick();
personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly);
foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories)
{
personKeyFormatted = Path.GetFileName(personKeyFormattedDirectory);
isReservedDirectoryName = personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Sorting)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Mapping)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.ManualCopy));
if (!isReservedDirectoryName && personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Individually)))
{
Individually(configuration, ticksDirectory, personKeyFormattedDirectory);
throw new Exception($"B) Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!");
}
_ = personKeyFormattedToNewestPersonKeyFormatted.TryGetValue(personKeyFormatted, out newestPersonKeyFormatted);
if (personKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
{
timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks);
if (timeSpan.TotalDays > 6)
throw new Exception($"{configuration.MappingDefaultName} <{ticksDirectory.DirectoryDateTime}> are only allowed within x days!");
}
yearDirectories = Directory.GetDirectories(personKeyFormattedDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string yearDirectory in yearDirectories)
{
if (check && !Directory.Exists(yearDirectory))
continue;
if (ticksDirectory.IsLocationContainerDebugDirectory is null || !ticksDirectory.IsLocationContainerDebugDirectory.Value)
linksCount = null;
else
linksCount = GetLinksCount(yearDirectory);
if (ticksDirectory.DirectoryName != configuration.LocationContainerDebugDirectory)
{
files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
File.Delete(file);
}
if (ticksDirectory.DirectoryName == configuration.LocationContainerDebugDirectory)
{
isDefault = null;
personDisplayDirectoryName = null;
files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly);
results.AddRange(GetRecords(propertyConfiguration, configuration, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName));
files = Directory.GetFiles(yearDirectory, "*.lnk", SearchOption.AllDirectories);
foreach (string file in files)
File.Delete(file);
continue;
}
personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
if (personNameDirectories.Length > 1)
throw new NotSupportedException("Try deleting *.lnk files!");
foreach (string personNameDirectory in personNameDirectories)
{
directoryNumber++;
personDisplayDirectoryName = Path.GetFileName(personNameDirectory);
isDefault = IPerson.IsDefaultName(personDisplayDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]);
if (isDefault.Value && personDisplayDirectoryName.Length == 1)
{
if (personKeyFormatted.Length != configuration.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, configuration.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
continue;
checkDirectory = Path.Combine(yearDirectory, $"X+{dateTime.Ticks}");
if (Directory.Exists(checkDirectory))
{
Directory.Delete(yearDirectory, recursive: true);
continue;
}
Directory.Move(personNameDirectory, checkDirectory);
if (!check)
check = true;
continue;
}
if (isDefault.Value && (ticksDirectory.DirectoryDateTime.Hour != 0 || ticksDirectory.DirectoryDateTime.Minute != 0 || ticksDirectory.DirectoryDateTime.Second != 0))
{
checkDirectory = Path.GetDirectoryName(ticksDirectory.Directory);
if (checkDirectory is null)
continue;
checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString());
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
checkDirectory = Path.Combine(checkDirectory, personKeyFormatted);
if (!Directory.Exists(checkDirectory))
{
Directory.Move(personKeyFormattedDirectory, checkDirectory);
if (!check)
check = true;
break;
}
}
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly);
if (isReservedDirectoryName && files.Length > 0)
throw new Exception($"Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!");
if (personKeyFormatted == manualCopyHumanized && files.Length > 0)
throw new Exception($"Move personKey directories up one from {manualCopyHumanized} and delete {manualCopyHumanized} directory!");
if (personKeyFormatted == forceSingleImageHumanized && files.Length > 0)
throw new Exception($"Move personKey directories up one from {forceSingleImageHumanized} and delete {forceSingleImageHumanized} directory!");
if (!isDefault.Value)
{
if (personKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
files = RenameBirth(files);
else if (newestPersonKeyFormatted is not null && personKeyFormatted != newestPersonKeyFormatted)
{
if (!check)
check = true;
MovedToNewestPersonKeyFormatted(personKeyFormatted, newestPersonKeyFormatted, ticksDirectory, personKeyFormattedDirectory);
continue;
}
}
if (personKeyFormatted.Length != configuration.PersonBirthdayFormat.Length)
continue;
if (personDisplayDirectoryName.Length == 1 || isDefault.Value || !personKeyFormattedCollection.Contains(personKeyFormatted))
personFirstInitialDirectory = personNameDirectory;
else
{
personFirstInitial = personDisplayDirectoryName[..1];
if (personFirstInitial.All(char.IsDigit))
{
foreach (string file in files)
File.Delete(file);
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.AllDirectories);
foreach (string file in files)
File.Delete(file);
_ = IPath.DeleteEmptyDirectories(personNameDirectory);
continue;
}
personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial.ToString());
if (Directory.Exists(personFirstInitialDirectory))
throw new Exception("Forgot to ...");
Directory.Move(personNameDirectory, personFirstInitialDirectory);
files = Directory.GetFiles(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
}
results.AddRange(GetRecords(propertyConfiguration, configuration, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName));
personNameLinkDirectories = Directory.GetDirectories(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string personNameLinkDirectory in personNameLinkDirectories)
{
files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
if (!file.EndsWith(".lnk"))
continue;
File.Delete(file);
}
_ = IPath.DeleteEmptyDirectories(personNameLinkDirectory);
}
_ = IPath.DeleteEmptyDirectories(personFirstInitialDirectory);
}
_ = IPath.DeleteEmptyDirectories(yearDirectory);
}
_ = IPath.DeleteEmptyDirectories(personKeyFormattedDirectory);
}
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
}
progressBar.Dispose();
if (check)
continue; continue;
break; fileHolder = IFileHolder.Get(file);
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath);
if (wholePercentages is null)
continue;
fileName = Path.GetFileName(file);
if (distinct.Contains(fileName))
{
checkFile = $"{file}.dup";
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
continue;
}
if (file.StartsWith(ticksDirectory.Directory))
{
@enum = IPath.GetEnum(filePath).ToString();
if (!ticksDirectory.Directory.EndsWith(@enum))
{
checkFile = GetCheckFile(ticksDirectory, @enum, fileName, file);
fileHolder = IFileHolder.Get(checkFile);
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
continue;
}
}
distinct.Add(fileName);
record = new(DirectoryNumber: directoryNumber,
IsDefault: isDefault,
LinksCount: linksCount,
MappedFaceFilePath: filePath,
PersonDisplayDirectoryName: personDisplayDirectoryName,
PersonKeyFormatted: personKeyFormatted);
results.Add(record);
} }
return results; return results;
} }
private static string GetCheckFile(TicksDirectory ticksDirectory, string @enum, string fileName, string file)
{
string result;
string checkDirectory;
string directory = file;
List<string> collection = [];
for (int i = 0; i < file.Length; i++)
{
directory = Path.GetDirectoryName(directory) ?? throw new Exception();
if (directory == ticksDirectory.Directory)
break;
collection.Add(Path.GetFileName(directory));
}
collection.Reverse();
checkDirectory = $"{ticksDirectory.Directory}.{@enum}";
foreach (string directoryName in collection)
checkDirectory = Path.Combine(checkDirectory, directoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
result = Path.Combine(checkDirectory, fileName);
if (File.Exists(result))
throw new Exception($"File <{fileName}> already exists!");
File.Move(file, result);
return result;
}
} }

View File

@ -87,7 +87,7 @@ internal abstract class FaceFileLogic
Dictionary<int, ReadOnlyDictionary<int, LocationContainer>> results = []; Dictionary<int, ReadOnlyDictionary<int, LocationContainer>> results = [];
foreach (KeyValuePair<int, Dictionary<int, LocationContainer>> keyValuePair in keyValuePairs) foreach (KeyValuePair<int, Dictionary<int, LocationContainer>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value)); results.Add(keyValuePair.Key, new(keyValuePair.Value));
return new(results); return results.AsReadOnly();
} }
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory)

File diff suppressed because it is too large Load Diff

View File

@ -7,69 +7,93 @@ namespace View_by_Distance.Map.Models.Stateless.Methods;
public interface IMapLogic public interface IMapLogic
{ {
ReadOnlyDictionary<int, List<long>> TestStatic_GetIdToPersonKeys(ReadOnlyDictionary<long, List<int>> personKeyToIds) => static string Get(bool saveIndividually, string forceSingleImageHumanized, int by, bool isDefaultName) =>
GetIdToPersonKeys(personKeyToIds); $"{by switch
static ReadOnlyDictionary<int, List<long>> GetIdToPersonKeys(ReadOnlyDictionary<long, List<int>> personKeyToIds) => {
MapLogic.GetIdToPersonKeys(personKeyToIds); Shared.Models.Stateless.IMapLogic.Mapping => nameof(Shared.Models.Stateless.IMapLogic.Mapping),
Shared.Models.Stateless.IMapLogic.Sorting => saveIndividually ?
nameof(Shared.Models.Stateless.IMapLogic.Individually) :
nameof(Shared.Models.Stateless.IMapLogic.Sorting),
Shared.Models.Stateless.IMapLogic.ForceSingleImage => forceSingleImageHumanized,
_ => throw new NotImplementedException()
}}{(!isDefaultName ? "-A" : "-Z")}";
ReadOnlyCollection<Face> TestStatic_GetFaces(ReadOnlyCollection<Item> items) => public static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Item> items) =>
GetFaces(items);
static ReadOnlyCollection<Face> GetFaces(ReadOnlyCollection<Item> items) =>
MapLogic.GetFaces(items);
Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection<Item> items) =>
GetSelectedMappingCollection(items);
static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Item> items) =>
MapLogic.GetSelectedMappingCollection(items); MapLogic.GetSelectedMappingCollection(items);
Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection<Face> faces) => public static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Face> faces) =>
GetSelectedMappingCollection(faces);
static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Face> faces) =>
MapLogic.GetSelectedMappingCollection(faces); MapLogic.GetSelectedMappingCollection(faces);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> TestStatic_GetIdToWholePercentagesToFace(ReadOnlyCollection<Mapping> distinctValidImageMappingCollection) => public static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages) =>
GetIdToWholePercentagesToFace(distinctValidImageMappingCollection);
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> GetIdToWholePercentagesToFace(ReadOnlyCollection<Mapping> distinctValidImageMappingCollection) =>
MapLogic.GetIdToWholePercentagesToFace(distinctValidImageMappingCollection);
List<(string, long)> TestStatic_GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) =>
GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory);
static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) =>
MapLogic.GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory);
void TestStatic_SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers) =>
SetCreationTime(mappingFromItem, locationContainers);
static void SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers) =>
DecadeLogic.SetCreationTime(mappingFromItem, locationContainers);
void TestStatic_MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers) =>
MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers);
static void MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers) =>
DecadeLogic.MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers);
bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) =>
CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);
static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) =>
MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages);
bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages) =>
CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages);
static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages) =>
MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages); MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages);
string TestStatic_GetDecade(MappingFromItem mappingFromItem) => public static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) =>
GetDecade(mappingFromItem); MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages);
static string GetDecade(MappingFromItem mappingFromItem) =>
public static string GetDecade(MappingFromItem mappingFromItem) =>
DecadeLogic.GetDecade(mappingFromItem, null); DecadeLogic.GetDecade(mappingFromItem, null);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> TestStatic_GetMappedFiles(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) => public static ReadOnlyCollection<Face> GetFaces(ReadOnlyCollection<Item> items) =>
GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory); MapLogic.GetFaces(items);
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) =>
FaceFileLogic.GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory);
List<LocationContainer> TestStatic_GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection<FilePath> filePaths) => public static ReadOnlyDictionary<int, List<long>> GetIdToPersonKeys(ReadOnlyDictionary<long, List<int>> personKeyToIds) =>
GetAvailable(maxDegreeOfParallelism, configuration, dFace, ticks, filePaths); MapLogic.GetIdToPersonKeys(personKeyToIds);
static List<LocationContainer> GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection<FilePath> filePaths) =>
public static void SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers) =>
DecadeLogic.SetCreationTime(mappingFromItem, locationContainers);
public static ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> GetIdToWholePercentagesToFace(ReadOnlyCollection<Mapping> distinctValidImageMappingCollection) =>
MapLogic.GetIdToWholePercentagesToFace(distinctValidImageMappingCollection);
public static List<LocationContainer> GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection<FilePath> filePaths) =>
FaceFileLogic.GetAvailable(maxDegreeOfParallelism, configuration, dFace, ticks, filePaths); FaceFileLogic.GetAvailable(maxDegreeOfParallelism, configuration, dFace, ticks, filePaths);
public static void MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers) =>
DecadeLogic.MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers);
public static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) =>
MapLogic.GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory);
public static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) =>
FaceFileLogic.GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory);
internal string TestStatic_GetDecade(MappingFromItem mappingFromItem) =>
GetDecade(mappingFromItem);
internal ReadOnlyCollection<Face> TestStatic_GetFaces(ReadOnlyCollection<Item> items) =>
GetFaces(items);
internal Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection<Item> items) =>
GetSelectedMappingCollection(items);
internal Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection<Face> faces) =>
GetSelectedMappingCollection(faces);
internal ReadOnlyDictionary<int, List<long>> TestStatic_GetIdToPersonKeys(ReadOnlyDictionary<long, List<int>> personKeyToIds) =>
GetIdToPersonKeys(personKeyToIds);
internal void TestStatic_SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers) =>
SetCreationTime(mappingFromItem, locationContainers);
internal ReadOnlyDictionary<int, ReadOnlyDictionary<int, Mapping>> TestStatic_GetIdToWholePercentagesToFace(ReadOnlyCollection<Mapping> distinctValidImageMappingCollection) =>
GetIdToWholePercentagesToFace(distinctValidImageMappingCollection);
internal List<LocationContainer> TestStatic_GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection<FilePath> filePaths) =>
GetAvailable(maxDegreeOfParallelism, configuration, dFace, ticks, filePaths);
internal void TestStatic_MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers) =>
MoveToDecade(propertyConfiguration, mappingFromItem, locationContainers);
internal bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages) =>
CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages);
internal bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) =>
CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);
internal List<(string, long)> TestStatic_GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) =>
GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory);
internal ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> TestStatic_GetMappedFiles(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) =>
GetMapped(maxDegreeOfParallelism, propertyConfiguration, configuration, ticks, personContainers, a2PeopleSingletonDirectory, eDistanceContentDirectory);
} }

View File

@ -8,36 +8,48 @@ namespace View_by_Distance.Map.Models.Stateless;
internal abstract class RelationLogic internal abstract class RelationLogic
{ {
internal record Group(string Key, long PersonKey, ReadOnlyCollection<LocationContainer> RelationContainersCollection); internal record Group(string Key,
long PersonKey,
ReadOnlyCollection<LocationContainer> RelationContainersCollection);
private static Dictionary<long, Dictionary<int, List<LocationContainer>>> GetPersonKeyTo(Configuration configuration, List<LocationContainer> locationContainers) internal static void SaveMappedRelations(Configuration configuration, Shared.Models.Methods.IDistance distance, string a2PeopleContentDirectory, string eDistanceContentDirectory, long ticks, List<LocationContainer> locationContainers, ReadOnlyDictionary<string, PersonContainer> readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary<long, List<PersonContainer>> readOnlyPersonKeyToPersonContainerCollection)
{ {
List<LocationContainer>? collection; int take;
Dictionary<int, List<LocationContainer>>? yearTo; string directory;
Dictionary<long, Dictionary<int, List<LocationContainer>>> personKeyTo = []; bool isCounterPersonYear;
foreach (LocationContainer locationContainer in locationContainers) string personKeyFormatted;
string? displayDirectoryName;
Uri uri = new(eDistanceContentDirectory);
ReadOnlyDictionary<string, string> movedFiles;
ReadOnlyCollection<RelationContainer> relationContainers;
ReadOnlyCollection<Group> groups = GetGroups(configuration, locationContainers);
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
string message = $") Save Mapped Relations - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(groups.Count, message, options);
foreach (Group group in groups)
{ {
if (locationContainer.PersonKey is null) if (configuration.LocationContainerDistanceTolerance is null)
break;
progressBar.Tick();
if (group.RelationContainersCollection.Count == 0)
continue; continue;
if (!locationContainer.FromDistanceContent) take = GetTake(configuration.LocationContainerDistanceTake, group.RelationContainersCollection.Count);
continue; isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(new DateTime(group.PersonKey).Year);
if (!locationContainer.FilePath.FullName.Contains(configuration.LocationContainerDirectoryPattern)) personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, group.PersonKey);
continue; displayDirectoryName = GetDisplayDirectoryName(readOnlyPersonKeyToPersonContainerCollection, readOnlyPersonKeyFormattedToPersonContainer, group.PersonKey, personKeyFormatted);
if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo)) directory = Path.Combine(a2PeopleContentDirectory, $"{ticks}-{configuration.LocationContainerDistanceTolerance.Value}", personKeyFormatted, group.Key);
{ if (!Directory.Exists(directory))
personKeyTo.Add(locationContainer.PersonKey.Value, []); _ = Directory.CreateDirectory(directory);
if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo)) WriteVsCodeFiles(eDistanceContentDirectory, displayDirectoryName, directory);
throw new Exception(); relationContainers = distance.GetRelationContainers(configuration.DistanceLimits, configuration.FaceDistancePermyriad, configuration.LocationContainerDistanceTake, configuration.LocationContainerDistanceTolerance.Value, group.RelationContainersCollection);
} movedFiles = GetMoveFiles(configuration, group.Key, take, isCounterPersonYear, displayDirectoryName, relationContainers);
if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection)) WriteFile(take, group.PersonKey, isCounterPersonYear, personKeyFormatted, displayDirectoryName, directory, ticks, uri, relationContainers, movedFiles);
{
yearTo.Add(locationContainer.CreationDateOnly.Year, []);
if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection))
throw new Exception();
}
collection.Add(locationContainer);
} }
return personKeyTo; if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory))
_ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory);
else
AddDisplayDirectoryNames(configuration, eDistanceContentDirectory, readOnlyPersonKeyFormattedToPersonContainer, readOnlyPersonKeyToPersonContainerCollection, groups);
} }
private static ReadOnlyCollection<Group> GetGroups(Configuration configuration, List<LocationContainer> locationContainers) private static ReadOnlyCollection<Group> GetGroups(Configuration configuration, List<LocationContainer> locationContainers)
@ -82,12 +94,127 @@ internal abstract class RelationLogic
locationContainer = collection[0]; locationContainer = collection[0];
if (locationContainer.PersonKey is null) if (locationContainer.PersonKey is null)
continue; continue;
results.Add(new(key, locationContainer.PersonKey.Value, new(collection))); results.Add(new(key, locationContainer.PersonKey.Value, collection.AsReadOnly()));
collection = []; collection = [];
years.Clear(); years.Clear();
} }
} }
return new(results); return results.AsReadOnly();
}
private static Dictionary<long, Dictionary<int, List<LocationContainer>>> GetPersonKeyTo(Configuration configuration, List<LocationContainer> locationContainers)
{
List<LocationContainer>? collection;
Dictionary<int, List<LocationContainer>>? yearTo;
Dictionary<long, Dictionary<int, List<LocationContainer>>> personKeyTo = [];
foreach (LocationContainer locationContainer in locationContainers)
{
if (locationContainer.PersonKey is null)
continue;
if (!locationContainer.FromDistanceContent)
continue;
if (!locationContainer.FilePath.FullName.Contains(configuration.LocationContainerDirectoryPattern))
continue;
if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo))
{
personKeyTo.Add(locationContainer.PersonKey.Value, []);
if (!personKeyTo.TryGetValue(locationContainer.PersonKey.Value, out yearTo))
throw new Exception();
}
if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection))
{
yearTo.Add(locationContainer.CreationDateOnly.Year, []);
if (!yearTo.TryGetValue(locationContainer.CreationDateOnly.Year, out collection))
throw new Exception();
}
collection.Add(locationContainer);
}
return personKeyTo;
}
private static int GetTake(int locationContainerDistanceTake, int count)
{
int result = locationContainerDistanceTake;
int subtract = (int)(locationContainerDistanceTake * .05);
if (subtract < 1)
subtract = 1;
if (count > 9000)
result -= subtract;
if (count > 8000)
result -= subtract;
if (count > 7000)
result -= subtract;
if (count > 6000)
result -= subtract;
if (count > 5000)
result -= subtract;
if (count > 4000)
result -= subtract;
if (count > 3000)
result -= subtract;
if (count > 2000)
result -= subtract;
if (count > 1000)
result -= subtract;
if (result < 3)
result = 3;
return result;
}
private static string? GetDisplayDirectoryName(ReadOnlyDictionary<long, List<PersonContainer>> readOnlyPersonKeyToPersonContainerCollection, ReadOnlyDictionary<string, PersonContainer> readOnlyPersonKeyFormattedToPersonContainer, long personKey, string personKeyFormatted)
{
string? result;
PersonContainer? personContainer;
List<PersonContainer>? collection;
_ = readOnlyPersonKeyToPersonContainerCollection.TryGetValue(personKey, out collection);
if (collection is not null)
result = collection[0].DisplayDirectoryName;
else
{
if (!readOnlyPersonKeyFormattedToPersonContainer.TryGetValue(personKeyFormatted, out personContainer))
result = null;
else
result = personContainer.DisplayDirectoryName;
}
return result;
}
private static void WriteVsCodeFiles(string eDistanceContentDirectory, string? displayDirectoryName, string directory)
{
string json;
string vsCodeDirectory = Path.Combine(directory, ".vscode");
if (!Directory.Exists(vsCodeDirectory))
_ = Directory.CreateDirectory(vsCodeDirectory);
if (displayDirectoryName is not null)
File.WriteAllText(Path.Combine(directory, $"_ {displayDirectoryName}.txt"), string.Empty);
json = /*lang=json*/ """{ "[markdown]": { "editor.wordWrap": "off" }, "foam.links.hover.enable": false, "foam.graph.style": { "background": "#202020", "node": { "note": "#f2cb1d", "distance": "green", "image": "orange", "placeholder": "white", } } }""";
_ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "settings.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
json = string.Concat("{ \"version\": \"2.0.0\", \"tasks\": [ { \"label\": \"MKLink\", \"type\": \"shell\", \"command\": \"New-Item\", \"args\": [ \"-ItemType\", \"Junction\", \"-Path\", \"'", directory.Replace('\\', '/'), "/()'\", \"-Target\", \"'", eDistanceContentDirectory.Replace('\\', '/'), "'\" ], \"problemMatcher\": [] } ] }");
_ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "tasks.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
}
private static ReadOnlyDictionary<string, string> GetMoveFiles(Configuration configuration, string key, int take, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection<RelationContainer> relationContainers)
{
ReadOnlyDictionary<string, string> results;
List<List<string>> linked = [];
for (int i = 0; i < 25; i++)
linked.Add([]);
foreach ((FileHolder fileHolder, ReadOnlyCollection<Relation> relations) in relationContainers)
{
foreach (Relation relation in relations.Take(take))
{
for (int i = 0; i < 25; i++)
{
if (!linked[i].Contains(relation.File))
{
linked[i].Add(relation.File);
break;
}
}
}
}
results = MoveFiles(configuration, key, isCounterPersonYear, displayDirectoryName, relationContainers, linked);
return results;
} }
private static ReadOnlyDictionary<string, string> MoveFiles(Configuration configuration, string key, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection<RelationContainer> relationContainers, List<List<string>> linked) private static ReadOnlyDictionary<string, string> MoveFiles(Configuration configuration, string key, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection<RelationContainer> relationContainers, List<List<string>> linked)
@ -202,92 +329,7 @@ internal abstract class RelationLogic
_ = Directory.CreateDirectory(checkDirectory); _ = Directory.CreateDirectory(checkDirectory);
} }
} }
return new(results); return results.AsReadOnly();
}
private static string? GetDisplayDirectoryName(ReadOnlyDictionary<long, List<PersonContainer>> readOnlyPersonKeyToPersonContainerCollection, ReadOnlyDictionary<string, PersonContainer> readOnlyPersonKeyFormattedToPersonContainer, long personKey, string personKeyFormatted)
{
string? result;
PersonContainer? personContainer;
List<PersonContainer>? collection;
_ = readOnlyPersonKeyToPersonContainerCollection.TryGetValue(personKey, out collection);
if (collection is not null)
result = collection[0].DisplayDirectoryName;
else
{
if (!readOnlyPersonKeyFormattedToPersonContainer.TryGetValue(personKeyFormatted, out personContainer))
result = null;
else
result = personContainer.DisplayDirectoryName;
}
return result;
}
private static int GetTake(int locationContainerDistanceTake, int count)
{
int result = locationContainerDistanceTake;
int subtract = (int)(locationContainerDistanceTake * .05);
if (subtract < 1)
subtract = 1;
if (count > 9000)
result -= subtract;
if (count > 8000)
result -= subtract;
if (count > 7000)
result -= subtract;
if (count > 6000)
result -= subtract;
if (count > 5000)
result -= subtract;
if (count > 4000)
result -= subtract;
if (count > 3000)
result -= subtract;
if (count > 2000)
result -= subtract;
if (count > 1000)
result -= subtract;
if (result < 3)
result = 3;
return result;
}
private static void WriteVsCodeFiles(string eDistanceContentDirectory, string? displayDirectoryName, string directory)
{
string json;
string vsCodeDirectory = Path.Combine(directory, ".vscode");
if (!Directory.Exists(vsCodeDirectory))
_ = Directory.CreateDirectory(vsCodeDirectory);
if (displayDirectoryName is not null)
File.WriteAllText(Path.Combine(directory, $"_ {displayDirectoryName}.txt"), string.Empty);
json = /*lang=json*/ """{ "[markdown]": { "editor.wordWrap": "off" }, "foam.links.hover.enable": false, "foam.graph.style": { "background": "#202020", "node": { "note": "#f2cb1d", "distance": "green", "image": "orange", "placeholder": "white", } } }""";
_ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "settings.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
json = string.Concat("{ \"version\": \"2.0.0\", \"tasks\": [ { \"label\": \"MKLink\", \"type\": \"shell\", \"command\": \"New-Item\", \"args\": [ \"-ItemType\", \"Junction\", \"-Path\", \"'", directory.Replace('\\', '/'), "/()'\", \"-Target\", \"'", eDistanceContentDirectory.Replace('\\', '/'), "'\" ], \"problemMatcher\": [] } ] }");
_ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "tasks.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
}
private static ReadOnlyDictionary<string, string> GetMoveFiles(Configuration configuration, string key, int take, bool isCounterPersonYear, string? displayDirectoryName, ReadOnlyCollection<RelationContainer> relationContainers)
{
ReadOnlyDictionary<string, string> results;
List<List<string>> linked = [];
for (int i = 0; i < 25; i++)
linked.Add([]);
foreach ((FileHolder fileHolder, ReadOnlyCollection<Relation> relations) in relationContainers)
{
foreach (Relation relation in relations.Take(take))
{
for (int i = 0; i < 25; i++)
{
if (!linked[i].Contains(relation.File))
{
linked[i].Add(relation.File);
break;
}
}
}
}
results = MoveFiles(configuration, key, isCounterPersonYear, displayDirectoryName, relationContainers, linked);
return results;
} }
private static void WriteFile(int take, long personKey, bool isCounterPersonYear, string personKeyFormatted, string? displayDirectoryName, string directory, long ticks, Uri uri, ReadOnlyCollection<RelationContainer> relationContainers, ReadOnlyDictionary<string, string> movedFiles) private static void WriteFile(int take, long personKey, bool isCounterPersonYear, string personKeyFormatted, string? displayDirectoryName, string directory, long ticks, Uri uri, ReadOnlyCollection<RelationContainer> relationContainers, ReadOnlyDictionary<string, string> movedFiles)
@ -398,44 +440,4 @@ internal abstract class RelationLogic
} }
} }
internal static void SaveMappedRelations(Configuration configuration, Shared.Models.Methods.IDistance distance, string a2PeopleContentDirectory, string eDistanceContentDirectory, long ticks, List<LocationContainer> locationContainers, ReadOnlyDictionary<string, PersonContainer> readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary<long, List<PersonContainer>> readOnlyPersonKeyToPersonContainerCollection)
{
int take;
string directory;
bool isCounterPersonYear;
string personKeyFormatted;
string? displayDirectoryName;
Uri uri = new(eDistanceContentDirectory);
ReadOnlyDictionary<string, string> movedFiles;
ReadOnlyCollection<RelationContainer> relationContainers;
ReadOnlyCollection<Group> groups = GetGroups(configuration, locationContainers);
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
string message = $") Save Mapped Relations - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(groups.Count, message, options);
foreach (Group group in groups)
{
if (configuration.LocationContainerDistanceTolerance is null)
break;
progressBar.Tick();
if (group.RelationContainersCollection.Count == 0)
continue;
take = GetTake(configuration.LocationContainerDistanceTake, group.RelationContainersCollection.Count);
isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(new DateTime(group.PersonKey).Year);
personKeyFormatted = IPersonBirthday.GetFormatted(configuration.PersonBirthdayFormat, group.PersonKey);
displayDirectoryName = GetDisplayDirectoryName(readOnlyPersonKeyToPersonContainerCollection, readOnlyPersonKeyFormattedToPersonContainer, group.PersonKey, personKeyFormatted);
directory = Path.Combine(a2PeopleContentDirectory, $"{ticks}-{configuration.LocationContainerDistanceTolerance.Value}", personKeyFormatted, group.Key);
if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory);
WriteVsCodeFiles(eDistanceContentDirectory, displayDirectoryName, directory);
relationContainers = distance.GetRelationContainers(configuration.DistanceLimits, configuration.FaceDistancePermyriad, configuration.LocationContainerDistanceTake, configuration.LocationContainerDistanceTolerance.Value, group.RelationContainersCollection);
movedFiles = GetMoveFiles(configuration, group.Key, take, isCounterPersonYear, displayDirectoryName, relationContainers);
WriteFile(take, group.PersonKey, isCounterPersonYear, personKeyFormatted, displayDirectoryName, directory, ticks, uri, relationContainers, movedFiles);
}
if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory))
_ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory);
else
AddDisplayDirectoryNames(configuration, eDistanceContentDirectory, readOnlyPersonKeyFormattedToPersonContainer, readOnlyPersonKeyToPersonContainerCollection, groups);
}
} }

View File

@ -35,10 +35,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />

View File

@ -34,10 +34,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -4,6 +4,7 @@ using System.Text.Json;
using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Methods; using View_by_Distance.Shared.Models.Methods;
using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Metadata.Models; namespace View_by_Distance.Metadata.Models;
@ -13,33 +14,103 @@ namespace View_by_Distance.Metadata.Models;
public class B_Metadata : IMetadata<MetadataExtractor.Directory> 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 bool _PropertiesChangedForMetadata;
private readonly IPropertyConfiguration _PropertyConfiguration; private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly bool _ForceMetadataLastWriteTimeToCreationTime; private readonly bool _ForceMetadataLastWriteTimeToCreationTime;
private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions; private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions;
private readonly ReadOnlyDictionary<string, ReadOnlyCollection<string>> _FileGroups; private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups;
public B_Metadata(IPropertyConfiguration propertyConfiguration) public B_Metadata(IDlibDotNet? dlibDotNet, IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, long ticks, string bResultsFullGroupDirectory)
{
_PropertiesChangedForMetadata = false;
_PropertyConfiguration = propertyConfiguration;
_ForceMetadataLastWriteTimeToCreationTime = false;
_WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
_FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, null, [propertyConfiguration.ResultSingleton]);
}
public B_Metadata(IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, string bResultsFullGroupDirectory)
{ {
_PropertyConfiguration = propertyConfiguration; _PropertyConfiguration = propertyConfiguration;
_PropertiesChangedForMetadata = propertiesChangedForMetadata; _PropertiesChangedForMetadata = propertiesChangedForMetadata;
_ForceMetadataLastWriteTimeToCreationTime = forceMetadataLastWriteTimeToCreationTime; _ForceMetadataLastWriteTimeToCreationTime = forceMetadataLastWriteTimeToCreationTime;
_ResultSingletonFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
_WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
_FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, bResultsFullGroupDirectory, [propertyConfiguration.ResultSingleton]); ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, bResultsFullGroupDirectory, [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();
}
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; return result;
} }
@ -47,8 +118,11 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
{ {
ExifDirectory? result = null; ExifDirectory? result = null;
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath); CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
FileInfo fileInfo = new(Path.Combine(_FileGroups[_PropertyConfiguration.ResultSingleton][directoryIndex], $"{mappingFromItem.FilePath.NameWithoutExtension}{mappingFromItem.FilePath.ExtensionLowered}.json")); string fileName = $"{mappingFromItem.FilePath.NameWithoutExtension}{mappingFromItem.FilePath.ExtensionLowered}.json";
string directory = _ResultSingletonFileGroups[0][cei.Enum][cei.Index];
FileInfo fileInfo = new(Path.Combine(directory, fileName));
MoveIf(fileName, cei, directory, fileInfo);
if (_ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) if (_ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{ {
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName); File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
@ -104,13 +178,32 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
return result; 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) (DateTime?, DateTime?[]) IMetadata<MetadataExtractor.Directory>.GetDateTimes(FilePath filePath, IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<DateTime?> results = []; List<DateTime?> results = [];
DateTime? result = null; DateTime? result = null;
DateTime? dateTime; DateTime? dateTime;
DateTime checkDateTime; DateTime checkDateTime;
string dateTimeFormat = Property.Models.Stateless.IProperty.DateTimeFormat(); string dateTimeFormat = Stateless.Methods.IMetadata.DateTimeFormat();
MetadataExtractor.Formats.Exif.ExifDirectoryBase? exifDirectoryBase = directories.OfType<MetadataExtractor.Formats.Exif.ExifDirectoryBase>().FirstOrDefault(); MetadataExtractor.Formats.Exif.ExifDirectoryBase? exifDirectoryBase = directories.OfType<MetadataExtractor.Formats.Exif.ExifDirectoryBase>().FirstOrDefault();
results.Add(new DateTime(filePath.CreationTicks)); results.Add(new DateTime(filePath.CreationTicks));
results.Add(new DateTime(filePath.LastWriteTicks)); results.Add(new DateTime(filePath.LastWriteTicks));
@ -120,7 +213,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
results.Add(checkDateTime); results.Add(checkDateTime);
else else
{ {
dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime)); dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime));
if (dateTime is not null) if (dateTime is not null)
results.Add(dateTime.Value); results.Add(dateTime.Value);
} }
@ -128,7 +221,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
results.Add(checkDateTime); results.Add(checkDateTime);
else else
{ {
dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized)); dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized));
if (dateTime is not null) if (dateTime is not null)
results.Add(dateTime.Value); results.Add(dateTime.Value);
} }
@ -139,7 +232,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
} }
else else
{ {
dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal)); dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, exifDirectoryBase.GetString(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal));
if (dateTime is not null) if (dateTime is not null)
{ {
result ??= dateTime.Value; result ??= dateTime.Value;
@ -157,7 +250,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
} }
else else
{ {
dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal));
if (dateTime is not null) if (dateTime is not null)
{ {
result ??= dateTime.Value; result ??= dateTime.Value;
@ -175,7 +268,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
} }
else else
{ {
dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated));
if (dateTime is not null) if (dateTime is not null)
{ {
result ??= dateTime.Value; result ??= dateTime.Value;
@ -193,7 +286,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
} }
else else
{ {
dateTime = Property.Models.Stateless.IProperty.GetDateTime(dateTimeFormat, quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); dateTime = Stateless.Methods.IMetadata.GetDateTime(dateTimeFormat, quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated));
if (dateTime is not null) if (dateTime is not null)
{ {
result ??= dateTime.Value; result ??= dateTime.Value;

View File

@ -1,3 +1,4 @@
using System.Globalization;
using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models;
namespace View_by_Distance.Metadata.Models.Stateless.Methods; namespace View_by_Distance.Metadata.Models.Stateless.Methods;
@ -47,4 +48,23 @@ internal static class Base
return result; return result;
} }
#pragma warning restore CA1416
internal static DateTime? GetDateTime(string dateTimeFormat, string? value)
{
DateTime? result;
string alternateFormat = "ddd MMM dd HH:mm:ss yyyy";
if (value is not null && DateTime.TryParse(value, out DateTime dateTime))
result = dateTime;
else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else
result = null;
return result;
}
#pragma warning disable CA1416
} }

View File

@ -484,7 +484,7 @@ internal abstract class Exif
return results.ToArray(); return results.ToArray();
} }
private static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, IReadOnlyList<MetadataExtractor.Directory> directories, System.Drawing.Size? size)
{ {
Shared.Models.ExifDirectory result; Shared.Models.ExifDirectory result;
Shared.Models.AviDirectory[] aviDirectories = GetAviDirectories(directories); Shared.Models.AviDirectory[] aviDirectories = GetAviDirectories(directories);
@ -520,13 +520,13 @@ internal abstract class Exif
internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath) internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath)
{ {
Shared.Models.ExifDirectory result; Shared.Models.ExifDirectory result;
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName);
System.Drawing.Size? size; System.Drawing.Size? size;
try try
{ size = Dimensions.GetDimensions(filePath.FullName); } { size = Dimensions.GetDimensions(filePath.FullName); }
catch (Exception) catch (Exception)
{ size = null; } { size = null; }
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName); result = Covert(filePath, directories, size);
result = Covert(filePath, size, directories);
return result; return result;
} }

View File

@ -64,4 +64,14 @@ public interface IMetadata
// static Dictionary<string, MetadataExtractorDirectory> GetMetadataCollection(FileInfo fileInfo, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions) => // static Dictionary<string, MetadataExtractorDirectory> GetMetadataCollection(FileInfo fileInfo, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions) =>
// Metadata.GetMetadataCollection(fileInfo, subFileTuples, parseExceptions); // Metadata.GetMetadataCollection(fileInfo, subFileTuples, parseExceptions);
string TestStatic_DateTimeFormat() =>
DateTimeFormat();
static string DateTimeFormat() =>
"yyyy:MM:dd HH:mm:ss";
DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) =>
GetDateTime(dateTimeFormat, value);
static DateTime? GetDateTime(string dateTimeFormat, string? value) =>
Base.GetDateTime(dateTimeFormat, value);
} }

View File

@ -35,10 +35,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />

View File

@ -193,6 +193,7 @@ public class MirrorLength
{ {
string message = nameof(MirrorLength); string message = nameof(MirrorLength);
List<(string, string, int)> collectionForMarkDown; List<(string, string, int)> collectionForMarkDown;
logger?.LogDebug("{method}", nameof(MirrorLengthFilesInDirectories));
bool inPlaceSave = _PropertyConfiguration.RootDirectory.First() == _AppSettings.Destination; bool inPlaceSave = _PropertyConfiguration.RootDirectory.First() == _AppSettings.Destination;
if (!inPlaceSave) if (!inPlaceSave)
collectionForMarkDown = []; collectionForMarkDown = [];

View File

@ -35,9 +35,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />

View File

@ -35,10 +35,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" /> <ProjectReference Include="..\Property\Property.csproj" />

View File

@ -37,7 +37,7 @@
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -33,14 +33,14 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -35,7 +35,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -22,19 +22,27 @@ public class A_Property
private readonly Configuration _Configuration; private readonly Configuration _Configuration;
private readonly List<string> _AngleBracketCollection; private readonly List<string> _AngleBracketCollection;
private readonly IPropertyConfiguration _PropertyConfiguration; private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly ReadOnlyDictionary<string, ReadOnlyCollection<string>> _FileGroups; private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups;
public A_Property(int maxDegreeOfParallelism, Configuration propertyConfiguration, string outputExtension, bool reverse, string aResultsFullGroupDirectory) public A_Property(int maxDegreeOfParallelism, Configuration propertyConfiguration, string outputExtension, bool reverse, string aResultsFullGroupDirectory)
{ {
Reverse = reverse; Reverse = reverse;
_ExceptionsDirectories = []; _ExceptionsDirectories = [];
_AngleBracketCollection = [];
_OutputExtension = outputExtension; _OutputExtension = outputExtension;
_ASCIIEncoding = new ASCIIEncoding(); _ASCIIEncoding = new ASCIIEncoding();
_Configuration = propertyConfiguration; _Configuration = propertyConfiguration;
_AngleBracketCollection = [];
_PropertyConfiguration = propertyConfiguration; _PropertyConfiguration = propertyConfiguration;
_MaxDegreeOfParallelism = maxDegreeOfParallelism; _MaxDegreeOfParallelism = maxDegreeOfParallelism;
_FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, aResultsFullGroupDirectory, [propertyConfiguration.ResultSingleton]); _ResultSingletonFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, aResultsFullGroupDirectory, [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 override string ToString() public override string ToString()
@ -43,6 +51,117 @@ public class A_Property
return result; return result;
} }
public void SetAngleBracketCollection(string aResultsFullGroupDirectory, string sourceDirectory, bool anyNullOrNoIsUniqueFileName = true)
{
_AngleBracketCollection.Clear();
if (!anyNullOrNoIsUniqueFileName)
_AngleBracketCollection.AddRange([Path.Combine(aResultsFullGroupDirectory, "<>")]);
else
_AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(_PropertyConfiguration,
sourceDirectory,
aResultsFullGroupDirectory,
contentDescription: string.Empty,
singletonDescription: "Properties for each image",
collectionDescription: string.Empty,
converted: false));
}
[Obsolete("Use ExifDirectory")]
public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, int t, Container.Models.Container[] containers)
{
int total = 0;
string message;
int totalSeconds;
bool anyNullOrNoIsUniqueFileName;
List<Exception> exceptions = [];
Container.Models.Container container;
int containersLength = containers.Length;
const string outputResolution = "Original";
List<Tuple<string, DateTime>> sourceDirectoryChanges = [];
string propertyRoot = IResult.GetResultsGroupDirectory(_PropertyConfiguration, nameof(A_Property));
for (int i = 0; i < containers.Length; i++)
{
container = containers[i];
if (container.Items.Count == 0)
continue;
sourceDirectoryChanges.Clear();
if (container.Items.Count == 0)
continue;
anyNullOrNoIsUniqueFileName = container.Items.Any(l => !l.IsUniqueFileName);
SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName);
totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}";
SavePropertyParallelWork(_MaxDegreeOfParallelism, metadata, exceptions, sourceDirectoryChanges, container, container.Items, message);
if (exceptions.Count == container.Items.Count)
throw new Exception(string.Concat("All in [", container.SourceDirectory, "]failed!"));
if (exceptions.Count != 0)
_ExceptionsDirectories.Add(container.SourceDirectory);
total += container.Items.Count;
}
}
private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName)
{
_AngleBracketCollection.Clear();
string aResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(_PropertyConfiguration,
nameof(A_Property),
string.Empty,
includeResizeGroup: false,
includeModel: false,
includePredictorModel: false);
SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName);
}
[Obsolete("Use ExifDirectory")]
private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, List<Exception> exceptions, List<Tuple<string, DateTime>> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection<Item> items, string message)
{
List<Tuple<string, DateTime>> sourceDirectoryFileTuples = [];
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(items.Count, message, options);
_ = Parallel.For(0, items.Count, parallelOptions, (i, state) =>
{
try
{
long ticks = DateTime.Now.Ticks;
DateTime dateTime = DateTime.Now;
List<Tuple<string, DateTime>> collection;
SavePropertyParallelForWork(metadata, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]);
if (i == 0 || sourceDirectoryChanges.Count != 0)
progressBar.Tick();
lock (sourceDirectoryFileTuples)
collection = (from l in sourceDirectoryFileTuples where l.Item2 > dateTime select l).ToList();
lock (sourceDirectoryChanges)
sourceDirectoryChanges.AddRange(collection);
}
catch (Exception ex)
{
lock (exceptions)
exceptions.Add(ex);
}
});
}
[Obsolete("Use ExifDirectory")]
private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, string sourceDirectory, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<Tuple<string, DateTime>> sourceDirectoryChanges, Item item)
{
Shared.Models.Property property;
List<string> parseExceptions = [];
bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered);
string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}");
if (item.IsValidImageFormatExtension && item.FilePath.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.FilePath.FullName != filteredSourceDirectoryFileExtensionLowered)
File.Move(item.FilePath.FullName, filteredSourceDirectoryFileExtensionLowered);
if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.ExifDirectory is null)
{
property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension);
lock (sourceDirectoryChanges)
sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
lock (item)
item.Update(null);
}
}
[Obsolete("Use ExifDirectory")]
private Shared.Models.Property GetImageProperty(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, Item item, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<string> parseExceptions, bool isIgnoreExtension) 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; Shared.Models.Property? result;
@ -57,8 +176,11 @@ public class A_Property
fileInfo = new(Path.Combine(angleBracket.Replace("<>", _PropertyConfiguration.ResultSingleton), $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json")); fileInfo = new(Path.Combine(angleBracket.Replace("<>", _PropertyConfiguration.ResultSingleton), $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json"));
else else
{ {
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, item.FilePath); string fileName = $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json";
fileInfo = new(Path.Combine(_FileGroups[_PropertyConfiguration.ResultSingleton][directoryIndex], $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json")); CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, item.FilePath);
string directory = _ResultSingletonFileGroups[0][cei.Enum][cei.Index];
fileInfo = new(Path.Combine(directory, fileName));
MoveIf(fileName, cei, directory, fileInfo);
} }
List<DateTime> dateTimes = (from l in sourceDirectoryFileTuples where l is not null && changesFrom.Contains(l.Item1) select l.Item2).ToList(); List<DateTime> dateTimes = (from l in sourceDirectoryFileTuples where l is not null && changesFrom.Contains(l.Item1) select l.Item2).ToList();
if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
@ -84,8 +206,8 @@ public class A_Property
json = File.ReadAllText(fileInfo.FullName); json = File.ReadAllText(fileInfo.FullName);
try try
{ {
if (item.Property is not null) if (item.ExifDirectory is not null)
result = item.Property; result = null;
else else
result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property);
if (result is not null && json.Contains("WrongYear")) if (result is not null && json.Contains("WrongYear"))
@ -164,113 +286,26 @@ public class A_Property
return result; return result;
} }
public void SetAngleBracketCollection(string aResultsFullGroupDirectory, string sourceDirectory, bool anyNullOrNoIsUniqueFileName = true) private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo)
{ {
_AngleBracketCollection.Clear(); string[] segments = directory.Split(cei.Combined);
if (!anyNullOrNoIsUniqueFileName) string? checkDirectory = segments.Length == 1 ?
_AngleBracketCollection.AddRange(new[] { Path.Combine(aResultsFullGroupDirectory, "<>") }); Path.Combine(segments[0], $"{cei.Combined[2..]}") :
else segments.Length == 2 ?
_AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(_PropertyConfiguration, $"{segments[0]}{cei.Combined[2..]}{segments[1]}" :
sourceDirectory, null;
aResultsFullGroupDirectory, if (checkDirectory is not null && Directory.Exists(checkDirectory))
contentDescription: string.Empty,
singletonDescription: "Properties for each image",
collectionDescription: string.Empty,
converted: false));
}
private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, string sourceDirectory, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<Tuple<string, DateTime>> sourceDirectoryChanges, Item item)
{
Shared.Models.Property property;
List<string> parseExceptions = [];
bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered);
string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}");
if (item.IsValidImageFormatExtension && item.FilePath.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.FilePath.FullName != filteredSourceDirectoryFileExtensionLowered)
File.Move(item.FilePath.FullName, filteredSourceDirectoryFileExtensionLowered);
if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.Property is null)
{ {
property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension); string checkFile = Path.Combine(checkDirectory, fileName);
lock (sourceDirectoryChanges) if (File.Exists(checkFile))
sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now)); {
lock (item) File.Move(checkFile, fileInfo.FullName);
item.Update(property); fileInfo.Refresh();
} }
}
private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName)
{
_AngleBracketCollection.Clear();
string aResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(_PropertyConfiguration,
nameof(A_Property),
string.Empty,
includeResizeGroup: false,
includeModel: false,
includePredictorModel: false);
SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName);
}
private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, List<Exception> exceptions, List<Tuple<string, DateTime>> sourceDirectoryChanges, Container container, ReadOnlyCollection<Item> items, string message)
{
List<Tuple<string, DateTime>> sourceDirectoryFileTuples = [];
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(items.Count, message, options);
_ = Parallel.For(0, items.Count, parallelOptions, (i, state) =>
{
try
{
long ticks = DateTime.Now.Ticks;
DateTime dateTime = DateTime.Now;
List<Tuple<string, DateTime>> collection;
SavePropertyParallelForWork(metadata, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]);
if (i == 0 || sourceDirectoryChanges.Count != 0)
progressBar.Tick();
lock (sourceDirectoryFileTuples)
collection = (from l in sourceDirectoryFileTuples where l.Item2 > dateTime select l).ToList();
lock (sourceDirectoryChanges)
sourceDirectoryChanges.AddRange(collection);
}
catch (Exception ex)
{
lock (exceptions)
exceptions.Add(ex);
}
});
}
public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, int t, Container[] containers)
{
int total = 0;
string message;
int totalSeconds;
Container container;
bool anyNullOrNoIsUniqueFileName;
List<Exception> exceptions = [];
int containersLength = containers.Length;
const string outputResolution = "Original";
List<Tuple<string, DateTime>> sourceDirectoryChanges = [];
string propertyRoot = IResult.GetResultsGroupDirectory(_PropertyConfiguration, nameof(A_Property));
for (int i = 0; i < containers.Length; i++)
{
container = containers[i];
if (container.Items.Count == 0)
continue;
sourceDirectoryChanges.Clear();
if (container.Items.Count == 0)
continue;
anyNullOrNoIsUniqueFileName = container.Items.Any(l => !l.IsUniqueFileName);
SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName);
totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}";
SavePropertyParallelWork(_MaxDegreeOfParallelism, metadata, exceptions, sourceDirectoryChanges, container, container.Items, message);
if (exceptions.Count == container.Items.Count)
throw new Exception(string.Concat("All in [", container.SourceDirectory, "]failed!"));
if (exceptions.Count != 0)
_ExceptionsDirectories.Add(container.SourceDirectory);
total += container.Items.Count;
} }
} }
[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) 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; Shared.Models.Property result;

View File

@ -10,49 +10,80 @@ namespace View_by_Distance.Property.Models.Stateless;
public interface IProperty public interface IProperty
{ {
string TestStatic_DateTimeFormat() => const string DateTimeFormat = "yyyy:MM:dd HH:mm:ss";
DateTimeFormat();
static string DateTimeFormat() =>
"yyyy:MM:dd HH:mm:ss";
byte[] TestStatic_GetBytes(string value) => // public
GetBytes(value);
static byte[] GetBytes(string value) =>
Property.GetBytes(value);
DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) =>
GetDateTime(dateTimeFormat, value);
static DateTime? GetDateTime(string dateTimeFormat, string? value) =>
Property.GetDateTime(dateTimeFormat, value);
PropertyItem TestStatic_GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) =>
GetPropertyItem(constructorInfo, id, type, value);
static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) =>
Property.GetPropertyItem(constructorInfo, id, type, value);
(string?, DateTime[], Shared.Models.Property) TestStatic_GetProperty(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) =>
GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding);
static (string?, DateTime[], Shared.Models.Property) GetProperty(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) =>
Property.GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding);
(DateTime?, DateTime[], int?, string?) TestStatic_Get(IPropertyConfiguration propertyConfiguration, bool populateId, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Get(propertyConfiguration, populateId, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Property.Get(populateId, null, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
(DateTime?, DateTime[], int?, string?) TestStatic_Get(bool populateId, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Get(populateId, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
static (DateTime?, DateTime[], int?, string?) Get(bool populateId, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Property.Get(populateId, null, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
(DateTime?, DateTime[], int?, string?) TestStatic_Get(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Get(populateId, metadata, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
static (DateTime?, DateTime[], int?, string?) Get(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => static (DateTime?, DateTime[], int?, string?) Get(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Property.Get(populateId, metadata, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); Property.Get(populateId, metadata, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
(DateTime?, DateTime[], int?, string?) TestStatic_Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => // public
Get(propertyConfiguration, populateId, metadata, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) => Property.Get(populateId, null, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
public static byte[] GetBytes(string value) =>
Property.GetBytes(value);
public static DateTime? GetDateTime(string dateTimeFormat, string? value) =>
Property.GetDateTime(dateTimeFormat, value);
public static double GetStandardDeviation(List<long> values, double average) =>
Property.GetStandardDeviation(values, average);
public static bool Any(Container.Models.Container[] propertyHolderCollections) =>
Property.Any(propertyHolderCollections);
public static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) =>
Property.GetThreeStandardDeviationHigh(minimum, container);
public static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) =>
Property.GetPropertyItem(constructorInfo, id, type, value);
public static (int, List<DateTime>, List<Item>) Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) =>
Property.Get(container, threeStandardDeviationHigh, i);
public static (DateTime?, DateTime[], int?, string?) Get(bool populateId, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Property.Get(populateId, null, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
public static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Property.Get(populateId, metadata, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding); Property.Get(populateId, metadata, FilePath.Get(propertyConfiguration, fileHolder, index: null), isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
public static (string?, DateTime[], Shared.Models.Property) GetProperty(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) =>
Property.GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding);
internal byte[] TestStatic_GetBytes(string value) =>
GetBytes(value);
internal DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) =>
GetDateTime(dateTimeFormat, value);
internal double TestStatic_GetStandardDeviation(List<long> values, double average) =>
GetStandardDeviation(values, average);
internal bool TestStatic_Any(Container.Models.Container[] propertyHolderCollections) =>
Any(propertyHolderCollections);
internal TimeSpan TestStatic_GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container) =>
GetThreeStandardDeviationHigh(minimum, container);
internal PropertyItem TestStatic_GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) =>
GetPropertyItem(constructorInfo, id, type, value);
internal (int, List<DateTime>, List<Item>) TestStatic_Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i) =>
Get(container, threeStandardDeviationHigh, i);
internal (DateTime?, DateTime[], int?, string?) TestStatic_Get(bool populateId, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Get(populateId, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
internal (DateTime?, DateTime[], int?, string?) TestStatic_Get(IPropertyConfiguration propertyConfiguration, bool populateId, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Get(propertyConfiguration, populateId, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
internal (DateTime?, DateTime[], int?, string?) TestStatic_Get(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Get(populateId, metadata, filePath, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
internal (DateTime?, DateTime[], int?, string?) TestStatic_Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
Get(propertyConfiguration, populateId, metadata, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
internal (string?, DateTime[], Shared.Models.Property) TestStatic_GetProperty(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) =>
GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding);
} }

View File

@ -3,44 +3,52 @@ namespace View_by_Distance.Property.Models.Stateless;
public interface IResult public interface IResult
{ {
string TestStatic_GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) => public static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) =>
GetRelativePath(propertyConfiguration, path);
static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) =>
Result.GetRelativePath(propertyConfiguration, path); Result.GetRelativePath(propertyConfiguration, path);
string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) => public static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
GetResultsGroupDirectory(propertyConfiguration, description, create);
static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) =>
Result.GetResultsGroupDirectory(propertyConfiguration, description, create);
string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
GetResultsGroupDirectory(propertyConfiguration, description);
static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
Result.GetResultsGroupDirectory(propertyConfiguration, description, create: true); Result.GetResultsGroupDirectory(propertyConfiguration, description, create: true);
string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) => public static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
GetResultsDateGroupDirectory(propertyConfiguration, description);
static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
Result.GetResultsDateGroupDirectory(propertyConfiguration, description); Result.GetResultsDateGroupDirectory(propertyConfiguration, description);
string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) => public static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) =>
GetResultsDateGroupDirectory(propertyConfiguration, description, jsonGroup); Result.GetResultsGroupDirectory(propertyConfiguration, description, create);
static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) =>
public static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) =>
Result.GetResultsDateGroupDirectory(propertyConfiguration, description, jsonGroup); Result.GetResultsDateGroupDirectory(propertyConfiguration, description, jsonGroup);
List<string> TestStatic_GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) => public static string GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) =>
GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted);
static List<string> GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) =>
Result.GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted);
string TestStatic_GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) =>
GetResultsFullGroupDirectory(propertyConfiguration, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel);
static string GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) =>
Result.GetResultsFullGroupDirectory(propertyConfiguration, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel); Result.GetResultsFullGroupDirectory(propertyConfiguration, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel);
List<string> TestStatic_GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) => public static List<string> GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) =>
GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel, contentDescription, singletonDescription, collectionDescription); Result.GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted);
static List<string> GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) =>
public static List<string> GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) =>
Result.GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel, contentDescription, singletonDescription, collectionDescription); Result.GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel, contentDescription, singletonDescription, collectionDescription);
internal string TestStatic_GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) =>
GetRelativePath(propertyConfiguration, path);
internal string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
GetResultsGroupDirectory(propertyConfiguration, description);
internal string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
GetResultsDateGroupDirectory(propertyConfiguration, description);
internal string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) =>
GetResultsGroupDirectory(propertyConfiguration, description, create);
internal string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) =>
GetResultsDateGroupDirectory(propertyConfiguration, description, jsonGroup);
internal string TestStatic_GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel) =>
GetResultsFullGroupDirectory(propertyConfiguration, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel);
internal List<string> TestStatic_GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) =>
GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted);
internal List<string> TestStatic_GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) =>
GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, description, outputResolution, includeResizeGroup, includeModel, includePredictorModel, contentDescription, singletonDescription, collectionDescription);
} }

View File

@ -14,56 +14,53 @@ namespace View_by_Distance.Property.Models.Stateless;
internal partial class Property internal partial class Property
{ {
[GeneratedRegex(@"\D+")] internal static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container)
private static partial Regex Digit();
private static List<DateTime> GetDateTimes(DateTime dateTimeFromName, DateTime?[] dateTimes)
{ {
List<DateTime> results = [dateTimeFromName]; TimeSpan result;
foreach (DateTime? dateTime in dateTimes) DateTime? minimumDateTime;
List<long> ticksCollection = [];
foreach (Item item in container.Items)
{ {
if (dateTime is null) if (item.ExifDirectory is null)
continue; continue;
results.Add(dateTime.Value); minimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(item.ExifDirectory);
if (minimumDateTime is null)
continue;
ticksCollection.Add(minimumDateTime.Value.Ticks);
} }
return results; long threeStandardDeviationHigh;
long min;
if (ticksCollection.Count == 0)
min = 0;
else
min = ticksCollection.Min();
if (ticksCollection.Count < minimum)
threeStandardDeviationHigh = long.MaxValue;
else
threeStandardDeviationHigh = GetThreeStandardDeviationHigh(ref ticksCollection, min);
result = new TimeSpan(threeStandardDeviationHigh - min);
return result;
} }
private static List<DateTime> GetDateTimes(DateTime?[] dateTimes, DateTime?[] metadataDateTimes) private static long GetThreeStandardDeviationHigh(ref List<long> ticksCollection, long min)
{ {
List<DateTime> results = []; long result;
foreach (DateTime? dateTime in metadataDateTimes) ticksCollection = (from l in ticksCollection select l - min).ToList();
{ double sum = ticksCollection.Sum();
if (dateTime is null || results.Contains(dateTime.Value)) double average = sum / ticksCollection.Count;
continue; double standardDeviation = GetStandardDeviation(ticksCollection, average);
results.Add(dateTime.Value); result = (long)Math.Ceiling(average + min + (standardDeviation * 3));
} return result;
foreach (DateTime? dateTime in dateTimes)
{
if (dateTime is null || results.Contains(dateTime.Value))
continue;
results.Add(dateTime.Value);
}
return results;
} }
private static List<DateTime> GetDateTimes(FilePath filePath, DateTime?[] dateTimes) internal static double GetStandardDeviation(List<long> values, double average)
{ {
List<DateTime> results = []; double result = 0;
string[] digits = Digit().Split(filePath.FullName); if (values.Count == 0)
foreach (string digit in digits) throw new Exception("Collection must have at least one value!");
{ double sum = values.Sum(l => (l - average) * (l - average));
if (digit.Length != 4 || digit[..2] is not "19" and not "20" || !int.TryParse(digit, out int year)) result = Math.Sqrt(sum / values.Count);
continue; return result;
results.Add(new(year, 1, 1));
}
foreach (DateTime? dateTime in dateTimes)
{
if (dateTime is null)
continue;
results.Add(dateTime.Value);
}
return results;
} }
internal static byte[] GetBytes(string value) internal static byte[] GetBytes(string value)
@ -111,12 +108,12 @@ internal partial class Property
format = dateFormat[1]; format = dateFormat[1];
length = dateFormat[0].Length + dateFormat[1].Length; length = dateFormat[0].Length + dateFormat[1].Length;
for (int i = dateFormat[0].Length; i < length; i++) for (int i = dateFormat[0].Length; i < length; i++)
_ = value.Append(filePath.NameWithoutExtension[i]); _ = value.Append(filePath.FileNameFirstSegment[i]);
if (value.Length != format.Length) if (value.Length != format.Length)
continue; continue;
if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime)) 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; result = checkDateTime;
else else
result = new DateTime(ticks); result = new DateTime(ticks);
@ -126,22 +123,40 @@ internal partial class Property
return result; return result;
} }
private static List<DateTime> GetDateTimes(DateTime?[] metadataDateTimes) internal static bool Any(Container.Models.Container[] containers)
{ {
List<DateTime> results = []; bool result = false;
foreach (DateTime? dateTime in metadataDateTimes) foreach (Container.Models.Container container in containers)
{ {
if (dateTime is null || results.Contains(dateTime.Value)) if (container.Items.Count == 0)
continue; continue;
results.Add(dateTime.Value); if ((from l in container.Items where l.Any() select true).Any())
{
result = true;
break;
}
} }
return results; return result;
} }
#pragma warning disable CA1416 internal static DateTime? GetDateTime(string dateTimeFormat, string? value)
{
DateTime? result;
string alternateFormat = "ddd MMM dd HH:mm:ss yyyy";
if (value is not null && DateTime.TryParse(value, out DateTime dateTime))
result = dateTime;
else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else
result = null;
return result;
}
internal static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value) internal static PropertyItem GetPropertyItem(ConstructorInfo constructorInfo, int id, short type, string value)
{ {
#pragma warning disable CA1416
PropertyItem result = (PropertyItem)constructorInfo.Invoke(null); PropertyItem result = (PropertyItem)constructorInfo.Invoke(null);
int length; int length;
byte[] bytes; byte[] bytes;
@ -161,27 +176,65 @@ internal partial class Property
result.Len = length; result.Len = length;
result.Type = type; result.Type = type;
result.Value = bytes; result.Value = bytes;
return result;
}
#pragma warning restore CA1416 #pragma warning restore CA1416
internal static DateTime? GetDateTime(string dateTimeFormat, string? value)
{
DateTime? result;
string alternateFormat = "ddd MMM dd HH:mm:ss yyyy";
if (value is not null && DateTime.TryParse(value, out DateTime dateTime))
result = dateTime;
else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else
result = null;
return result; return result;
} }
#pragma warning disable CA1416 internal static (int, List<DateTime>, List<Item>) Get(Container.Models.Container container, TimeSpan threeStandardDeviationHigh, int i)
{
List<Item> results = [];
int j = i;
long? ticks;
TimeSpan timeSpan;
Item item;
DateTime? minimumDateTime;
Item nextItem;
DateTime? nextMinimumDateTime;
List<DateTime> dateTimes = [];
for (; j < container.Items.Count; j++)
{
ticks = null;
item = container.Items[j];
if (item.ExifDirectory is null)
continue;
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.ExifDirectory is null)
continue;
nextMinimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(nextItem.ExifDirectory);
if (nextMinimumDateTime is null)
continue;
ticks = nextMinimumDateTime.Value.Ticks;
break;
}
results.Add(item);
dateTimes.Add(minimumDateTime.Value);
if (ticks.HasValue)
{
timeSpan = new(ticks.Value - minimumDateTime.Value.Ticks);
if (timeSpan > threeStandardDeviationHigh)
break;
}
}
return new(j, dateTimes, results);
}
internal static (DateTime?, DateTime[], int?, string?) Get(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding)
{
int? id = null;
string? message;
DateTime[] dateTimes;
Shared.Models.Property? property = null;
if (isIgnoreExtension || !isValidImageFormatExtension)
(message, dateTimes, property) = (null, [], null);
else
(message, dateTimes, property) = GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding);
return new(property?.DateTimeOriginal, dateTimes, property?.Id, message);
}
internal static (string?, DateTime[], Shared.Models.Property) GetProperty(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding) internal static (string?, DateTime[], Shared.Models.Property) GetProperty(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, Shared.Models.Property? property, bool isIgnoreExtension, bool isValidImageFormatExtension, int? id, ASCIIEncoding asciiEncoding)
{ {
@ -225,6 +278,7 @@ internal partial class Property
{ {
try try
{ {
#pragma warning disable CA1416
using Image image = Image.FromFile(filePath.FullName); using Image image = Image.FromFile(filePath.FullName);
width = image.Width; width = image.Width;
height = image.Height; height = image.Height;
@ -240,7 +294,7 @@ internal partial class Property
bitmap.UnlockBits(bitmapData); bitmap.UnlockBits(bitmapData);
id ??= Shared.Models.Stateless.Methods.IId.GetDeterministicHashCode(bytes); id ??= Shared.Models.Stateless.Methods.IId.GetDeterministicHashCode(bytes);
} }
dateTimeFormat = IProperty.DateTimeFormat(); dateTimeFormat = IProperty.DateTimeFormat;
if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime)) if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime))
{ {
propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime); propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime);
@ -318,6 +372,7 @@ internal partial class Property
keywords = Encoding.Unicode.GetString(propertyItem.Value).Trim('\0', ' ').Split(';'); keywords = Encoding.Unicode.GetString(propertyItem.Value).Trim('\0', ' ').Split(';');
} }
} }
#pragma warning restore CA1416
message = null; message = null;
dateTimes = [new(filePath.LastWriteTicks), new(filePath.CreationTicks), dateTime, dateTimeDigitized, dateTimeOriginal, gpsDateStamp]; dateTimes = [new(filePath.LastWriteTicks), new(filePath.CreationTicks), dateTime, dateTimeDigitized, dateTimeOriginal, gpsDateStamp];
} }
@ -351,19 +406,68 @@ internal partial class Property
return (message, dateTimesByLogic.ToArray(), result); return (message, dateTimesByLogic.ToArray(), result);
} }
#pragma warning restore CA1416 private static List<DateTime> GetDateTimes(DateTime?[] metadataDateTimes)
internal static (DateTime?, DateTime[], int?, string?) Get(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding)
{ {
int? id = null; List<DateTime> results = [];
string? message; foreach (DateTime? dateTime in metadataDateTimes)
DateTime[] dateTimes; {
Shared.Models.Property? property = null; if (dateTime is null || results.Contains(dateTime.Value))
if (isIgnoreExtension || !isValidImageFormatExtension) continue;
(message, dateTimes, property) = (null, [], null); results.Add(dateTime.Value);
else }
(message, dateTimes, property) = GetProperty(populateId, metadata, filePath, property, isIgnoreExtension, isValidImageFormatExtension, id, asciiEncoding); return results;
return new(property?.DateTimeOriginal, dateTimes, property?.Id, message);
} }
private static List<DateTime> GetDateTimes(FilePath filePath, DateTime?[] dateTimes)
{
List<DateTime> results = [];
string[] digits = Digit().Split(filePath.FullName);
foreach (string digit in digits)
{
if (digit.Length != 4 || digit[..2] is not "19" and not "20" || !int.TryParse(digit, out int year))
continue;
results.Add(new(year, 1, 1));
}
foreach (DateTime? dateTime in dateTimes)
{
if (dateTime is null)
continue;
results.Add(dateTime.Value);
}
return results;
}
private static List<DateTime> GetDateTimes(DateTime dateTimeFromName, DateTime?[] dateTimes)
{
List<DateTime> results = [dateTimeFromName];
foreach (DateTime? dateTime in dateTimes)
{
if (dateTime is null)
continue;
results.Add(dateTime.Value);
}
return results;
}
private static List<DateTime> GetDateTimes(DateTime?[] dateTimes, DateTime?[] metadataDateTimes)
{
List<DateTime> results = [];
foreach (DateTime? dateTime in metadataDateTimes)
{
if (dateTime is null || results.Contains(dateTime.Value))
continue;
results.Add(dateTime.Value);
}
foreach (DateTime? dateTime in dateTimes)
{
if (dateTime is null || results.Contains(dateTime.Value))
continue;
results.Add(dateTime.Value);
}
return results;
}
[GeneratedRegex(@"\D+")]
private static partial Regex Digit();
} }

View File

@ -5,13 +5,8 @@ namespace View_by_Distance.Property.Models.Stateless;
internal class Result internal class Result
{ {
internal static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) internal static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) =>
{ Shared.Models.Stateless.Methods.IPath.GetRelativePath(path, propertyConfiguration.RootDirectory.Length);
string result = Path.Combine(GetResultsDateGroupDirectory(propertyConfiguration, description), jsonGroup);
if (!Directory.Exists(result))
_ = Directory.CreateDirectory(result);
return result;
}
internal static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) internal static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description)
{ {
@ -21,12 +16,66 @@ internal class Result
return result; return result;
} }
internal static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) internal static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create)
{ {
string result = Shared.Models.Stateless.Methods.IPath.GetRelativePath(path, propertyConfiguration.RootDirectory.Length); string result = Path.Combine($"{propertyConfiguration.RootDirectory}-Results", description.Replace('_', ')'));
if (create && !Directory.Exists(result))
_ = Directory.CreateDirectory(result);
return result; return result;
} }
internal static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup)
{
string result = Path.Combine(GetResultsDateGroupDirectory(propertyConfiguration, description), jsonGroup);
if (!Directory.Exists(result))
_ = Directory.CreateDirectory(result);
return result;
}
internal static string GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel)
{
string result = GetResultsDateGroupDirectory(propertyConfiguration, description);
if (includeResizeGroup)
result = Path.Combine(result, outputResolution);
if (includeModel && includePredictorModel)
{
string modelName;
string predictorModelName;
if (propertyConfiguration.ModelName is null)
modelName = Model.Hog.ToString();
else
modelName = propertyConfiguration.ModelName;
if (propertyConfiguration.PredictorModelName is null)
predictorModelName = PredictorModel.Large.ToString();
else
predictorModelName = propertyConfiguration.PredictorModelName;
string dateGroupDirectory = string.Concat(outputResolution.Replace(" ", string.Empty), "-", modelName, "-", predictorModelName, "-", propertyConfiguration.NumberOfJitters, "-", propertyConfiguration.NumberOfTimesToUpsample);
result = Path.Combine(result, dateGroupDirectory);
}
else if (includeModel)
throw new Exception();
else if (includePredictorModel)
throw new Exception();
if (!Directory.Exists(result))
_ = Directory.CreateDirectory(result);
return result;
}
internal static List<string> GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted)
{
List<string> results = [];
string sourceDirectorySegment = GetRelativePath(propertyConfiguration, sourceDirectory);
string result = string.Concat(Path.Combine(dateGroupDirectory, "<>"), sourceDirectorySegment);
if (!string.IsNullOrEmpty(contentDescription))
CheckContent(propertyConfiguration, dateGroupDirectory, contentDescription, result);
if (!string.IsNullOrEmpty(singletonDescription))
CheckSingleton(propertyConfiguration, dateGroupDirectory, singletonDescription, converted, result);
if (!string.IsNullOrEmpty(collectionDescription))
CheckCollection(propertyConfiguration, dateGroupDirectory, collectionDescription, converted, result);
results.Add(result);
return results;
}
private static void CheckContent(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string dateGroupDirectory, string contentDescription, string result) private static void CheckContent(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string dateGroupDirectory, string contentDescription, string result)
{ {
string checkDirectory; string checkDirectory;
@ -74,58 +123,6 @@ internal class Result
_ = Directory.CreateDirectory(checkDirectory); _ = Directory.CreateDirectory(checkDirectory);
} }
internal static string GetResultsFullGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel)
{
string result = GetResultsDateGroupDirectory(propertyConfiguration, description);
if (includeResizeGroup)
result = Path.Combine(result, outputResolution);
if (includeModel && includePredictorModel)
{
string modelName;
string predictorModelName;
if (propertyConfiguration.ModelName is null)
modelName = Model.Hog.ToString();
else
modelName = propertyConfiguration.ModelName;
if (propertyConfiguration.PredictorModelName is null)
predictorModelName = PredictorModel.Large.ToString();
else
predictorModelName = propertyConfiguration.PredictorModelName;
string dateGroupDirectory = string.Concat(outputResolution.Replace(" ", string.Empty), "-", modelName, "-", predictorModelName, "-", propertyConfiguration.NumberOfJitters, "-", propertyConfiguration.NumberOfTimesToUpsample);
result = Path.Combine(result, dateGroupDirectory);
}
else if (includeModel)
throw new Exception();
else if (includePredictorModel)
throw new Exception();
if (!Directory.Exists(result))
_ = Directory.CreateDirectory(result);
return result;
}
internal static List<string> GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted)
{
List<string> results = [];
string sourceDirectorySegment = GetRelativePath(propertyConfiguration, sourceDirectory);
string result = string.Concat(Path.Combine(dateGroupDirectory, "<>"), sourceDirectorySegment);
if (!string.IsNullOrEmpty(contentDescription))
CheckContent(propertyConfiguration, dateGroupDirectory, contentDescription, result);
if (!string.IsNullOrEmpty(singletonDescription))
CheckSingleton(propertyConfiguration, dateGroupDirectory, singletonDescription, converted, result);
if (!string.IsNullOrEmpty(collectionDescription))
CheckCollection(propertyConfiguration, dateGroupDirectory, collectionDescription, converted, result);
results.Add(result);
return results;
}
internal static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create)
{
string result = Path.Combine($"{propertyConfiguration.RootDirectory}-Results", description.Replace('_', ')'));
if (create && !Directory.Exists(result))
_ = Directory.CreateDirectory(result);
return result;
}
internal static List<string> GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription) internal static List<string> GetDirectoryInfoCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string sourceDirectory, string description, string outputResolution, bool includeResizeGroup, bool includeModel, bool includePredictorModel, string contentDescription, string singletonDescription, string collectionDescription)
{ {
List<string> results; List<string> results;

View File

@ -40,12 +40,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Container\Container.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -251,7 +251,7 @@ public class Rename
catch (Exception) { continue; } catch (Exception) { continue; }
CommandTask<CommandResult> result = Cli.Wrap("ffmpeg.exe") CommandTask<CommandResult> result = Cli.Wrap("ffmpeg.exe")
// .WithArguments(new[] { "-ss", "00:00:00", "-t", "00:00:00", "-i", files[i], "-qscale:v", "2", "-r", "0.01", $"{fileHolder.Name}-%4d.jpg" }) // .WithArguments(new[] { "-ss", "00:00:00", "-t", "00:00:00", "-i", files[i], "-qscale:v", "2", "-r", "0.01", $"{fileHolder.Name}-%4d.jpg" })
.WithArguments(new[] { "-i", files[i], "-vframes", "1", $"{fileHolder.Name}-%4d.jpg" }) .WithArguments(["-i", files[i], "-vframes", "1", $"{fileHolder.Name}-%4d.jpg"])
.WithWorkingDirectory(fileHolder.DirectoryFullPath) .WithWorkingDirectory(fileHolder.DirectoryFullPath)
.ExecuteAsync(); .ExecuteAsync();
result.Task.Wait(); result.Task.Wait();

View File

@ -34,11 +34,11 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.7.0" /> <PackageReference Include="CliWrap" Version="3.8.1" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Metadata\Metadata.csproj" /> <ProjectReference Include="..\Metadata\Metadata.csproj" />

View File

@ -40,11 +40,10 @@ public class C_Resize
private readonly bool _ForceResizeLastWriteTimeToCreationTime; private readonly bool _ForceResizeLastWriteTimeToCreationTime;
private readonly IPropertyConfiguration _PropertyConfiguration; private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions; private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions;
private readonly Dictionary<string, ReadOnlyCollection<string>> _FileGroups; private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups;
public C_Resize(IPropertyConfiguration propertyConfiguration, bool forceResizeLastWriteTimeToCreationTime, bool overrideForResizeImages, bool propertiesChangedForResize, string[] validResolutions, ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) public C_Resize(IPropertyConfiguration propertyConfiguration, bool forceResizeLastWriteTimeToCreationTime, bool overrideForResizeImages, bool propertiesChangedForResize, string[] validResolutions, ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension)
{ {
_FileGroups = [];
_Original = "Original"; _Original = "Original";
_TempResolutionWidth = 3; _TempResolutionWidth = 3;
_TempResolutionHeight = 4; _TempResolutionHeight = 4;
@ -61,56 +60,31 @@ public class C_Resize
_OverrideForResizeImages = overrideForResizeImages; _OverrideForResizeImages = overrideForResizeImages;
_PropertiesChangedForResize = propertiesChangedForResize; _PropertiesChangedForResize = propertiesChangedForResize;
_ForceResizeLastWriteTimeToCreationTime = forceResizeLastWriteTimeToCreationTime; _ForceResizeLastWriteTimeToCreationTime = forceResizeLastWriteTimeToCreationTime;
_ResultSingletonFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
_WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception(); ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
_ConstructorInfo = constructorInfo; _ConstructorInfo = constructorInfo;
} }
public override string ToString() public static byte[] GetBitmapData(Bitmap bitmap)
{ {
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); byte[] results;
return result;
}
public void Update(string cResultsFullGroupDirectory)
{
_FileGroups.Clear();
ReadOnlyDictionary<string, ReadOnlyCollection<string>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, [_PropertyConfiguration.ResultSingleton]);
foreach (KeyValuePair<string, ReadOnlyCollection<string>> keyValuePair in keyValuePairs)
_FileGroups.Add(keyValuePair.Key, keyValuePair.Value);
}
public void SetAngleBracketCollection(string cResultsFullGroupDirectory, string sourceDirectory)
{
AngleBracketCollection.Clear();
AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(_PropertyConfiguration,
sourceDirectory,
cResultsFullGroupDirectory,
contentDescription: "Resized image",
singletonDescription: "Resize dimensions for each resolution",
collectionDescription: string.Empty,
converted: true));
}
#pragma warning disable CA1416 #pragma warning disable CA1416
Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height);
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetJpegLowQuality() BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
{ IntPtr intPtr = bitmapData.Scan0;
(ImageCodecInfo, EncoderParameters, string) result; int length = bitmapData.Stride * bitmap.Height;
ImageFormat imageFormat = ImageFormat.Jpeg; results = new byte[length];
ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders(); Marshal.Copy(intPtr, results, 0, length);
ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First(); bitmap.UnlockBits(bitmapData);
EncoderParameters encoderParameters = new(1); #pragma warning restore CA1416
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); return results;
if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension))
throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension));
result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]);
return result;
} }
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetPngLowQuality() public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetPngLowQuality()
{ {
(ImageCodecInfo, EncoderParameters, string) result; (ImageCodecInfo, EncoderParameters, string) result;
#pragma warning disable CA1416
ImageFormat imageFormat = ImageFormat.Png; ImageFormat imageFormat = ImageFormat.Png;
ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders(); ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First(); ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First();
@ -119,12 +93,30 @@ public class C_Resize
if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension))
throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension));
result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]); result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]);
#pragma warning restore CA1416
return result;
}
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetJpegLowQuality()
{
(ImageCodecInfo, EncoderParameters, string) result;
#pragma warning disable CA1416
ImageFormat imageFormat = ImageFormat.Jpeg;
ImageCodecInfo[] imageCodecInfoCollection = ImageCodecInfo.GetImageEncoders();
ImageCodecInfo imageCodecInfo = (from l in imageCodecInfoCollection where l.FormatID == imageFormat.Guid select l).First();
EncoderParameters encoderParameters = new(1);
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L);
if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension))
throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension));
result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]);
#pragma warning restore CA1416
return result; return result;
} }
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetTuple(string outputExtension, int outputQuality) public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetTuple(string outputExtension, int outputQuality)
{ {
(ImageCodecInfo, EncoderParameters, string) result; (ImageCodecInfo, EncoderParameters, string) result;
#pragma warning disable CA1416
ImageFormat imageFormat = outputExtension switch ImageFormat imageFormat = outputExtension switch
{ {
".gif" => ImageFormat.Gif, ".gif" => ImageFormat.Gif,
@ -146,55 +138,274 @@ public class C_Resize
if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension))
throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension));
result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]); result = new(imageCodecInfo, encoderParameters, imageCodecInfo.FilenameExtension.Split(';')[0].ToLower()[1..]);
#pragma warning restore CA1416
return result; return result;
} }
public static byte[] GetBitmapData(Bitmap bitmap) public FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, Item item, bool outputResolutionHasNumber, int id) =>
GetResizedFileHolder(cResultsFullGroupDirectory, item.FilePath, outputResolutionHasNumber, $"{id}{item.FilePath.ExtensionLowered}");
private FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, FilePath filePath, bool outputResolutionHasNumber, string fileName)
{ {
byte[] results; FileHolder result;
Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height); if (outputResolutionHasNumber)
BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat); result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(AngleBracketCollection[0].Replace("<>", _PropertyConfiguration.ResultContent), fileName));
IntPtr intPtr = bitmapData.Scan0; else
int length = bitmapData.Stride * bitmap.Height; {
results = new byte[length]; CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
Marshal.Copy(intPtr, results, 0, length); result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(cResultsFullGroupDirectory, _PropertyConfiguration.ResultContent, cei.Combined, fileName));
bitmap.UnlockBits(bitmapData); }
return result;
}
public override string ToString()
{
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
return result;
}
public void Update(string cResultsFullGroupDirectory)
{
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, [_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 void SetAngleBracketCollection(string cResultsFullGroupDirectory, string sourceDirectory)
{
AngleBracketCollection.Clear();
AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(_PropertyConfiguration,
sourceDirectory,
cResultsFullGroupDirectory,
contentDescription: "Resized image",
singletonDescription: "Resize dimensions for each resolution",
collectionDescription: string.Empty,
converted: true));
}
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, ExifDirectory exifDirectory, MappingFromItem mappingFromItem)
{
Dictionary<string, int[]>? results;
string json;
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
string fileName = $"{mappingFromItem.FilePath.NameWithoutExtension}{mappingFromItem.FilePath.ExtensionLowered}.json";
string directory = _ResultSingletonFileGroups[0][cei.Enum][cei.Index];
FileInfo fileInfo = new(Path.Combine(directory, fileName));
MoveIf(fileName, cei, directory, fileInfo);
if (_ForceResizeLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
fileInfo.Refresh();
}
if (_ForceResizeLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
}
if (_PropertiesChangedForResize)
results = null;
else if (!fileInfo.Exists)
results = null;
else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old"))
throw new ArgumentException("must be a *.json file");
else if (dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime)
results = null;
else
{
json = File.ReadAllText(fileInfo.FullName);
try
{
results = JsonSerializer.Deserialize<Dictionary<string, int[]>>(json);
if (results is null)
throw new Exception();
subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), fileInfo.LastWriteTime));
}
catch (Exception)
{
results = null;
parseExceptions.Add(nameof(C_Resize));
}
}
if (results is null)
{
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();
if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime))
{
if (!_ForceResizeLastWriteTimeToCreationTime)
subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), DateTime.Now));
else
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), fileInfo.CreationTime));
}
}
}
return results; return results;
} }
private void CopyPropertyItems(byte[] bytes, PropertyItem[] propertyItems, Bitmap bitmap) private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo)
{ {
bool hasId = false; string[] segments = directory.Split(cei.Combined);
int id = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized; string? checkDirectory = segments.Length == 1 ?
int imageWidth = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageWidth; Path.Combine(segments[0], $"{cei.Combined[2..]}") :
int imageHeight = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageHeight; segments.Length == 2 ?
int orientation = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation; $"{segments[0]}{cei.Combined[2..]}{segments[1]}" :
foreach (PropertyItem propertyItem in propertyItems) null;
if (checkDirectory is not null && Directory.Exists(checkDirectory))
{ {
if (propertyItem.Id == id) string checkFile = Path.Combine(checkDirectory, fileName);
hasId = true; if (File.Exists(checkFile))
else if (propertyItem.Id == imageWidth) {
continue; File.Move(checkFile, fileInfo.FullName);
else if (propertyItem.Id == imageHeight) fileInfo.Refresh();
continue; }
else if (propertyItem.Id == orientation)
continue;
bitmap.SetPropertyItem(propertyItem);
} }
if (!hasId) }
private Dictionary<string, int[]> GetImageResizes(ExifDirectory exifDirectory)
{
Dictionary<string, int[]> results = [];
int[] desired;
int checkWidth;
int checkHeight;
int desiredWidth;
int desiredHeight;
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 = exifDirectory.Width.Value;
checkHeight = exifDirectory.Height.Value;
if (!_ValidResolutions.Contains(_Original))
results.Add(_Original, [checkWidth, checkHeight, orientation.Value]);
foreach (string validResolution in _ValidResolutions)
{ {
PropertyItem propertyItem = (PropertyItem)_ConstructorInfo.Invoke(null); if (validResolution == _Original)
propertyItem.Id = id; {
propertyItem.Len = bytes.Length; desiredWidth = checkWidth;
propertyItem.Type = 2; desiredHeight = checkHeight;
propertyItem.Value = bytes; }
bitmap.SetPropertyItem(propertyItem); else
{
desired = GetCollection(validResolution);
desiredWidth = desired[0];
desiredHeight = desired[1];
}
if (checkWidth <= desiredWidth && checkHeight <= desiredHeight)
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.Value]);
else
results.Add(validResolution, [Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight, orientation.Value]);
}
else
{
if (checkWidth * desiredHeight <= desiredWidth * checkHeight)
results.Add(validResolution, [desiredWidth, desiredHeight, orientation.Value, desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth)]);
else
results.Add(validResolution, [desiredWidth, desiredHeight, orientation.Value, Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight]);
}
}
} }
return results;
}
private static int[] GetCollection(string outputResolution)
{
List<int> results = [];
string[] segments = outputResolution.Split('x');
results.Add(int.Parse(segments[0]));
results.Add(int.Parse(segments[1]));
return results.ToArray();
}
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));
#pragma warning disable CA1854
if (!outputResolutionToResize.ContainsKey(_Original))
throw new Exception();
if (!outputResolutionToResize.ContainsKey(outputResolution))
throw new Exception();
#pragma warning restore CA1854
FileInfo fileInfo = new(mappingFromItem.ResizedFileHolder.FullName);
bool check = false;
int[] resize = outputResolutionToResize[outputResolution];
int outputResolutionWidth = resize[_OutputResolutionWidthIndex];
int outputResolutionHeight = resize[_OutputResolutionHeightIndex];
int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex];
int[] originalCollection = outputResolutionToResize[_Original];
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
if (_OverrideForResizeImages)
check = true;
else if (!fileInfo.Exists)
check = true;
if (outputResolutionWidth == originalCollection[_OutputResolutionWidthIndex] && outputResolutionHeight == originalCollection[_OutputResolutionHeightIndex] && outputResolutionOrientation == originalCollection[_OutputResolutionOrientationIndex])
{
if (!check && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.CreationTime.AddDays(1))
check = true;
if (check)
{
// if (fileInfo.Exists)
// File.Delete(fileInfo.FullName);
// File.Copy(mappingFromItem.FilePath.FullName, fileInfo.FullName);
// item.SetResizedFileHolder(_FileNameExtension, Shared.Models.Stateless.Methods.IFileHolder.Refresh(mappingFromItem.ResizedFileHolder));
// subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), DateTime.Now));
}
}
else
{
if (!check && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime)
check = true;
if (check)
{
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(ExifDirectory exifDirectory, MappingFromItem mappingFromItem, int[] resize)
{
string dateTimeFormat = IProperty.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();
if (resize.Length == 3)
SaveResizedSubfile3(mappingFromItem, resize, bytes);
else if (resize.Length == 5)
SaveResizedSubfile5(mappingFromItem, resize, bytes);
else
throw new Exception();
} }
private void SaveResizedSubfile3(MappingFromItem mappingFromItem, int[] resize, byte[] bytes) private void SaveResizedSubfile3(MappingFromItem mappingFromItem, int[] resize, byte[] bytes)
{ {
Bitmap bitmap; Bitmap bitmap;
#pragma warning disable CA1416
int outputResolutionWidth = resize[_OutputResolutionWidthIndex]; int outputResolutionWidth = resize[_OutputResolutionWidthIndex];
using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false); using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false);
int outputResolutionHeight = resize[_OutputResolutionHeightIndex]; int outputResolutionHeight = resize[_OutputResolutionHeightIndex];
@ -234,11 +445,45 @@ public class C_Resize
CopyPropertyItems(bytes, propertyItems, bitmap); CopyPropertyItems(bytes, propertyItems, bitmap);
bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters); bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
bitmap.Dispose(); bitmap.Dispose();
#pragma warning restore CA1416
}
private void CopyPropertyItems(byte[] bytes, PropertyItem[] propertyItems, Bitmap bitmap)
{
bool hasId = false;
int id = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized;
int imageWidth = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageWidth;
int imageHeight = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageHeight;
int orientation = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation;
#pragma warning disable CA1416
foreach (PropertyItem propertyItem in propertyItems)
{
if (propertyItem.Id == id)
hasId = true;
else if (propertyItem.Id == imageWidth)
continue;
else if (propertyItem.Id == imageHeight)
continue;
else if (propertyItem.Id == orientation)
continue;
bitmap.SetPropertyItem(propertyItem);
}
if (!hasId)
{
PropertyItem propertyItem = (PropertyItem)_ConstructorInfo.Invoke(null);
propertyItem.Id = id;
propertyItem.Len = bytes.Length;
propertyItem.Type = 2;
propertyItem.Value = bytes;
bitmap.SetPropertyItem(propertyItem);
}
#pragma warning restore CA1416
} }
private void SaveResizedSubfile5(MappingFromItem mappingFromItem, int[] resize, byte[] bytes) private void SaveResizedSubfile5(MappingFromItem mappingFromItem, int[] resize, byte[] bytes)
{ {
Bitmap bitmap; Bitmap bitmap;
#pragma warning disable CA1416
using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false); using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false);
PropertyItem[] propertyItems = temp.PropertyItems; PropertyItem[] propertyItems = temp.PropertyItems;
int tempResolutionWidth = resize[_TempResolutionWidth]; int tempResolutionWidth = resize[_TempResolutionWidth];
@ -291,215 +536,7 @@ public class C_Resize
bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters); bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
} }
bitmap.Dispose(); bitmap.Dispose();
}
#pragma warning restore CA1416 #pragma warning restore CA1416
private void SaveResizedSubfile(Shared.Models.Property property, 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);
byte[] bytes = _ASCIIEncoding.GetBytes(dateTimeValue);
if (_ASCIIEncoding.GetString(bytes, 0, bytes.Length) != dateTimeValue)
throw new Exception();
if (resize.Length == 3)
SaveResizedSubfile3(mappingFromItem, resize, bytes);
else if (resize.Length == 5)
SaveResizedSubfile5(mappingFromItem, resize, bytes);
else
throw new Exception();
}
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)
{
if (mappingFromItem.ResizedFileHolder is null)
throw new NullReferenceException(nameof(mappingFromItem.ResizedFileHolder));
#pragma warning disable CA1854
if (!outputResolutionToResize.ContainsKey(_Original))
throw new Exception();
if (!outputResolutionToResize.ContainsKey(outputResolution))
throw new Exception();
#pragma warning restore CA1854
FileInfo fileInfo = new(mappingFromItem.ResizedFileHolder.FullName);
bool check = false;
int[] resize = outputResolutionToResize[outputResolution];
int outputResolutionWidth = resize[_OutputResolutionWidthIndex];
int outputResolutionHeight = resize[_OutputResolutionHeightIndex];
int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex];
int[] originalCollection = outputResolutionToResize[_Original];
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata), nameof(C_Resize)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
if (_OverrideForResizeImages)
check = true;
else if (!fileInfo.Exists)
check = true;
if (outputResolutionWidth == originalCollection[_OutputResolutionWidthIndex] && outputResolutionHeight == originalCollection[_OutputResolutionHeightIndex] && outputResolutionOrientation == originalCollection[_OutputResolutionOrientationIndex])
{
if (!check && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.CreationTime.AddDays(1))
check = true;
if (check)
{
// if (fileInfo.Exists)
// File.Delete(fileInfo.FullName);
// File.Copy(mappingFromItem.FilePath.FullName, fileInfo.FullName);
// item.SetResizedFileHolder(_FileNameExtension, Shared.Models.Stateless.Methods.IFileHolder.Refresh(mappingFromItem.ResizedFileHolder));
// subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), DateTime.Now));
}
}
else
{
if (!check && dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime)
check = true;
if (check)
{
SaveResizedSubfile(property, 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 static int[] GetCollection(string outputResolution)
{
List<int> results = [];
string[] segments = outputResolution.Split('x');
results.Add(int.Parse(segments[0]));
results.Add(int.Parse(segments[1]));
return results.ToArray();
}
private Dictionary<string, int[]> GetImageResizes(Shared.Models.Property property)
{
Dictionary<string, int[]> results = [];
int[] desired;
int checkWidth;
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)
throw new NotSupportedException();
checkWidth = property.Width.Value;
checkHeight = property.Height.Value;
if (!_ValidResolutions.Contains(_Original))
results.Add(_Original, [checkWidth, checkHeight, orientation]);
foreach (string validResolution in _ValidResolutions)
{
if (validResolution == _Original)
{
desiredWidth = checkWidth;
desiredHeight = checkHeight;
}
else
{
desired = GetCollection(validResolution);
desiredWidth = desired[0];
desiredHeight = desired[1];
}
if (checkWidth <= desiredWidth && checkHeight <= desiredHeight)
results.Add(validResolution, [checkWidth, checkHeight, orientation]);
else
{
if (desiredWidth != desiredHeight)
{
if (checkWidth * desiredHeight > desiredWidth * checkHeight)
results.Add(validResolution, [desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth), orientation]);
else
results.Add(validResolution, [Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight, orientation]);
}
else
{
if (checkWidth * desiredHeight <= desiredWidth * checkHeight)
results.Add(validResolution, [desiredWidth, desiredHeight, orientation, desiredWidth, Convert.ToInt32(desiredWidth * checkHeight / (double)checkWidth)]);
else
results.Add(validResolution, [desiredWidth, desiredHeight, orientation, Convert.ToInt32(desiredHeight * checkWidth / (double)checkHeight), desiredHeight]);
}
}
}
return results;
}
private FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, FilePath filePath, bool outputResolutionHasNumber, string fileName)
{
FileHolder result;
if (outputResolutionHasNumber)
result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(AngleBracketCollection[0].Replace("<>", _PropertyConfiguration.ResultContent), fileName));
else
{
(string directoryName, _) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath);
result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(cResultsFullGroupDirectory, _PropertyConfiguration.ResultContent, directoryName, fileName));
}
return result;
}
public FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, Item item, bool outputResolutionHasNumber) =>
GetResizedFileHolder(cResultsFullGroupDirectory, item.FilePath, outputResolutionHasNumber, item.FilePath.Name);
public FileHolder GetResizedFileHolder(string cResultsFullGroupDirectory, Item item, bool outputResolutionHasNumber, int id) =>
GetResizedFileHolder(cResultsFullGroupDirectory, item.FilePath, outputResolutionHasNumber, $"{id}{item.FilePath.ExtensionLowered}");
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)
{
Dictionary<string, int[]>? results;
string json;
string[] changesFrom = [nameof(A_Property), nameof(B_Metadata)];
List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath);
FileInfo fileInfo = new(Path.Combine(_FileGroups[_PropertyConfiguration.ResultSingleton][directoryIndex], $"{mappingFromItem.FilePath.NameWithoutExtension}{mappingFromItem.FilePath.ExtensionLowered}.json"));
if (_ForceResizeLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
fileInfo.Refresh();
}
if (_ForceResizeLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
}
if (_PropertiesChangedForResize)
results = null;
else if (!fileInfo.Exists)
results = null;
else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old"))
throw new ArgumentException("must be a *.json file");
else if (dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime)
results = null;
else
{
json = File.ReadAllText(fileInfo.FullName);
try
{
results = JsonSerializer.Deserialize<Dictionary<string, int[]>>(json);
if (results is null)
throw new Exception();
subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), fileInfo.LastWriteTime));
}
catch (Exception)
{
results = null;
parseExceptions.Add(nameof(C_Resize));
}
}
if (results is null)
{
results = GetImageResizes(property);
json = JsonSerializer.Serialize(results, _WriteIndentedJsonSerializerOptions);
bool updateDateWhenMatches = dateTimes.Count != 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime))
{
if (!_ForceResizeLastWriteTimeToCreationTime)
subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), DateTime.Now));
else
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
subFileTuples.Add(new Tuple<string, DateTime>(nameof(C_Resize), fileInfo.CreationTime));
}
}
}
return results;
} }
} }

View File

@ -35,7 +35,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" /> <ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -35,9 +35,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Metadata\Metadata.csproj" /> <ProjectReference Include="..\Metadata\Metadata.csproj" />

View File

@ -22,7 +22,7 @@ public class SetCreatedDate
private readonly IsEnvironment _IsEnvironment; private readonly IsEnvironment _IsEnvironment;
private readonly IConfigurationRoot _ConfigurationRoot; private readonly IConfigurationRoot _ConfigurationRoot;
private readonly Property.Models.Configuration _PropertyConfiguration; private readonly Property.Models.Configuration _PropertyConfiguration;
private readonly ReadOnlyDictionary<string, ReadOnlyCollection<string>> _FileGroups; private readonly ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> _FileGroups;
public SetCreatedDate(List<string> args, ILogger<Program> logger, IsEnvironment isEnvironment, IConfigurationRoot configurationRoot, AppSettings appSettings, string workingDirectory, bool isSilent, IConsole console) public SetCreatedDate(List<string> args, ILogger<Program> logger, IsEnvironment isEnvironment, IConfigurationRoot configurationRoot, AppSettings appSettings, string workingDirectory, bool isSilent, IConsole console)
{ {

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,23 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record CombinedEnumAndIndex(string Combined,
byte Enum,
int Index)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, CombinedEnumAndIndexSourceGenerationContext.Default.CombinedEnumAndIndex);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(CombinedEnumAndIndex))]
internal partial class CombinedEnumAndIndexSourceGenerationContext : 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

@ -4,7 +4,7 @@ namespace View_by_Distance.Shared.Models;
public record Datum( public record Datum(
[property: JsonPropertyName("accessRuleIds")] IReadOnlyList<object> AccessRuleIds, [property: JsonPropertyName("accessRuleIds")] IReadOnlyList<object> AccessRuleIds,
[property: JsonPropertyName("childAssetTypeInfo")] IReadOnlyList<object> ChildAssetTypeInfo, [property: JsonPropertyName("childAssetTyceinfo")] IReadOnlyList<object> ChildAssetTyceinfo,
[property: JsonPropertyName("contentProperties")] ContentProperties ContentProperties, [property: JsonPropertyName("contentProperties")] ContentProperties ContentProperties,
[property: JsonPropertyName("createdBy")] string CreatedBy, [property: JsonPropertyName("createdBy")] string CreatedBy,
[property: JsonPropertyName("createdDate")] DateTime CreatedDate, [property: JsonPropertyName("createdDate")] DateTime CreatedDate,

View File

@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@ -33,13 +34,12 @@ public class Face : Properties.IFace
_OutputResolution = outputResolution; _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) this(DateTime.MinValue, null, null, null, location, null, null)
{ {
DateTime?[] dateTimes;
_OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth); _OutputResolution = new(outputResolutionHeight, outputResolutionOrientation, outputResolutionWidth);
dateTimes = [property.CreationTime, property.LastWriteTime, property.DateTime, property.DateTimeDigitized, property.DateTimeFromName, property.DateTimeOriginal, property.GPSDateStamp]; ReadOnlyCollection<DateTime> dateTimes = Stateless.Methods.IDate.GetDateTimes(exifDirectory);
_DateTime = (from l in dateTimes where l.HasValue select l.Value).Min(); _DateTime = dateTimes.Min();
} }
public override string ToString() public override string ToString()
@ -48,12 +48,16 @@ public class Face : Properties.IFace
return result; 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; namespace View_by_Distance.Shared.Models;
internal record FilePair(string Path, public record FilePair(FilePath FilePath,
bool IsUnique, bool IsUnique,
bool? IsNotUniqueAndNeedsReview, bool? IsNotUniqueAndNeedsReview,
List<string> Collection, List<FilePath> Collection,
string? Match) FilePath? Match)
{ {
public override string ToString() public override string ToString()
@ -20,6 +20,6 @@ internal record FilePair(string Path,
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(FilePair))] [JsonSerializable(typeof(FilePair))]
internal partial class FilePairSourceGenerationContext : JsonSerializerContext public partial class FilePairSourceGenerationContext : JsonSerializerContext
{ {
} }

View File

@ -1,3 +1,4 @@
using System.Collections.ObjectModel;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using View_by_Distance.Shared.Models.Stateless.Methods; using View_by_Distance.Shared.Models.Stateless.Methods;
@ -20,12 +21,6 @@ public record FilePath(long CreationTicks,
int? SortOrder) 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) public static FilePath Get(Properties.IPropertyConfiguration propertyConfiguration, FileHolder fileHolder, int? index)
{ {
if (fileHolder.CreationTime is null) if (fileHolder.CreationTime is null)
@ -83,6 +78,61 @@ public record FilePath(long CreationTicks,
return result; 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 = [];
List<FilePath>? collection;
Dictionary<int, List<FilePath>> keyValuePairs = [];
foreach (ReadOnlyCollection<FilePath> filePaths in filePathsCollection)
{
if (filePaths.Count == 0)
continue;
foreach (FilePath filePath in filePaths)
{
if (filePath.Id is null)
continue;
if (!keyValuePairs.TryGetValue(filePath.Id.Value, out collection))
{
keyValuePairs.Add(filePath.Id.Value, []);
if (!keyValuePairs.TryGetValue(filePath.Id.Value, out collection))
throw new Exception();
}
collection.Add(filePath);
}
}
foreach (KeyValuePair<int, List<FilePath>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return results.AsReadOnly();
}
public override string ToString()
{
string result = JsonSerializer.Serialize(this, FilePathSourceGenerationContext.Default.FilePath);
return result;
}
} }
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]

View File

@ -6,6 +6,7 @@ namespace View_by_Distance.Shared.Models;
public class Item : Properties.IItem public class Item : Properties.IItem
{ {
protected ExifDirectory? _ExifDirectory;
protected List<Face> _Faces; protected List<Face> _Faces;
protected readonly bool? _FileSizeChanged; protected readonly bool? _FileSizeChanged;
protected readonly FilePath _FilePath; protected readonly FilePath _FilePath;
@ -15,10 +16,10 @@ public class Item : Properties.IItem
protected bool _IsValidImageFormatExtension; protected bool _IsValidImageFormatExtension;
protected bool? _LastWriteTimeChanged; protected bool? _LastWriteTimeChanged;
protected bool? _Moved; protected bool? _Moved;
protected Property? _Property;
protected readonly string _RelativePath; protected readonly string _RelativePath;
protected FileHolder? _ResizedFileHolder; protected FileHolder? _ResizedFileHolder;
protected readonly FileHolder _SourceDirectoryFileHolder; protected readonly FileHolder _SourceDirectoryFileHolder;
public ExifDirectory? ExifDirectory => _ExifDirectory;
public List<Face> Faces => _Faces; public List<Face> Faces => _Faces;
public bool? FileSizeChanged => _FileSizeChanged; public bool? FileSizeChanged => _FileSizeChanged;
public FilePath FilePath => _FilePath; public FilePath FilePath => _FilePath;
@ -28,13 +29,12 @@ public class Item : Properties.IItem
public bool IsValidImageFormatExtension => _IsValidImageFormatExtension; public bool IsValidImageFormatExtension => _IsValidImageFormatExtension;
public bool? LastWriteTimeChanged => _LastWriteTimeChanged; public bool? LastWriteTimeChanged => _LastWriteTimeChanged;
public bool? Moved => _Moved; public bool? Moved => _Moved;
public Property? Property => _Property;
public string RelativePath => _RelativePath; public string RelativePath => _RelativePath;
public FileHolder? ResizedFileHolder => _ResizedFileHolder; public FileHolder? ResizedFileHolder => _ResizedFileHolder;
public FileHolder SourceDirectoryFileHolder => _SourceDirectoryFileHolder; public FileHolder SourceDirectoryFileHolder => _SourceDirectoryFileHolder;
[JsonConstructor] [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; _Faces = faces;
_FilePath = filePath; _FilePath = filePath;
@ -45,25 +45,28 @@ public class Item : Properties.IItem
_IsValidImageFormatExtension = isValidImageFormatExtension; _IsValidImageFormatExtension = isValidImageFormatExtension;
_LastWriteTimeChanged = lastWriteTimeChanged; _LastWriteTimeChanged = lastWriteTimeChanged;
_Moved = moved; _Moved = moved;
_Property = property; _ExifDirectory = exifDirectory;
_RelativePath = relativePath; _RelativePath = relativePath;
_ResizedFileHolder = resizedFileHolder; _ResizedFileHolder = resizedFileHolder;
_SourceDirectoryFileHolder = sourceDirectoryFileHolder; _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; Item result;
if (relativePath.EndsWith(".json")) if (relativePath.EndsWith(".json"))
throw new ArgumentException("Can not be a *.json file!"); throw new ArgumentException("Can not be a *.json file!");
if (filePath.ExtensionLowered is ".json") if (filePath.ExtensionLowered is ".json")
throw new ArgumentException("Can not be a *.json file!"); 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; return result;
} }
public static Item Get(FilePath filePath, FileHolder sourceDirectoryFileHolder, string relativePath, bool isValidImageFormatExtension) => public bool Any() =>
Get(filePath, sourceDirectoryFileHolder, relativePath, null, null, false, isValidImageFormatExtension, null, null, null, null); !_SourceDirectoryFileHolder.Exists || (_FileSizeChanged.HasValue && _FileSizeChanged.Value) || (_LastWriteTimeChanged.HasValue && _LastWriteTimeChanged.Value) || (_Moved.HasValue && _Moved.Value);
public override string ToString() public override string ToString()
{ {
@ -71,6 +74,9 @@ public class Item : Properties.IItem
return result; return result;
} }
public void Update(ExifDirectory exifDirectory) =>
_ExifDirectory = exifDirectory;
public void SetMoved(bool moved) => _Moved = moved; public void SetMoved(bool moved) => _Moved = moved;
public void SetResizedFileHolder(string filenameExtension, FileHolder fileHolder) public void SetResizedFileHolder(string filenameExtension, FileHolder fileHolder)
@ -83,8 +89,4 @@ public class Item : Properties.IItem
_ResizedFileHolder = fileHolder; _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;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models; namespace View_by_Distance.Shared.Models;
public record MappingFromItem(DateTime[] ContainerDateTimes, public record MappingFromItem(DateTime[] ContainerDateTimes,
DateTime? DateTimeDigitized,
DateTime? DateTimeOriginal, DateTime? DateTimeOriginal,
int Id, int Id,
bool? IsArchive, bool? IsArchive,
FilePath FilePath, FilePath FilePath,
bool? IsWrongYear, bool? IsWrongYear,
string[] Keywords, ReadOnlyCollection<string> Keywords,
DateTime MinimumDateTime, DateTime MinimumDateTime,
string? Model, string? Model,
string RelativePath, string RelativePath,
@ -29,14 +29,18 @@ public record MappingFromItem(DateTime[] ContainerDateTimes,
internal static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Item item, FileHolder? resizedFileHolder) internal static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Item item, FileHolder? resizedFileHolder)
{ {
MappingFromItem result; MappingFromItem result;
if (item.Property?.Id is null) if (item.ExifDirectory?.FilePath.Id is null)
throw new NotSupportedException(); throw new NotSupportedException();
if (resizedFileHolder is null) if (resizedFileHolder is null)
throw new NotSupportedException(); throw new NotSupportedException();
List<DateTime> dateTimes = item.Property.GetDateTimes(); ExifDirectory exifDirectory = item.ExifDirectory;
DateTime minimumDateTime = Stateless.Methods.IProperty.GetMinimumDateTime(item.Property); DateTime minimumDateTime = Stateless.Methods.IDate.GetMinimum(exifDirectory);
(bool? isWrongYear, _) = Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes); DateTime? dateTime = Stateless.Methods.IDate.GetDateTimeOriginal(exifDirectory);
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); 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; return result;
} }

View File

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

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 interface IItem
{ {
public ExifDirectory? ExifDirectory { get; }
public bool? FileSizeChanged { get; } public bool? FileSizeChanged { get; }
public List<Face> Faces { get; } public List<Face> Faces { get; }
public FilePath FilePath { get; } public FilePath FilePath { get; }
@ -11,7 +12,6 @@ public interface IItem
public bool IsUniqueFileName { get; } public bool IsUniqueFileName { get; }
public bool IsValidImageFormatExtension { get; } public bool IsValidImageFormatExtension { get; }
public bool? Moved { get; } public bool? Moved { get; }
public Property? Property { get; }
public string RelativePath { get; } public string RelativePath { get; }
public FileHolder? ResizedFileHolder { get; } public FileHolder? ResizedFileHolder { get; }
public FileHolder SourceDirectoryFileHolder { get; } public FileHolder SourceDirectoryFileHolder { get; }

View File

@ -1,282 +0,0 @@
using System.Collections.ObjectModel;
using System.Text.Json;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract class Container
{
private record FilePair(bool IsUnique, List<string> Collection, FilePath FilePath, Models.Item Item) { }
internal static DateTime[] GetContainerDateTimes(ReadOnlyCollection<Models.Item> items)
{
DateTime[] results;
long containerMinimumTicks = (from l in items select l.FilePath.LastWriteTicks).Min();
long containerMaximumTicks = (from l in items select l.FilePath.LastWriteTicks).Max();
results = [new(containerMinimumTicks), new(containerMaximumTicks)];
return results;
}
internal static ReadOnlyCollection<Models.Item> GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container)
{
List<Models.Item> results = [];
foreach (Models.Item item in container.Items)
{
if (!item.IsValidImageFormatExtension || propertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered))
continue;
results.Add(item);
}
return container.Items.Count == results.Count ? container.Items : new(results);
}
private static List<Models.FilePair> GetFilePairs(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string aPropertySingletonDirectory, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
{
int renamed;
const bool useCeilingAverage = true;
List<Models.FilePair>? filePairs = null;
ReadOnlyCollection<string[]>? jsonFilesCollection = null;
IReadOnlyDictionary<string, List<string>>? compareFileNamesToFiles = null;
IReadOnlyDictionary<string, List<string>> fileNamesToFiles = XDirectory.GetFilesKeyValuePairs(filePathsCollection);
for (int i = 0; i < short.MaxValue; i++)
{
renamed = 0;
jsonFilesCollection = IDirectory.GetFilesCollection(aPropertySingletonDirectory, directorySearchFilter, extension, useCeilingAverage);
compareFileNamesToFiles = XDirectory.GetFilesKeyValuePairs(jsonFilesCollection);
renamed += XDirectory.LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension);
filePairs = XDirectory.GetFiles(propertyConfiguration, filePathsCollection, fileNamesToFiles, extension, compareFileNamesToFiles);
renamed += XDirectory.MaybeMove(propertyConfiguration, filePairs, aPropertySingletonDirectory, extension);
if (renamed == 0)
break;
if (i > 10)
throw new NotImplementedException();
}
if (filePairs is null || jsonFilesCollection is null || compareFileNamesToFiles is null)
throw new NullReferenceException(nameof(filePairs));
return filePairs;
}
private static Models.Property? GetProperty(Models.FilePair filePair)
{
Models.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 void ParallelFor(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string extension, int rootDirectoryLength, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, Models.FilePair filePair, List<FilePair> results)
{
dlibDotNet?.Tick();
bool abandoned = false;
Models.FileHolder sourceDirectoryFileHolder;
Models.Property? property = GetProperty(filePair);
Models.FileHolder imageFileHolder = IFileHolder.Get(filePair.Path);
FilePath filePath = FilePath.Get(propertyConfiguration, imageFileHolder, 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 (shouldIgnore is not null)
{
if (shouldIgnore.Value)
{
FileInfo fileInfo = new(filePath.FullName);
if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden))
File.SetAttributes(imageFileHolder.FullName, FileAttributes.Hidden);
}
if (filePath.HasIgnoreKeyword is not null && filePath.HasIgnoreKeyword.Value != shouldIgnore.Value)
{
if (filePath.DirectoryFullPath.Contains("Results") && filePath.DirectoryFullPath.Contains("Resize"))
File.Delete(filePath.FullName);
else
throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>");
}
}
string relativePath = IPath.GetRelativePath(filePair.Path, rootDirectoryLength, forceExtensionToLower: true);
bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != 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(aPropertySingletonDirectory, relativePath, extension)));
else
{
string fileName = Path.GetFileName(filePair.Path);
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(propertyConfiguration, filePath);
sourceDirectoryFileHolder = IFileHolder.Get(Path.Combine(aPropertySingletonDirectory, directoryName, $"{fileName}{extension}"));
}
if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks)
{
File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePath.LastWriteTicks));
File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value);
}
Models.Item item = Models.Item.Get(filePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged);
lock (results)
results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item));
}
private static List<FilePair> GetFilePairs(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, string directorySearchFilter)
{
List<FilePair> results = [];
const string extension = ".json";
int maxDegreeOfParallelism = Environment.ProcessorCount;
int filesCollectionDirectoryLength = filesCollectionDirectory.Length;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
List<Models.FilePair> filePairs = GetFilePairs(propertyConfiguration, directorySearchFilter, extension, aPropertySingletonDirectory, filePathsCollection);
_ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, extension, filesCollectionDirectoryLength, splatNineIdentifiers, filePairs[i], results));
return results;
}
private static (int, Models.Container[]) GetContainers(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, string directorySearchFilter)
{
List<Models.Container> results = [];
string directory;
List<Models.Item>? items;
Models.Container container;
List<string> directories = [];
Dictionary<string, List<Models.Item>> directoryToItems = [];
foreach (ReadOnlyCollection<FilePath> filePaths in filePathsCollection)
{
if (filePaths.Count == 0)
continue;
directory = filePaths[0].DirectoryFullPath;
if (directory is null)
continue;
if (!directories.Contains(directory))
directories.Add(directory);
if (!directoryToItems.TryGetValue(directory, out items))
{
directoryToItems.Add(directory, []);
if (!directoryToItems.TryGetValue(directory, out items))
throw new Exception();
}
}
List<FilePair> filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection, directorySearchFilter);
foreach (FilePair filePair in filePairs)
{
if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items))
{
directoryToItems.Add(filePair.FilePath.DirectoryFullPath, []);
if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items))
throw new Exception();
}
items.Add(filePair.Item);
}
foreach (KeyValuePair<string, List<Models.Item>> keyValuePair in directoryToItems)
{
if (keyValuePair.Value.Count == 0)
continue;
container = new(keyValuePair.Key, new(keyValuePair.Value));
results.Add(container);
}
return (filePairs.Count, results.ToArray());
}
internal static ReadOnlyCollection<Models.Container> GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
{
Models.Container[] results;
const string directorySearchFilter = "*";
(_, results) = GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection, directorySearchFilter);
return new(results);
}
internal static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string aPropertySingletonDirectory)
{
int count;
Models.Container[] results;
IDlibDotNet? dlibDotNet = null;
const bool useCeilingAverage = true;
const string fileSearchFilter = "*";
const string directorySearchFilter = "*";
ReadOnlyCollection<string[]> filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection);
(count, results) = GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, propertyConfiguration.RootDirectory, splatNineIdentifiers, filePathsCollection, directorySearchFilter);
return (count, results);
}
internal static List<int> GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers)
{
List<int> results = [];
ReadOnlyCollection<Models.Item> filteredItems;
foreach (Models.Container container in readOnlyContainers)
{
if (container.Items.Count == 0)
continue;
filteredItems = GetValidImageItems(propertyConfiguration, container);
if (filteredItems.Count == 0)
continue;
foreach (Models.Item item in filteredItems)
{
if (item.Property?.Id is null || item.ResizedFileHolder is null)
continue;
if (results.Contains(item.Property.Id.Value))
continue;
results.Add(item.Property.Id.Value);
}
}
return results;
}
internal static List<string> GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers)
{
List<string> results = [];
ReadOnlyCollection<Models.Item> filteredItems;
foreach (Models.Container container in readOnlyContainers)
{
if (container.Items.Count == 0)
continue;
filteredItems = GetValidImageItems(propertyConfiguration, container);
if (filteredItems.Count == 0)
continue;
foreach (Models.Item item in filteredItems)
{
if (item.Property?.Id is null || item.ResizedFileHolder is null)
continue;
if (results.Contains(item.FilePath.FileNameFirstSegment))
continue;
results.Add(item.FilePath.FileNameFirstSegment);
}
}
return results;
}
internal static ReadOnlyCollection<Models.Item> GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems)
{
List<Models.Item> results = [];
List<int> distinct = [];
ReadOnlyCollection<Models.Item> filteredItems;
foreach (Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
if (!filterItems)
filteredItems = container.Items;
else
{
filteredItems = GetValidImageItems(propertyConfiguration, container);
if (filteredItems.Count == 0)
continue;
}
foreach (Models.Item item in filteredItems)
{
if (item.Property?.Id is null || item.ResizedFileHolder is null)
continue;
if (distinctItems)
{
if (distinct.Contains(item.Property.Id.Value))
continue;
distinct.Add(item.Property.Id.Value);
}
results.Add(item);
}
}
return new(results);
}
}

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

@ -1,48 +0,0 @@
using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IContainer
{
DateTime[] TestStatic_GetContainerDateTimes(ReadOnlyCollection<Models.Item> items) =>
GetContainerDateTimes(items);
static DateTime[] GetContainerDateTimes(ReadOnlyCollection<Models.Item> items) =>
Container.GetContainerDateTimes(items);
ReadOnlyCollection<Models.Item> TestStatic_GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container) =>
GetValidImageItems(propertyConfiguration, container);
static ReadOnlyCollection<Models.Item> GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container) =>
Container.GetValidImageItems(propertyConfiguration, container);
(int, Models.Container[]) TestStatic_GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) =>
GetContainers(propertyConfiguration, aPropertySingletonDirectory);
static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) =>
GetContainers(propertyConfiguration, null, aPropertySingletonDirectory);
(int, Models.Container[]) TestStatic_GetContainers(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string aPropertySingletonDirectory) =>
GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory);
static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, string aPropertySingletonDirectory) =>
Container.GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory);
ReadOnlyCollection<Models.Container> TestStatic_GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection) =>
GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection);
static ReadOnlyCollection<Models.Container> GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection) =>
Container.GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, splatNineIdentifiers, filePathsCollection);
List<int> TestStatic_GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers);
static List<int> GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers);
List<string> TestStatic_GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers);
static List<string> GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> readOnlyContainers) =>
Container.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers);
ReadOnlyCollection<Models.Item> TestStatic_GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems) =>
GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems);
static ReadOnlyCollection<Models.Item> GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<Models.Container> containers, bool distinctItems, bool filterItems) =>
Container.GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems);
}

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

@ -5,49 +5,70 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IDirectory public interface IDirectory
{ {
char TestStatic_GetDirectory(string fileName) => public static int GetDirectory(char directory) =>
GetDirectory(fileName);
static char GetDirectory(string fileName) =>
fileName.Split('-').Length > 2 ? '-' : fileName.Split('.')[0][^1];
int TestStatic_GetDirectory(char directory) =>
GetDirectory(directory);
static int GetDirectory(char directory) =>
directory == '-' ? 10 : int.TryParse(directory.ToString(), out int value) ? value : 11; directory == '-' ? 10 : int.TryParse(directory.ToString(), out int value) ? value : 11;
ReadOnlyCollection<string[]> TestStatic_GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) => public static char GetDirectory(string fileName) =>
GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage); fileName.Split('-').Length > 2 ? '-' : fileName.Split('.')[0][^1];
static ReadOnlyCollection<string[]> GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) =>
XDirectory.GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
ReadOnlyCollection<ReadOnlyCollection<FilePath>> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) => public static void MoveFiles(List<string> files, string find, string replace) =>
GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage);
static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage) =>
XDirectory.GetFilePathCollections(propertyConfiguration, directorySearchFilter, fileSearchFilter, directory, useCeilingAverage);
void TestStatic_MoveFiles(List<string> files, string find, string replace) =>
MoveFiles(files, find, replace);
static void MoveFiles(List<string> files, string find, string replace) =>
XDirectory.MoveFiles(files, find, replace); XDirectory.MoveFiles(files, find, replace);
(string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyCollection<string>> fileGroups, Action? tick) => public static List<string> CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) =>
GetToDoCollection(propertyConfiguration, filePathsCollection, fileGroups, tick);
static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyCollection<string>> fileGroups, Action? tick) =>
XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates: false, ifCanUseId: true, filePathsCollection, fileGroups, tick);
(string[], List<(FilePath, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyCollection<string>> fileGroups, Action? tick) =>
GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, tick);
static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, ReadOnlyCollection<string>> fileGroups, Action? tick) =>
XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filePathsCollection, fileGroups, tick);
List<string> TestStatic_CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) =>
CopyOrMove(toDoCollection, move, moveBack, tick);
static List<string> CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) =>
XDirectory.CopyOrMove(toDoCollection, move, moveBack, tick); XDirectory.CopyOrMove(toDoCollection, move, moveBack, tick);
ReadOnlyCollection<ReadOnlyCollection<FilePath>> TestStatic_GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection) => public static ReadOnlyCollection<string[]> GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) =>
GetFilePathCollections(propertyConfiguration, filesCollection); XDirectory.GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
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, ReadOnlyCollection<string[]> filesCollection, bool useIgnoreExtensions) =>
XDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
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, 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);
internal char TestStatic_GetDirectory(string fileName) =>
GetDirectory(fileName);
internal void TestStatic_MoveFiles(List<string> files, string find, string replace) =>
MoveFiles(files, find, replace);
internal List<string> TestStatic_CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) =>
CopyOrMove(toDoCollection, move, moveBack, tick);
internal ReadOnlyCollection<string[]> TestStatic_GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) =>
GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
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, ReadOnlyCollection<string[]> filesCollection, bool useIgnoreExtensions) =>
GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
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, 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

@ -4,5 +4,8 @@ public interface IDlibDotNet
{ {
void Tick(); 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

@ -1,61 +1,75 @@
using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IId public interface IId
{ // ... {
const int DeterministicHashCode = 9876543; const int DeterministicHashCode = 9876543;
static bool IsOffsetDeterministicHashCode(Properties.IPropertyConfiguration propertyConfiguration) => public static int GetDeterministicHashCode(byte[] value) =>
propertyConfiguration.Offset == DeterministicHashCode;
string TestStatic_GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
Id.GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
int TestStatic_GetId(Properties.IPropertyConfiguration propertyConfiguration, string intelligentId) =>
GetId(propertyConfiguration, intelligentId);
static int GetId(Properties.IPropertyConfiguration propertyConfiguration, string intelligentId) =>
Id.GetId(propertyConfiguration, intelligentId);
string TestStatic_GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
static string GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
Id.GetPaddedId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
string TestStatic_GetIgnoreFullPath(FilePath filePath, Models.FileHolder fileHolder) =>
GetIgnoreFullPath(filePath, fileHolder);
static string GetIgnoreFullPath(FilePath filePath, Models.FileHolder fileHolder) =>
fileHolder.DirectoryFullPath is null ?
throw new NotSupportedException() :
filePath.Id > -1 ?
fileHolder.NameWithoutExtension[^1] == '9' ?
Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}8{fileHolder.ExtensionLowered}") :
throw new NotSupportedException("High") :
fileHolder.NameWithoutExtension[^1] == '1' ?
Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}2{fileHolder.ExtensionLowered}") :
throw new NotSupportedException("Low");
bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) =>
NameWithoutExtensionIsIntelligentIdFormat(propertyConfiguration, fileNameFirstSegment);
static bool NameWithoutExtensionIsIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) =>
fileNameFirstSegment.Length - 1 == propertyConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
NameWithoutExtensionIsPaddedIntelligentIdFormat(propertyConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment);
static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(Properties.IPropertyConfiguration propertyConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
fileNameFirstSegment.Length == propertyConfiguration.IntMinValueLength + sortOrderOnlyLengthIndex + 1
&& fileNameFirstSegment[^1] is '1' or '2' or '8' or '9'
&& fileNameFirstSegment.All(char.IsNumber);
bool TestStatic_NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) =>
NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder);
static bool NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) =>
Id.NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder.NameWithoutExtension.Split('.')[0]);
int TestStatic_GetDeterministicHashCode(byte[] value) =>
GetDeterministicHashCode(value);
static int GetDeterministicHashCode(byte[] value) =>
Id.GetDeterministicHashCode(value); Id.GetDeterministicHashCode(value);
public static byte GetHasIgnoreKeyword(FilePath filePath) =>
Id.GetHasIgnoreKeyword(filePath);
public static int GetId(IPropertyConfiguration propertyConfiguration, string intelligentId) =>
Id.GetId(propertyConfiguration, intelligentId);
public static bool IsOffsetDeterministicHashCode(IPropertyConfiguration propertyConfiguration) =>
propertyConfiguration.Offset == DeterministicHashCode;
public static byte GetHasDateTimeOriginal(IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
Id.GetHasDateTimeOriginal(propertyConfiguration, filePath);
public static byte GetMissingDateTimeOriginal(IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
Id.GetMissingDateTimeOriginal(propertyConfiguration, filePath);
public static bool NameWithoutExtensionIsIdFormat(IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) =>
Id.NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder.NameWithoutExtension.Split('.')[0]);
public static bool NameWithoutExtensionIsIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) =>
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, 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 '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);
internal byte TestStatic_GetHasIgnoreKeyword(FilePath filePath) =>
GetHasIgnoreKeyword(filePath);
internal int TestStatic_GetId(IPropertyConfiguration propertyConfiguration, string intelligentId) =>
GetId(propertyConfiguration, intelligentId);
internal byte TestStatic_GetHasDateTimeOriginal(IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
GetHasDateTimeOriginal(propertyConfiguration, filePath);
internal byte TestStatic_GetMissingDateTimeOriginal(IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
GetMissingDateTimeOriginal(propertyConfiguration, filePath);
internal bool TestStatic_NameWithoutExtensionIsIdFormat(IPropertyConfiguration propertyConfiguration, Models.FileHolder fileHolder) =>
NameWithoutExtensionIsIdFormat(propertyConfiguration, fileHolder);
internal bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(IPropertyConfiguration propertyConfiguration, string fileNameFirstSegment) =>
NameWithoutExtensionIsIntelligentIdFormat(propertyConfiguration, fileNameFirstSegment);
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; namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IMappingFromItem public interface IMappingFromItem
{ // ... {
MappingFromItem TestStatic_GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder) public static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder)
=> GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
static MappingFromItem GetMappingFromItem(DateTime[] containerDateTimes, Models.Item item, Models.FileHolder? resizedFileHolder)
=> MappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); => MappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
MappingFromItem TestStatic_GetMappingFromItem(Models.Item item) public static MappingFromItem GetMappingFromItem(Models.Item item)
=> GetMappingFromItem(item);
static MappingFromItem GetMappingFromItem(Models.Item item)
=> GetMappingFromItem(containerDateTimes: [], item, item.ResizedFileHolder); => 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

@ -4,72 +4,95 @@ using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IPath public interface IPath
{ // ... {
string TestStatic_GetRelativePath(string path, int length) => public static byte GetEnum(FilePath filePath) =>
GetRelativePath(path, length); XPath.GetEnum(filePath);
static string GetRelativePath(string path, int length) =>
XPath.GetRelativePath(path, length, forceExtensionToLower: false);
bool TestStatic_DeleteEmptyDirectories(string rootDirectory) => public static string[] GetDirectories(string directory) =>
DeleteEmptyDirectories(rootDirectory);
static bool DeleteEmptyDirectories(string rootDirectory) =>
XPath.DeleteEmptyDirectories(rootDirectory);
void TestStatic_ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
ChangeDateForEmptyDirectories(rootDirectory, ticks);
static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
XPath.ChangeDateForEmptyDirectories(rootDirectory, ticks);
void TestStatic_MakeHiddenIfAllItemsAreHidden(string rootDirectory) =>
MakeHiddenIfAllItemsAreHidden(rootDirectory);
static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) =>
XPath.MakeHiddenIfAllItemsAreHidden(rootDirectory);
void TestStatic_DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
DeleteEmptyDirectories(rootDirectory, deletedDirectories);
static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
XPath.DeleteEmptyDirectories(rootDirectory, deletedDirectories);
// $dirs = gci "" -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName $dirs | Foreach-Object { Remove-Item $_ }
string[] TestStatic_GetDirectoryNames(string directory) =>
GetDirectoryNames(directory);
static string[] GetDirectoryNames(string directory) =>
XPath.GetDirectoryNames(directory).ToArray();
string[] TestStatic_GetDirectories(string directory) =>
GetDirectories(directory);
static string[] GetDirectories(string directory) =>
XPath.GetDirectories(directory).ToArray(); XPath.GetDirectories(directory).ToArray();
string TestStatic_GetRelativePath(string path, int length, bool forceExtensionToLower) => public static string[] GetDirectoryNames(string directory) =>
GetRelativePath(path, length, forceExtensionToLower); XPath.GetDirectoryNames(directory).ToArray();
static string GetRelativePath(string path, int length, bool forceExtensionToLower) =>
public static string GetRelativePath(string path, int length) =>
XPath.GetRelativePath(path, length, forceExtensionToLower: false);
public static bool DeleteEmptyDirectories(string rootDirectory) =>
XPath.DeleteEmptyDirectories(rootDirectory);
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);
public static string GetRelativePath(string path, int length, bool forceExtensionToLower) =>
XPath.GetRelativePath(path, length, forceExtensionToLower); XPath.GetRelativePath(path, length, forceExtensionToLower);
bool TestStatic_WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) => public static string GetDirectory(string sourceDirectory, int level, string directoryName) =>
WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches);
static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) =>
XPath.WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches);
(int level, List<string> directories) TestStatic_Get(string rootDirectory, string sourceDirectory) =>
Get(rootDirectory, sourceDirectory);
static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory) =>
XPath.Get(rootDirectory, sourceDirectory);
string TestStatic_GetDirectory(string sourceDirectory, int level, string directoryName) =>
GetDirectory(sourceDirectory, level, directoryName);
static string GetDirectory(string sourceDirectory, int level, string directoryName) =>
XPath.GetDirectory(sourceDirectory, level, directoryName); XPath.GetDirectory(sourceDirectory, level, directoryName);
(string, int) TestStatic_GetDirectoryNameAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) => public static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
GetDirectoryNameAndIndex(propertyConfiguration, filePath); XPath.DeleteEmptyDirectories(rootDirectory, deletedDirectories);
static (string, int) GetDirectoryNameAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) => public static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory) =>
XPath.GetDirectoryNameAndIndex(propertyConfiguration, filePath); XPath.Get(rootDirectory, sourceDirectory);
ReadOnlyDictionary<string, ReadOnlyCollection<string>> TestStatic_GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) => public static CombinedEnumAndIndex GetCombinedEnumAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups); XPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath);
static ReadOnlyDictionary<string, ReadOnlyCollection<string>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
public static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) =>
XPath.WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches);
public static ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
XPath.GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups); XPath.GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups);
internal byte TestStatic_GetEnum(FilePath filePath) =>
GetEnum(filePath);
internal string[] TestStatic_GetDirectories(string directory) =>
GetDirectories(directory);
internal string[] TestStatic_GetDirectoryNames(string directory) =>
GetDirectoryNames(directory);
internal string TestStatic_GetRelativePath(string path, int length) =>
GetRelativePath(path, length);
internal bool TestStatic_DeleteEmptyDirectories(string rootDirectory) =>
DeleteEmptyDirectories(rootDirectory);
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);
internal string TestStatic_GetRelativePath(string path, int length, bool forceExtensionToLower) =>
GetRelativePath(path, length, forceExtensionToLower);
internal string TestStatic_GetDirectory(string sourceDirectory, int level, string directoryName) =>
GetDirectory(sourceDirectory, level, directoryName);
internal void TestStatic_DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
DeleteEmptyDirectories(rootDirectory, deletedDirectories);
internal (int level, List<string> directories) TestStatic_Get(string rootDirectory, string sourceDirectory) =>
Get(rootDirectory, sourceDirectory);
internal CombinedEnumAndIndex TestStatic_GetCombinedEnumAndIndex(IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
GetCombinedEnumAndIndex(propertyConfiguration, filePath);
internal bool TestStatic_WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) =>
WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches);
internal ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> TestStatic_GetKeyValuePairs(IPropertyConfiguration propertyConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
GetKeyValuePairs(propertyConfiguration, resultsFullGroupDirectory, jsonGroups);
} }

View File

@ -23,11 +23,6 @@ public interface IProperty
static string GetDiffRootDirectory(string diffPropertyDirectory) => static string GetDiffRootDirectory(string diffPropertyDirectory) =>
Property.GetDiffRootDirectory(diffPropertyDirectory); Property.GetDiffRootDirectory(diffPropertyDirectory);
bool TestStatic_Any(Models.Container[] propertyHolderCollections) =>
Any(propertyHolderCollections);
static bool Any(Models.Container[] propertyHolderCollections) =>
Property.Any(propertyHolderCollections);
(bool?, string[]) TestStatic_IsWrongYear(string[] segments, string year) => (bool?, string[]) TestStatic_IsWrongYear(string[] segments, string year) =>
IsWrongYear(segments, year); IsWrongYear(segments, year);
static (bool?, string[]) IsWrongYear(string[] segments, string year) => static (bool?, string[]) IsWrongYear(string[] segments, string year) =>
@ -43,21 +38,6 @@ public interface IProperty
static List<DateTime> GetDateTimes(Models.Property property) => static List<DateTime> GetDateTimes(Models.Property property) =>
Property.GetDateTimes(property.CreationTime, property.LastWriteTime, property.DateTime, property.DateTimeDigitized, property.DateTimeFromName, property.DateTimeOriginal, property.GPSDateStamp); Property.GetDateTimes(property.CreationTime, property.LastWriteTime, property.DateTime, property.DateTimeDigitized, property.DateTimeFromName, property.DateTimeOriginal, property.GPSDateStamp);
double TestStatic_GetStandardDeviation(List<long> values, double average) =>
GetStandardDeviation(values, average);
static double GetStandardDeviation(List<long> values, double average) =>
Property.GetStandardDeviation(values, average);
TimeSpan TestStatic_GetThreeStandardDeviationHigh(int minimum, Models.Container container) =>
GetThreeStandardDeviationHigh(minimum, container);
static TimeSpan GetThreeStandardDeviationHigh(int minimum, Models.Container container) =>
Property.GetThreeStandardDeviationHigh(minimum, container);
(int, List<DateTime>, List<Models.Item>) TestStatic_Get(Models.Container container, TimeSpan threeStandardDeviationHigh, int i) =>
Get(container, threeStandardDeviationHigh, i);
static (int, List<DateTime>, List<Models.Item>) Get(Models.Container container, TimeSpan threeStandardDeviationHigh, int i) =>
Property.Get(container, threeStandardDeviationHigh, i);
List<DateTime> TestStatic_GetDateTimes(DateTime creationTime, DateTime lastWriteTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, DateTime? gpsDateStamp) => List<DateTime> TestStatic_GetDateTimes(DateTime creationTime, DateTime lastWriteTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, DateTime? gpsDateStamp) =>
GetDateTimes(creationTime, lastWriteTime, dateTime, dateTimeDigitized, dateTimeFromName, dateTimeOriginal, gpsDateStamp); GetDateTimes(creationTime, lastWriteTime, dateTime, dateTimeDigitized, dateTimeFromName, dateTimeOriginal, gpsDateStamp);
static List<DateTime> GetDateTimes(DateTime creationTime, DateTime lastWriteTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, DateTime? gpsDateStamp) => static List<DateTime> GetDateTimes(DateTime creationTime, DateTime lastWriteTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, DateTime? gpsDateStamp) =>

View File

@ -5,81 +5,6 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract class Id internal abstract class Id
{ {
internal static bool NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameWithoutExtension)
{
bool result;
if (fileNameWithoutExtension.Length < 5 || fileNameWithoutExtension.Length > propertyConfiguration.IntMinValueLength)
result = false;
else
{
bool skipOneAllAreNumbers = fileNameWithoutExtension[1..].All(char.IsNumber);
result = (skipOneAllAreNumbers && fileNameWithoutExtension[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameWithoutExtension[0]));
}
return result;
}
internal static int GetId(Properties.IPropertyConfiguration propertyConfiguration, string intelligentId)
{
int result;
StringBuilder results = new();
if (propertyConfiguration.IntMinValueLength < (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
for (int i = intelligentId.Length - (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
_ = results.Append(intelligentId[i]);
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
result = int.Parse(results.ToString());
if (intelligentId[^1] is '1' or '2')
result *= -1;
else if (intelligentId[^1] is not '9' and not '8')
throw new NotSupportedException();
return result;
}
internal static string GetIntelligentId(Properties.IPropertyConfiguration propertyConfiguration, long id, 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)
{
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : 9;
value = id.ToString().PadLeft(propertyConfiguration.IntMinValueLength, '0');
}
else
{
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : 1;
value = id.ToString()[1..].PadLeft(propertyConfiguration.IntMinValueLength, '0');
}
for (int i = value.Length - propertyConfiguration.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
_ = stringBuilder.Append(value[i]);
for (int i = value.Length - propertyConfiguration.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
resultAllInOneSubdirectoryChars.Add(value[i]);
result = $"{stringBuilder}{string.Join(string.Empty, resultAllInOneSubdirectoryChars)}{key}";
return result;
}
internal static string GetPaddedId(Properties.IPropertyConfiguration propertyConfiguration, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
{
string result;
if (propertyConfiguration.Offset < 0)
result = Guid.NewGuid().ToString();
else
{
string intelligentId = GetIntelligentId(propertyConfiguration, id, hasIgnoreKeyword, hasDateTimeOriginal);
int check = GetId(propertyConfiguration, intelligentId);
if (check != id)
throw new NotSupportedException();
result = index is null || propertyConfiguration.Offset == IId.DeterministicHashCode ? intelligentId : $"{propertyConfiguration.Offset + index}{intelligentId}";
}
return result;
}
internal static int GetDeterministicHashCode(byte[] value) internal static int GetDeterministicHashCode(byte[] value)
{ {
int result; int result;
@ -99,4 +24,97 @@ internal abstract class Id
return result; return result;
} }
internal static byte GetHasIgnoreKeyword(FilePath filePath) =>
(byte)(filePath.Id > -1 ? 8 : 2);
internal static int GetId(Properties.IPropertyConfiguration propertyConfiguration, string intelligentId)
{
int result;
StringBuilder results = new();
if (propertyConfiguration.IntMinValueLength < (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
for (int i = intelligentId.Length - (propertyConfiguration.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
_ = results.Append(intelligentId[i]);
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
result = int.Parse(results.ToString());
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();
return result;
}
internal static byte GetHasDateTimeOriginal(Properties.IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
(byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 9 : 1 : filePath.Id > -1 ? 6 : 4);
internal static byte GetMissingDateTimeOriginal(Properties.IPropertyConfiguration propertyConfiguration, FilePath filePath) =>
(byte)(!propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 7 : 3 : filePath.Id > -1 ? 5 : 0);
internal static bool NameWithoutExtensionIsIdFormat(Properties.IPropertyConfiguration propertyConfiguration, string fileNameWithoutExtension)
{
bool result;
if (fileNameWithoutExtension.Length < 5 || fileNameWithoutExtension.Length > propertyConfiguration.IntMinValueLength)
result = false;
else
{
bool skipOneAllAreNumbers = fileNameWithoutExtension[1..].All(char.IsNumber);
result = (skipOneAllAreNumbers && fileNameWithoutExtension[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameWithoutExtension[0]));
}
return result;
}
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();
int key;
string value;
List<char> resultAllInOneSubdirectoryChars = [];
if (hasDateTimeOriginal is null)
{
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
{
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--)
_ = stringBuilder.Append(value[i]);
for (int i = value.Length - propertyConfiguration.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
resultAllInOneSubdirectoryChars.Add(value[i]);
result = $"{stringBuilder}{string.Join(string.Empty, resultAllInOneSubdirectoryChars)}{key}";
return result;
}
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, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
int check = GetId(propertyConfiguration, intelligentId);
if (check != id)
throw new NotSupportedException();
result = index is null || propertyConfiguration.Offset == IId.DeterministicHashCode ? intelligentId : $"{propertyConfiguration.Offset + index}{intelligentId}";
}
return result;
}
} }

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();
}
}

Some files were not shown because too many files have changed in this diff Show More