23 Commits

Author SHA1 Message Date
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
ee0219f321 Using PersonContainer for PersonDisplayDirectoryName 2024-12-29 11:51:08 -07:00
75cfb2a0d9 Alignment with AA 2024-12-28 19:36:44 -07:00
17be39bef9 Updated Offset date to update images from new camera 2024-12-08 19:05:54 -07:00
c085ac3f76 Random with Immich and IsArchive 2024-11-24 15:47:19 -07:00
a6b3318eec net9.0 2024-11-23 21:37:12 -07:00
49e9daea8f IgnoreExtensions 2024-11-09 09:38:28 -07:00
e2e6e15ea2 d8013da9 2024-11-09 09:38:20 -07:00
d8013da912 ResultContentCollection
ValidVideoFormatExtensions
2024-11-03 10:33:56 -07:00
4da353d150 Season breakout
DirectoryName to DirectoryFullPath
2024-11-02 14:24:28 -07:00
444e7ed71a Fast Forward Moving Picture Experts Group 2024-10-30 17:16:52 -07:00
99198cc378 Small changes 2024-10-20 20:12:05 -07:00
e532c3ef1e FaceFile for D2 2024-10-20 17:49:11 -07:00
c580c7eaa4 Identifier new properties 2024-10-14 08:59:26 -07:00
43b66f01b6 Bump 2024-10-14 08:59:18 -07:00
304b5e2a0e Patch but not understood 2024-08-31 09:31:03 -07:00
326e579d5c Runs but broken 2024-08-31 08:32:06 -07:00
161 changed files with 8719 additions and 6400 deletions

View File

@ -82,33 +82,60 @@ csharp_style_var_elsewhere = false:warning
csharp_style_var_for_built_in_types = false:warning
csharp_style_var_when_type_is_apparent = false:warning
csharp_using_directive_placement = outside_namespace
dotnet_analyzer_diagnostic.category-Design.severity = error
dotnet_analyzer_diagnostic.category-Documentation.severity = error
dotnet_analyzer_diagnostic.category-Globalization.severity = none
dotnet_analyzer_diagnostic.category-Interoperability.severity = error
dotnet_analyzer_diagnostic.category-Maintainability.severity = error
dotnet_analyzer_diagnostic.category-Naming.severity = none
dotnet_analyzer_diagnostic.category-Performance.severity = none
dotnet_analyzer_diagnostic.category-Reliability.severity = error
dotnet_analyzer_diagnostic.category-Security.severity = error
dotnet_analyzer_diagnostic.category-SingleFile.severity = error
dotnet_analyzer_diagnostic.category-Style.severity = error
dotnet_analyzer_diagnostic.category-Usage.severity = error
dotnet_code_quality_unused_parameters = all
dotnet_code_quality_unused_parameters = non_public # IDE0060: Remove unused parameter
dotnet_code_quality.CAXXXX.api_surface = private, internal
dotnet_diagnostic.CA1001.severity = none # CA1001: Types that own disposable fields should be disposable
dotnet_diagnostic.CA1051.severity = none # CA1051: Do not declare visible instance fields
dotnet_diagnostic.CA1511.severity = warning # CA1511: Use 'ArgumentException.ThrowIfNullOrEmpty' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1511.severity = warning # CA1511: Use 'ArgumentException.ThrowIfNullOrEmpty' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1513.severity = warning # Use 'ObjectDisposedException.ThrowIf' instead of explicitly throwing a new exception instance
dotnet_diagnostic.CA1816.severity = none # CA1816: Call GC.SuppressFinalize correctly
dotnet_diagnostic.CA1825.severity = warning # CA1823: Avoid zero-length array allocations
dotnet_diagnostic.CA1829.severity = warning # CA1829: Use Length/Count property instead of Count() when available
dotnet_diagnostic.CA1834.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
dotnet_diagnostic.CA1854.severity = warning # CA1854: Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' check to avoid double lookup
dotnet_diagnostic.CA1860.severity = warning # CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance
dotnet_diagnostic.CA1861.severity = none # CA1861: Prefer 'static readonly' fields over constant array arguments
dotnet_diagnostic.CA1862.severity = warning # CA1862: Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase'
dotnet_diagnostic.CA1866.severity = none # CA1866: Use 'string.EndsWith(char)' instead of 'string.EndsWith(string)' when you have a string with a single char
dotnet_diagnostic.CA1869.severity = none # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead.
dotnet_diagnostic.CA2201.severity = none # CA2201: Exception type System.NullReferenceException is reserved by the runtime
dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'
dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name
dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2");
dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant.
dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary
dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement (IDE0010)
dotnet_diagnostic.IDE0028.severity = warning # IDE0028: Collection initialization can be simplified
dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031)
dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed
dotnet_diagnostic.IDE0048.severity = none # Parentheses preferences (IDE0047 and IDE0048)
dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references (IDE0049)
dotnet_diagnostic.IDE0051.severity = error # Private member '' is unused [, ]
dotnet_diagnostic.IDE0058.severity = warning # IDE0058: Expression value is never used
dotnet_diagnostic.IDE0060.severity = warning # IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0074.severity = warning # IDE0074: Use compound assignment
dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure (IDE0130)
dotnet_diagnostic.IDE0200.severity = warning # IDE0200: Lambda expression can be removed [Map]
dotnet_diagnostic.IDE0230.severity = warning # IDE0230: Use UTF-8 string literal
dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290)
dotnet_diagnostic.IDE0300.severity = warning # IDE0300: Collection initialization can be simplified
dotnet_diagnostic.IDE0301.severity = warning #IDE0301: Collection initialization can be simplified
dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified
dotnet_diagnostic.JSON002.severity = warning # JSON002: Probable JSON string detected
dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning
dotnet_naming_rule.abstract_method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.abstract_method_should_be_pascal_case.symbols = abstract_method

80
.vscode/launch.json vendored
View File

@ -10,8 +10,8 @@
"name": "Compare",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Compare/bin/Debug/net8.0/win-x64/Compare.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Compare/bin/Debug/net9.0/win-x64/Compare.dll",
"args": [
"s"
],
@ -27,8 +27,8 @@
"name": "Copy-Distinct",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Copy-Distinct/bin/Debug/net8.0/win-x64/Copy-Distinct.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Copy-Distinct/bin/Debug/net9.0/win-x64/Copy-Distinct.dll",
"args": [],
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
@ -42,8 +42,8 @@
"name": "Duplicate-Search",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Duplicate-Search/bin/Debug/net8.0/win-x64/Duplicate-Search.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Duplicate-Search/bin/Debug/net9.0/win-x64/Duplicate-Search.dll",
"args": [
"s"
],
@ -59,8 +59,8 @@
"name": "Date-Group",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Date-Group/bin/Debug/net8.0/win-x64/Date-Group.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Date-Group/bin/Debug/net9.0/win-x64/Date-Group.dll",
"args": [
"s"
],
@ -76,8 +76,8 @@
"name": "Delete-By-Distinct",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Delete-By-Distinct/bin/Debug/net8.0/win-x64/Delete-By-Distinct.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Delete-By-Distinct/bin/Debug/net9.0/win-x64/Delete-By-Distinct.dll",
"args": [
"s"
],
@ -93,8 +93,8 @@
"name": "Delete-By-Relative",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Delete-By-Relative/bin/Debug/net8.0/win-x64/Delete-By-Relative.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Delete-By-Relative/bin/Debug/net9.0/win-x64/Delete-By-Relative.dll",
"args": [
"s"
],
@ -110,8 +110,8 @@
"name": "Drag-Drop",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Drag-Drop/bin/Debug/net8.0-windows/win-x64/Drag-Drop.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Drag-Drop/bin/Debug/net9.0-windows/win-x64/Drag-Drop.dll",
"args": [
"s"
],
@ -127,8 +127,8 @@
"name": "Drag-Drop-Explorer",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Drag-Drop-Explorer/bin/Debug/net8.0-windows/win-x64/Drag-Drop-Explorer.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Drag-Drop-Explorer/bin/Debug/net9.0-windows/win-x64/Drag-Drop-Explorer.dll",
"args": [
"s"
],
@ -144,8 +144,8 @@
"name": "Drag-Drop-Move",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Drag-Drop-Move/bin/Debug/net8.0-windows/win-x64/Drag-Drop-Move.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Drag-Drop-Move/bin/Debug/net9.0-windows/win-x64/Drag-Drop-Move.dll",
"args": [
"s"
],
@ -161,8 +161,8 @@
"name": "Drag-Drop-Set-Property-Item",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Drag-Drop-Set-Property-Item/bin/Debug/net8.0-windows/win-x64/Drag-Drop-Set-Property-Item.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Drag-Drop-Set-Property-Item/bin/Debug/net9.0-windows/win-x64/Drag-Drop-Set-Property-Item.dll",
"args": [
"s"
],
@ -178,8 +178,8 @@
"name": "Instance",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Instance/bin/Debug/net8.0/win-x64/Instance.dll",
"preLaunchTask": "buildInstance",
"program": "${workspaceFolder}/Instance/bin/Debug/net9.0/win-x64/Instance.dll",
"args": [
"s"
],
@ -195,8 +195,8 @@
"name": "Mirror-Length",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Mirror-Length/bin/Debug/net8.0/win-x64/Mirror-Length.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Mirror-Length/bin/Debug/net9.0/win-x64/Mirror-Length.dll",
"args": [],
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
@ -210,8 +210,8 @@
"name": "Metadata-Query",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Metadata-Query/bin/Debug/net8.0/win-x64/Metadata-Query.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Metadata-Query/bin/Debug/net9.0/win-x64/Metadata-Query.dll",
"args": [
"s"
],
@ -227,8 +227,8 @@
"name": "Move-By-Id",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Move-By-Id/bin/Debug/net8.0/win-x64/Move-By-Id.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Move-By-Id/bin/Debug/net9.0/win-x64/Move-By-Id.dll",
"args": [
"s"
],
@ -244,8 +244,8 @@
"name": "Not-Copy-Copy",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Not-Copy-Copy/bin/Debug/net8.0/win-x64/Not-Copy-Copy.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Not-Copy-Copy/bin/Debug/net9.0/win-x64/Not-Copy-Copy.dll",
"args": [
"s"
],
@ -261,8 +261,8 @@
"name": "Offset-Date-Time-Original",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Offset-Date-Time-Original/bin/Debug/net8.0/win-x64/Offset-Date-Time-Original.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Offset-Date-Time-Original/bin/Debug/net9.0/win-x64/Offset-Date-Time-Original.dll",
"args": [
"s"
],
@ -278,8 +278,8 @@
"name": "PrepareForOld",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/PrepareForOld/bin/Debug/net8.0/win-x64/PrepareForOld.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/PrepareForOld/bin/Debug/net9.0/win-x64/PrepareForOld.dll",
"args": [
"s"
],
@ -295,8 +295,8 @@
"name": "Person",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Person/bin/Debug/net8.0/Person.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Person/bin/Debug/net9.0/Person.dll",
"args": [
"s"
],
@ -312,8 +312,8 @@
"name": "Rename",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Rename/bin/Debug/net8.0/win-x64/Rename.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Rename/bin/Debug/net9.0/win-x64/Rename.dll",
"args": [
"s"
],
@ -329,8 +329,8 @@
"name": "Set-Created-Date",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/Set-Created-Date/bin/Debug/net8.0/win-x64/Set-Created-Date.dll",
"preLaunchTask": "buildSolution",
"program": "${workspaceFolder}/Set-Created-Date/bin/Debug/net9.0/win-x64/Set-Created-Date.dll",
"args": [
"s"
],

5
.vscode/mklink.md vendored
View File

@ -13,3 +13,8 @@ mklink /J "D:\1-Images-A\Images-4083e56a-Results\A2)People\4083e56a\{}\!" "D:\1-
```bash
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"
```

View File

@ -23,6 +23,9 @@
"Immich",
"jfif",
"JOSN",
"makernote",
"Makernote",
"Makernotes",
"mmod",
"Nicéphore",
"Niépce",
@ -32,6 +35,8 @@
"permyriad",
"Phares",
"Phgtv",
"photoshop",
"Photoshop",
"RDHC",
"Rects",
"resnet",

52
.vscode/tasks.json vendored
View File

@ -53,7 +53,7 @@
"problemMatcher": "$msCompile"
},
{
"label": "build",
"label": "buildSolution",
"command": "dotnet",
"type": "process",
"args": [
@ -63,6 +63,56 @@
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "buildInstance",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Instance/Instance.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "buildShared",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Shared/View-by-Distance.Shared.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "buildMetadata",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Metadata/Metadata.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

@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>BlurHash.Core</PackageId>
@ -18,6 +18,6 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Memory" Version="4.6.0" />
</ItemGroup>
</Project>

View File

@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@ -24,6 +24,6 @@
<ProjectReference Include="..\BlurHash.Core\BlurHash.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
</ItemGroup>
</Project>

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.BlurHash</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -33,8 +33,8 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<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" />

View File

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

View File

@ -160,7 +160,7 @@ public class Compare
}
if (_IsEnvironment.Development && propertyConfiguration.PopulatePropertyId)
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())
throw new Exception();
if (propertyConfiguration.PopulatePropertyId && Shared.Models.Stateless.Methods.IProperty.Any(containers))
@ -211,7 +211,7 @@ public class Compare
}
_Logger?.LogInformation(". . .");
}
string aPropertyContentCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(A_Property), "[()]");
string aPropertyContentCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(A_Property), propertyConfiguration.ResultContentCollection);
ThirdPassToMove(propertyConfiguration, mapLogic, propertyLogic, containers, aPropertyContentCollectionDirectory);
if (!isSilent)
{
@ -242,6 +242,53 @@ public class Compare
_ = 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)
{
string result;
@ -376,12 +423,6 @@ public class Compare
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)
{
long result;
@ -391,121 +432,6 @@ public class Compare
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)
{
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)
{
if (_Configuration?.PropertyConfiguration is null)
@ -865,4 +570,299 @@ public class Compare
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

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Compare</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -33,14 +33,14 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<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,51 @@
<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" />
</ItemGroup>
</Project>

View File

@ -1,8 +1,10 @@
using System.Collections.ObjectModel;
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, List<Item> Items)
public record Container(string SourceDirectory, ReadOnlyCollection<Item> Items)
{
public override string ToString()

View File

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

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) =>
Container.GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection);
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) =>
GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection);
}

View File

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>d862524f-2b48-4f47-b4c3-5a8615814ec2</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Copy.Distinct</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -35,9 +35,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />

View File

@ -18,8 +18,8 @@ public class CopyDistinct
private readonly Configuration _Configuration;
private readonly IsEnvironment _IsEnvironment;
private readonly IConfigurationRoot _ConfigurationRoot;
private readonly ReadOnlyDictionary<string, string[]> _FileGroups;
private readonly Property.Models.Configuration _PropertyConfiguration;
private readonly ReadOnlyDictionary<string, 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)
{
@ -179,8 +179,10 @@ public class CopyDistinct
{
progressBar = new(count, message, options);
string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey;
string[] directories = _FileGroups[key];
(distinctDirectories, toDoCollection) = IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, _AppSettings.IfCanUseId, filesCollection, directories, () => progressBar.Tick());
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)

View File

@ -34,17 +34,15 @@ public class Configuration
}
}
private static Models.Configuration Get(Configuration? configuration)
private static Models.Configuration Get(Configuration? configuration, Property.Models.Configuration propertyConfiguration)
{
Models.Configuration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.PersonBirthdayFormat is null) throw new NullReferenceException(nameof(configuration.PersonBirthdayFormat));
if (configuration.PropertyConfiguration is null) throw new NullReferenceException(nameof(configuration.PropertyConfiguration));
result = new(
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat,
configuration.PropertyConfiguration);
result = new(propertyConfiguration,
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat);
return result;
}
@ -66,7 +64,7 @@ public class Configuration
#pragma warning restore IL3050, IL2026
}
PreVerify(configurationRoot, configuration);
result = Get(configuration);
result = Get(configuration, propertyConfiguration);
return result;
}

View File

@ -13,10 +13,9 @@ public class Configuration
public Property.Models.Configuration PropertyConfiguration => _PropertyConfiguration;
[JsonConstructor]
public Configuration(
string[] ignoreExtensions,
string personBirthdayFormat,
Property.Models.Configuration propertyConfiguration)
public Configuration(Property.Models.Configuration propertyConfiguration,
string[] ignoreExtensions,
string personBirthdayFormat)
{
IgnoreExtensions = ignoreExtensions;
PersonBirthdayFormat = personBirthdayFormat;

View File

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>8004d966-1a9e-4545-a220-83f32b6a13e9</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Date.Group</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -34,15 +34,15 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<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" />

View File

@ -57,11 +57,11 @@ public class DateGroup
string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}");
if (!Directory.Exists(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);
if (propertyLogic.ExceptionsDirectories.Count != 0)
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);
if (propertyLogic.ExceptionsDirectories.Count != 0)
@ -251,11 +251,11 @@ public class DateGroup
if (topDirectoryName.Length > 1)
_ = destinationDirectoryName.Append(topDirectoryName);
if (_Configuration.BySeason)
directoryNames.AddRange(new string[] { $"{destinationDirectoryName} {year} {seasonName}" });
directoryNames.AddRange([$"{destinationDirectoryName} {year} {seasonName}"]);
else if (_Configuration.ByDay)
directoryNames.AddRange(new string[] { $"{destinationDirectoryName} {year}", $"{weekOfYear}) {year}-{day}" });
directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year}-{day}"]);
else if (_Configuration.ByWeek)
directoryNames.AddRange(new string[] { $"{destinationDirectoryName} {year}", $"{weekOfYear}) {year} {month}" });
directoryNames.AddRange([$"{destinationDirectoryName} {year}", $"{weekOfYear}) {year} {month}"]);
else
throw new Exception();
}
@ -302,7 +302,7 @@ public class DateGroup
return result;
}
private static Item[] GetFilterItems(Container container)
private static Item[] GetFilterItems(Container.Models.Container container)
{
List<Item> results = [];
foreach (Item item in container.Items)
@ -313,7 +313,7 @@ public class DateGroup
return results.ToArray();
}
private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container[] containers)
private (Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] GetFileMoveCollectionAll(Property.Models.Configuration configuration, string destinationRoot, Container.Models.Container[] containers)
{
(Item Item, long LastWriteTimeTicks, long MinimumDateTimeTicks, string[] Destination)[] results;
Item[] filteredItems;
@ -322,7 +322,7 @@ public class DateGroup
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)
foreach (Container.Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
@ -352,7 +352,7 @@ public class DateGroup
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;
bool hasDuplicate;
@ -422,7 +422,7 @@ public class DateGroup
{
try
{
windowsShortcut = new() { Path = item.FilePath.DirectoryName, Description = item.FilePath.Name };
windowsShortcut = new() { Path = item.FilePath.DirectoryFullPath, Description = item.FilePath.Name };
windowsShortcut.Save(string.Concat(fullFileName, ".lnk"));
windowsShortcut.Dispose();
}
@ -453,7 +453,7 @@ public class DateGroup
_ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(configuration.RootDirectory);
}
private static void CreateDateShortcut(Property.Models.Configuration configuration, Container[] containers)
private static void CreateDateShortcut(Property.Models.Configuration configuration, Container.Models.Container[] containers)
{
string path;
string fileName;
@ -468,17 +468,17 @@ public class DateGroup
WindowsShortcut windowsShortcut;
TimeSpan threeStandardDeviationHigh;
string aPropertyContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "()");
foreach (Container container in containers)
foreach (Container.Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
selectedTotal = 0;
threeStandardDeviationHigh = Shared.Models.Stateless.Methods.IProperty.GetThreeStandardDeviationHigh(minimum, container);
threeStandardDeviationHigh = Property.Models.Stateless.IProperty.GetThreeStandardDeviationHigh(minimum, container);
if (threeStandardDeviationHigh.TotalHours > maximumHours)
threeStandardDeviationHigh = new(maximumHours, 0, 0);
for (int i = 0; i < container.Items.Count; i++)
{
(i, dateTimes, selectedItems) = Shared.Models.Stateless.Methods.IProperty.Get(container, threeStandardDeviationHigh, i);
(i, dateTimes, selectedItems) = Property.Models.Stateless.IProperty.Get(container, threeStandardDeviationHigh, i);
selectedTotal += selectedItems.Count;
foreach (Item item in selectedItems)
{

View File

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>0589ecff-b296-48be-a3f7-7bf27f453975</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Delete.By.Distinct</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -34,14 +34,14 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />

View File

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>ead2e5ee-5f40-4151-bdb6-31d630d94f28</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Delete.By.Relative</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -34,14 +34,14 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Distance</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -36,7 +36,7 @@
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />

View File

@ -14,24 +14,26 @@ public class DistanceLimits : IDistanceLimits
public double FaceDistancePermyriad { init; get; }
public int SortingMaximumPerFaceShouldBeHigh { init; get; }
public bool RangeDaysDeltaTargetLessThenUpper { init; get; }
public double RangeDistanceToleranceUpperLimit { init; get; }
public DistanceLimits(int faceAreaPermyriad,
int faceConfidencePercent,
int faceDistancePermyriad,
int[] rangeDaysDeltaTolerance,
float[] rangeDistanceTolerance,
float[] rangeFaceAreaPermyriadTolerance,
float[] rangeFaceAreaTolerance,
float[] rangeFaceConfidence,
int sortingMaximumPerFaceShouldBeHigh,
int? useFiltersCounter = null)
{
RangeDistanceToleranceUpperLimit = rangeDistanceTolerance[2];
SortingMaximumPerFaceShouldBeHigh = sortingMaximumPerFaceShouldBeHigh;
RangeDaysDeltaTargetLessThenUpper = rangeDaysDeltaTolerance[1] > rangeDaysDeltaTolerance[2];
if (useFiltersCounter is null)
{
RangeDaysDeltaTolerance = rangeDaysDeltaTolerance[1];
FaceConfidencePercent = faceConfidencePercent * rangeFaceConfidence[1];
FaceAreaPermyriad = faceAreaPermyriad * rangeFaceAreaPermyriadTolerance[1];
FaceAreaPermyriad = faceAreaPermyriad * rangeFaceAreaTolerance[1];
FaceDistancePermyriad = faceDistancePermyriad * rangeDistanceTolerance[1];
}
else
@ -39,7 +41,7 @@ public class DistanceLimits : IDistanceLimits
RangeDaysDeltaTolerance = ((rangeDaysDeltaTolerance[2] - rangeDaysDeltaTolerance[0]) * 0.01 * useFiltersCounter.Value) + rangeDaysDeltaTolerance[1];
FaceConfidencePercent = faceConfidencePercent * ((rangeFaceConfidence[2] - rangeFaceConfidence[0]) * 0.01 * useFiltersCounter.Value) + rangeFaceConfidence[1];
FaceDistancePermyriad = faceDistancePermyriad * ((rangeDistanceTolerance[2] - rangeDistanceTolerance[0]) * 0.01 * useFiltersCounter.Value) + rangeDistanceTolerance[1];
FaceAreaPermyriad = faceAreaPermyriad * ((rangeFaceAreaPermyriadTolerance[2] - rangeFaceAreaPermyriadTolerance[0]) * 0.01 * useFiltersCounter.Value) + rangeFaceAreaPermyriadTolerance[1];
FaceAreaPermyriad = faceAreaPermyriad * ((rangeFaceAreaTolerance[2] - rangeFaceAreaTolerance[0]) * 0.01 * useFiltersCounter.Value) + rangeFaceAreaTolerance[1];
}
}

View File

@ -1,10 +1,13 @@
using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Data;
using System.Text.Json;
using View_by_Distance.FaceRecognitionDotNet;
using View_by_Distance.Map.Models;
using View_by_Distance.Property.Models.Stateless;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Methods;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Distance.Models;
@ -50,6 +53,7 @@ public partial class E_Distance : IDistance
private FaceDistanceContainer[] GetFaceDistanceContainers(MappingFromItem mappingFromItem, List<Face> intersectFaces)
{
FaceDistanceContainer[] results;
DateTime dateTime;
int wholePercentages;
int confidencePercent;
FaceDistance faceDistance;
@ -61,14 +65,15 @@ public partial class E_Distance : IDistance
throw new NotSupportedException();
if (face.Mapping?.MappingFromFilterPost is null)
throw new NotSupportedException();
dateTime = mappingFromItem.GetDateTimeOriginalThenMinimumDateTime();
confidencePercent = Shared.Models.Stateless.Methods.ILocation.GetConfidencePercent(_FaceConfidencePercent, face.Location.Confidence);
wholePercentages = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
if (face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding faceEncoding)
faceDistance = new(confidencePercent, mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping?.MappingFromFilterPost, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages);
faceDistance = new(confidencePercent, dateTime, faceEncoding, face.Mapping?.MappingFromFilterPost, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages);
else
{
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
faceDistance = new(confidencePercent, mappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping?.MappingFromFilterPost, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages);
faceDistance = new(confidencePercent, dateTime, faceEncoding, face.Mapping?.MappingFromFilterPost, mappingFromItem.Id, mappingFromItem.IsWrongYear, wholePercentages);
lock (intersectFaces)
face.SetFaceDistance(faceDistance);
}
@ -91,7 +96,7 @@ public partial class E_Distance : IDistance
return new(faceDistanceEncodings);
}
private List<(Face Face, double? Length)> GetValues(MappingFromItem mappingFromItem, List<Face> intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding)
private List<(Face Face, double? Length)> GetValues(IDistanceLimits distanceLimits, MappingFromItem mappingFromItem, List<Face> intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding)
{
List<(Face Face, double? Length)> results = [];
Face face;
@ -112,6 +117,8 @@ public partial class E_Distance : IDistance
{
face = intersectFaces[i];
faceDistanceLength = faceDistanceLengths[i];
if (faceDistanceLength.Length is null || faceDistanceLength.Length > distanceLimits.RangeDistanceToleranceUpperLimit)
continue;
if (faceDistanceLength.Length is null)
throw new NotSupportedException();
results.Add(new(face, faceDistanceLength.Length.Value));
@ -119,10 +126,10 @@ public partial class E_Distance : IDistance
return results;
}
private (Face, double?)[] GetClosestFaceByDistanceIgnoringTolerance(MappingFromItem mappingFromItem, List<Face> intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding)
private (Face, double?)[] GetClosestFaceByDistanceIgnoringTolerance(IDistanceLimits distanceLimits, MappingFromItem mappingFromItem, List<Face> intersectFaces, Shared.Models.FaceEncoding modelsFaceEncoding)
{
(Face, double?)[] results;
List<(Face Face, double? Length)> collection = GetValues(mappingFromItem, intersectFaces, modelsFaceEncoding);
List<(Face Face, double? Length)> collection = GetValues(distanceLimits, mappingFromItem, intersectFaces, modelsFaceEncoding);
results = (from l in collection where l.Length < _RangeDistanceToleranceAverage orderby l.Length select l).Take(1).ToArray();
if (results.Length > 0)
{
@ -132,11 +139,11 @@ public partial class E_Distance : IDistance
return results;
}
private static List<(Face, double?)> GetMatchingFacesByFaceEncoding(Face[] filteredFaces, string? json)
private static List<(Face, double?)> GetMatchingFacesByFaceEncoding(List<Face> faces, string? json)
{
List<(Face, double?)> results = [];
string check;
foreach (Face face in filteredFaces)
foreach (Face face in faces)
{
if (json is null || face.FaceEncoding is null)
continue;
@ -165,7 +172,7 @@ public partial class E_Distance : IDistance
mappedFaceDirectory = Path.GetDirectoryName(file);
if (mappedFaceDirectory is null)
throw new NotSupportedException();
deterministicHashCodeKey = Shared.Models.Stateless.Methods.IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(filePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
checkFile = Path.Combine(mappedFaceDirectory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{facesFileNameExtension}");
if (checkFile == file)
continue;
@ -201,17 +208,14 @@ public partial class E_Distance : IDistance
}
}
public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, FilePath filePath, MappingFromItem mappingFromItem, List<Face> faces, ReadOnlyCollection<LocationContainer> locationContainers)
public void LookForMatchFacesAndPossiblyRename(bool overrideForFaceImages, IDistanceLimits distanceLimits, IFaceD dFace, FilePath filePath, MappingFromItem mappingFromItem, ExifDirectory exifDirectory, List<Face> faces, ReadOnlyCollection<LocationContainer> locationContainers)
{
string? json;
string[] matches;
FileInfo? fileInfo;
List<Face> intersectFaces;
List<(Face, double?)> checkFaces = [];
Shared.Models.FaceEncoding? modelsFaceEncoding;
Face[] filteredFaces = (from l in faces where l.FaceEncoding is not null && l.Location is not null && l.OutputResolution is not null select l).ToArray();
if (filteredFaces.Length != faces.Count)
checkFaces.Clear();
List<(Face Face, double? Distance)> checkFaces = [];
foreach (LocationContainer locationContainer in locationContainers)
{
if (_Renamed.Contains(locationContainer.FilePath.FullName))
@ -232,8 +236,8 @@ public partial class E_Distance : IDistance
MoveUnableToMatch(locationContainer.FilePath);
continue;
}
if (filteredFaces.Length > 0)
checkFaces.AddRange(GetMatchingFacesByFaceEncoding(filteredFaces, json));
if (faces.Count > 0)
checkFaces.AddRange(GetMatchingFacesByFaceEncoding(faces, json));
if (checkFaces.Count == 1)
_Debug.Add(0);
if (checkFaces.Count != 1 && !string.IsNullOrEmpty(json))
@ -242,11 +246,11 @@ public partial class E_Distance : IDistance
modelsFaceEncoding = JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
if (modelsFaceEncoding is null)
throw new NotSupportedException();
if (filteredFaces.Length > 0)
if (faces.Count > 0)
{
intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(filteredFaces, _RectangleIntersectMinimum, locationContainer.WholePercentages);
intersectFaces = Shared.Models.Stateless.Methods.ILocation.FilterByIntersect(faces, _RectangleIntersectMinimum, locationContainer.WholePercentages);
if (intersectFaces.Count > 0)
checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(mappingFromItem, intersectFaces, modelsFaceEncoding));
checkFaces.AddRange(GetClosestFaceByDistanceIgnoringTolerance(distanceLimits, mappingFromItem, intersectFaces, modelsFaceEncoding));
}
}
if (checkFaces.Count == 0)
@ -261,7 +265,7 @@ public partial class E_Distance : IDistance
MoveUnableToMatch(locationContainer.FilePath);
continue;
}
fileInfo = CheckFileThenGetFileInfo(facesFileNameExtension, filePath, mappingFromItem, locationContainer.FilePath.FullName, checkFaces);
fileInfo = CheckFileThenGetFileInfo(dFace.FileNameExtension, filePath, mappingFromItem, locationContainer.FilePath.FullName, checkFaces);
if (fileInfo is not null)
{
if (_DistanceRenameToMatch && fileInfo is not null)
@ -275,6 +279,15 @@ public partial class E_Distance : IDistance
}
continue;
}
if (overrideForFaceImages)
{
json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(locationContainer.ExifDirectory);
if (json is null || !json.Contains(nameof(DateTime)))
{
if (checkFaces.Count == 1)
dFace.ReSaveFace(exifDirectory, locationContainer.FilePath, checkFaces[0].Face, mappedFile: true);
}
}
if (_AllMappedFaceFileNames.Contains(locationContainer.FilePath.Name))
{
lock (_AllMappedFaceFiles)
@ -322,10 +335,88 @@ public partial class E_Distance : IDistance
File.WriteAllLines(eDistanceContentFileName, results);
}
public static void PreFilterSetFaceDistances(int maxDegreeOfParallelism, long ticks, ReadOnlyCollection<Face> distinctFilteredFaces)
public static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMappedWithEncoding(ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mapped)
{
Dictionary<int, ReadOnlyDictionary<int, LocationContainer>> results = [];
string? json;
LocationContainer? locationContainer;
Shared.Models.FaceEncoding? faceEncoding;
FaceRecognitionDotNet.FaceEncoding? encoding;
Dictionary<int, LocationContainer> keyValuePairs;
foreach (KeyValuePair<int, ReadOnlyDictionary<int, LocationContainer>> keyValuePair in mapped)
{
keyValuePairs = [];
foreach (KeyValuePair<int, LocationContainer> keyValue in keyValuePair.Value)
{
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(keyValue.Value.ExifDirectory);
faceEncoding = json is null ? null : JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
if (faceEncoding is null)
continue;
encoding = FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding);
locationContainer = LocationContainer.Get(keyValue.Value, encoding, keepExifDirectory: false);
keyValuePairs.Add(keyValue.Key, locationContainer);
}
results.Add(keyValuePair.Key, new(keyValuePairs));
}
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)
{
List<LocationContainer> results = [];
string? json;
string? model;
bool? canReMap;
bool? isFocusPerson;
bool? inSkipCollection;
Shared.Models.FaceEncoding? faceEncoding;
FaceRecognitionDotNet.FaceEncoding? encoding;
ReadOnlyDictionary<int, LocationContainer>? keyValuePairs;
ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers;
foreach (LocationContainer locationContainer in available)
{
if (mapped.TryGetValue(locationContainer.Id, out keyValuePairs))
{
if (keyValuePairs.ContainsKey(locationContainer.WholePercentages))
continue;
}
if (locationContainer.ExifDirectory is null || locationContainer.FaceFile is null)
continue;
inSkipCollection = mapLogic.InSkipCollection(locationContainer.Id, locationContainer.WholePercentages);
if (inSkipCollection is not null && inSkipCollection.Value)
continue;
wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(locationContainer.Id);
canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, locationContainer.WholePercentages);
if (canReMap is not null && !canReMap.Value)
continue;
isFocusPerson = mapLogic.IsFocusPerson(skipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, locationContainer.WholePercentages);
if (isFocusPerson is not null && !isFocusPerson.Value)
continue;
if (!string.IsNullOrEmpty(focusModel))
{
model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(locationContainer.ExifDirectory);
if (string.IsNullOrEmpty(model) || !model.Contains(focusModel))
continue;
}
if (!string.IsNullOrEmpty(focusDirectory))
{
if (!locationContainer.FilePath.DirectoryFullPath.Contains(focusDirectory))
continue;
}
json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory);
faceEncoding = json is null ? null : JsonSerializer.Deserialize<Shared.Models.FaceEncoding>(json);
if (faceEncoding is null)
continue;
encoding = FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding);
results.Add(LocationContainer.Get(locationContainer, encoding, keepExifDirectory: false));
}
return results;
}
public static void PreFilterSetFaceDistances(int maxDegreeOfParallelism, Configuration configuration, long ticks, ReadOnlyCollection<Face> distinctValidImageFaces)
{
List<Face> faces = [];
foreach (Face face in distinctFilteredFaces)
foreach (Face face in distinctValidImageFaces)
{
if (face.Mapping?.MappingFromFilterPre is null)
throw new NotSupportedException();
@ -335,8 +426,10 @@ public partial class E_Distance : IDistance
continue;
if (face.Mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !face.Mapping.MappingFromFilterPre.IsFocusRelativePath.Value)
continue;
if (face.FaceEncoding is not null && face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding _)
continue;
// if (!configuration.ReMap && face.Mapping.MappingFromPerson is not null)
// continue;
if (!configuration.ReMap && face.FaceEncoding is not null && face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding)
throw new NotSupportedException($"{face.FaceEncoding} should not be null!");
faces.Add(face);
}
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
@ -352,18 +445,19 @@ public partial class E_Distance : IDistance
throw new NotSupportedException();
progressBar.Tick();
faceEncoding = FaceRecognition.LoadFaceEncoding(face.FaceEncoding.RawEncoding);
FaceDistance faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping.MappingFromFilterPost, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages);
DateTime dateTime = face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime();
FaceDistance faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, dateTime, faceEncoding, face.Mapping.MappingFromFilterPost, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages);
lock (face)
face.SetFaceDistance(faceDistance);
});
}
private static List<SortingContainer> GetSortingContainers(Map.Models.Configuration mapConfiguration, IDistanceLimits distanceLimits, Face face, FaceDistance faceDistanceEncoding, List<Sorting> sortingCollection)
private static List<SortingContainer> GetSortingContainers(Configuration mapConfiguration, IDistanceLimits distanceLimits, Face face, FaceDistance faceDistanceEncoding, List<Sorting> sortingCollection)
{
List<SortingContainer> results = [];
SortingContainer sortingContainer;
int days = 0, distance = 0;
Sorting[] collection = Shared.Models.Stateless.Methods.ISorting.Sort(sortingCollection);
SortingContainer sortingContainer;
Sorting[] collection = ISorting.Sort(sortingCollection);
foreach (Sorting sorting in collection)
{
if (face.Mapping?.MappingFromLocation is null || faceDistanceEncoding.WholePercentages is null)
@ -389,7 +483,7 @@ public partial class E_Distance : IDistance
return results;
}
private static List<Sorting> GetSortingCollection(Map.Models.MapLogic mapLogic, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, int i, Face face, FaceDistance faceDistanceEncoding)
private static List<Sorting> GetSortingCollection(MapLogic mapLogic, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, int i, Face face, FaceDistance faceDistanceEncoding)
{
List<Sorting> results;
List<FaceDistance> faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncoding);
@ -397,27 +491,45 @@ public partial class E_Distance : IDistance
return results;
}
public static ReadOnlyCollection<FaceDistanceContainer> GetFaceDistanceContainers(ReadOnlyCollection<Face> distinctFilteredFaces)
public static ReadOnlyCollection<FaceDistanceContainer> GetFaceDistanceContainers(ReadOnlyCollection<Face> distinctValidImageFaces)
{
ReadOnlyCollection<FaceDistanceContainer> results;
DateTime dateTime;
FaceDistance faceDistance;
FaceDistanceContainer faceDistanceContainer;
List<FaceDistanceContainer> collection = [];
foreach (Face face in distinctFilteredFaces)
foreach (Face face in distinctValidImageFaces)
{
if (face.Mapping?.MappingFromLocation is null)
throw new NotSupportedException();
if (face.FaceDistance?.Encoding is not FaceRecognitionDotNet.FaceEncoding faceEncoding)
continue;
faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime(), faceEncoding, face.Mapping.MappingFromFilterPost, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages);
dateTime = face.Mapping.MappingFromItem.GetDateTimeOriginalThenMinimumDateTime();
faceDistance = new(face.Mapping.MappingFromLocation.ConfidencePercent, dateTime, faceEncoding, face.Mapping.MappingFromFilterPost, face.Mapping.MappingFromItem.Id, face.Mapping.MappingFromItem.IsWrongYear, face.Mapping.MappingFromLocation.WholePercentages);
faceDistanceContainer = new(face, faceDistance);
collection.Add(faceDistanceContainer);
}
results = new(collection.ToArray());
results = collection.AsReadOnly();
return results;
}
public static FaceDistanceContainer[] FilteredPostLoadFaceDistanceContainers(Map.Models.MapLogic mapLogic, ReadOnlyCollection<FaceDistanceContainer> faceDistanceContainers, long? skipOlderThan, DistanceLimits distanceLimits)
public static List<LocationContainer> GetPostFilterLocationContainer(MapLogic mapLogic, List<LocationContainer> preFiltered, DistanceLimits distanceLimits)
{
List<LocationContainer> results = [];
foreach (LocationContainer locationContainer in preFiltered)
{
if (locationContainer.FaceFile is null)
continue;
if (locationContainer.FaceFile.AreaPermyriad < distanceLimits.FaceAreaPermyriad)
continue;
if (locationContainer.FaceFile.ConfidencePercent < distanceLimits.FaceConfidencePercent)
continue;
results.Add(locationContainer);
}
return results;
}
public static FaceDistanceContainer[] FilteredPostLoadFaceDistanceContainers(MapLogic mapLogic, ReadOnlyCollection<FaceDistanceContainer> faceDistanceContainers, long? skipOlderThan, DistanceLimits distanceLimits)
{
List<FaceDistanceContainer> results = [];
foreach (FaceDistanceContainer faceDistanceContainer in faceDistanceContainers)
@ -446,7 +558,46 @@ public partial class E_Distance : IDistance
return results.ToArray();
}
public static ReadOnlyCollection<SortingContainer> SetFaceMappingSortingCollectionThenGetSortedSortingContainers(int maxDegreeOfParallelism, Map.Models.Configuration mapConfiguration, long ticks, Map.Models.MapLogic mapLogic, IDistanceLimits distanceLimits, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, FaceDistanceContainer[] filteredFaceDistanceContainers)
private static ReadOnlyCollection<LocationContainer> GetReadOnlyLocationContainer(ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedWithEncoding, List<LocationContainer> postFiltered)
{
List<LocationContainer> results = [];
foreach (LocationContainer locationContainer in postFiltered)
results.Add(locationContainer);
foreach (KeyValuePair<int, ReadOnlyDictionary<int, LocationContainer>> keyValuePair in mappedWithEncoding)
{
foreach (KeyValuePair<int, LocationContainer> keyValue in keyValuePair.Value)
results.Add(keyValue.Value);
}
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)
{
List<LocationContainer> results = [];
ReadOnlyCollection<LocationContainer> locationContainers;
ReadOnlyCollection<LocationContainer> readOnlyLocationContainers = GetReadOnlyLocationContainer(mappedWithEncoding, postFiltered);
foreach (LocationContainer locationContainer in postFiltered)
{
dlibDotNet.Tick();
locationContainers = FaceRecognition.GetLocationContainers(mapConfiguration.FaceDistancePermyriad, readOnlyLocationContainers, locationContainer);
foreach (LocationContainer item in locationContainers)
{
if (item.LengthPermyriad is null)
continue;
if (item.LengthPermyriad > distanceLimits.FaceDistancePermyriad)
break;
if (!mapConfiguration.SaveSortingWithoutPerson && item.PersonKey is null)
continue;
if (item.Id == locationContainer.Id && item.WholePercentages == locationContainer.WholePercentages)
continue;
results.Add(item);
}
}
LocationContainer[] array = results.OrderBy(l => l.LengthPermyriad).ToArray();
return new(array);
}
public static ReadOnlyCollection<SortingContainer> SetFaceMappingSortingCollectionThenGetSortedSortingContainers(int maxDegreeOfParallelism, Configuration mapConfiguration, long ticks, MapLogic mapLogic, IDistanceLimits distanceLimits, ReadOnlyCollection<FaceDistance> faceDistanceEncodings, FaceDistanceContainer[] filteredFaceDistanceContainers)
{
List<SortingContainer> results = [];
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
@ -470,13 +621,13 @@ public partial class E_Distance : IDistance
}
});
if (distanceLimits is not null && distanceLimits.RangeDaysDeltaTargetLessThenUpper)
results = Shared.Models.Stateless.Methods.ISortingContainer.Sort(results);
results = ISortingContainer.Sort(results);
else
results = Shared.Models.Stateless.Methods.ISortingContainer.SortUsingDaysDelta(results);
return new(results);
results = ISortingContainer.SortUsingDaysDelta(results);
return results.AsReadOnly();
}
private static ReadOnlyCollection<RelationContainer> GetRelationCollections(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)
{
List<RelationContainer> results = [];
string fileName;
@ -496,6 +647,7 @@ public partial class E_Distance : IDistance
foreach (Record record in records)
{
mappedRelations = [];
FaceDistance faceDistanceLength;
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(record.FilePath.FullName);
if (files.Count > 1)
{
@ -508,20 +660,22 @@ public partial class E_Distance : IDistance
fileName = Path.GetFileName(files[i]);
if (fileName == fileHolder.Name)
continue;
FaceDistance faceDistance = faceDistanceLengths[i];
if (faceDistance.Length is null || faceDistance.Length.Value > distanceTolerance)
faceDistanceLength = faceDistanceLengths[i];
if (faceDistanceLength.Length is null || faceDistanceLength.Length > distanceLimits.RangeDistanceToleranceUpperLimit)
continue;
distancePermyriad = (int)(faceDistance.Length.Value * faceDistancePermyriad);
if (faceDistanceLength.Length is null || faceDistanceLength.Length.Value > distanceTolerance)
continue;
distancePermyriad = (int)(faceDistanceLength.Length.Value * faceDistancePermyriad);
mappedRelations.Add(new(distancePermyriad, files[i]));
}
}
mappedRelations = (from l in mappedRelations orderby l.DistancePermyriad select l).Take(locationContainerDistanceTake).ToList();
results.Add(new(fileHolder, new(mappedRelations)));
}
return new(results);
return results.AsReadOnly();
}
ReadOnlyCollection<RelationContainer> IDistance.GetRelationContainers(int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection<LocationContainer> locationContainers)
ReadOnlyCollection<RelationContainer> IDistance.GetRelationContainers(IDistanceLimits distanceLimits, int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection<LocationContainer> locationContainers)
{
ReadOnlyCollection<RelationContainer> result;
string? json;
@ -539,7 +693,7 @@ public partial class E_Distance : IDistance
faceRecognitionDotNetFaceEncoding = FaceRecognition.LoadFaceEncoding(modelsFaceEncoding.RawEncoding);
records.Add(new(locationContainer.FilePath, faceRecognitionDotNetFaceEncoding));
}
result = GetRelationCollections(faceDistancePermyriad, locationContainerDistanceTake, locationContainerDistanceTolerance, records);
result = GetRelationCollections(distanceLimits, faceDistancePermyriad, locationContainerDistanceTake, locationContainerDistanceTolerance, records);
return result;
}

View File

@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<OutputType>WinExe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net9.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<PropertyGroup>
@ -26,9 +26,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

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

View File

@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<OutputType>WinExe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net9.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<PropertyGroup>
@ -26,9 +26,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
</ItemGroup>
<ItemGroup>

View File

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

View File

@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<OutputType>WinExe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net9.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<UserSecretsId>7b153e3d-672b-4f7a-888a-cb31645a2439</UserSecretsId>
</PropertyGroup>
@ -27,13 +27,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" />
<ProjectReference Include="..\Container\Container.csproj" />
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Resize\Resize.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -107,9 +107,9 @@ public partial class DragDropSearch : Form
private void LoadData()
{
Container[] containers;
Container.Models.Container[] containers;
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}");
(_, containers) = IContainer.GetContainers(_Configuration.PropertyConfiguration, aPropertySingletonDirectory);
(_, 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)
{
@ -162,8 +162,8 @@ public partial class DragDropSearch : Form
{
Text = item.FilePath.Name;
_TextBox.Text = item.FilePath.FullName;
if (!string.IsNullOrEmpty(item.FilePath.DirectoryName))
_ = Process.Start("explorer.exe", string.Concat("\"", item.FilePath.DirectoryName, "\""));
if (!string.IsNullOrEmpty(item.FilePath.DirectoryFullPath))
_ = Process.Start("explorer.exe", string.Concat("\"", item.FilePath.DirectoryFullPath, "\""));
}
}
}

View File

@ -8,13 +8,11 @@ public class Program
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main()
{
ApplicationConfiguration.Initialize();
private static void Main() =>
// ApplicationConfiguration.Initialize();
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 = [];
foreach (Item item in container.Items)
@ -26,11 +24,11 @@ public class Program
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 = [];
Item[] filteredItems;
foreach (Container container in containers)
foreach (Container.Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;

View File

@ -4,7 +4,7 @@
<Nullable>enable</Nullable>
<OutputType>WinExe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net9.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<UserSecretsId>c64a15ed-0ba3-4378-8f80-0c19d0531747</UserSecretsId>
</PropertyGroup>
@ -27,9 +27,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Metadata\Metadata.csproj" />

View File

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

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Duplicate-Search</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -33,14 +33,14 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />

View File

@ -2,6 +2,7 @@
using Microsoft.Extensions.Logging;
using Phares.Shared;
using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Text;
using System.Text.Json;
using View_by_Distance.Duplicate.Search.Models;
@ -29,7 +30,7 @@ public class DuplicateSearch
else
{
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);
bool argZeroIsConfigurationRootDirectory = configuration.RootDirectory == argZero;
string destinationRoot = Property.Models.Stateless.IResult.GetResultsGroupDirectory(configuration, "Z) Moved");
@ -124,10 +125,10 @@ public class DuplicateSearch
}
}
private static Container[] GetContainers(long ticks, Configuration configuration)
private static Container.Models.Container[] GetContainers(long ticks, Configuration configuration)
{
int f;
Container[] containers;
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 };
@ -135,38 +136,38 @@ public class DuplicateSearch
{
progressBar.Tick();
string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(A_Property), "{}");
(f, containers) = Shared.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory);
(f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration, aPropertySingletonDirectory);
}
return containers;
}
private static Dictionary<int, List<MappingFromItem?>> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, Container[] containers, string destinationRoot, List<int> preloadIds)
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;
Item[] filteredItems;
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)
foreach (Container.Models.Container container in containers)
{
if (container.Items.Count == 0)
continue;
if (!argZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero))
continue;
filteredItems = Shared.Models.Stateless.Methods.IContainer.GetFilterItems(configuration, container);
if (filteredItems.Length == 0)
validImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(configuration, container);
if (validImageItems.Count == 0)
continue;
containerDateTimes = Shared.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems);
foreach (Item item in filteredItems)
containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(validImageItems);
foreach (Item item in validImageItems)
{
if (item.Property?.Id is null)
{
@ -188,7 +189,7 @@ public class DuplicateSearch
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.FilePath, mappingFromItem.IsWrongYear, item.Property.Keywords ?? [], mappingFromItem.MinimumDateTime, item.Property.Model, mappingFromItem.RelativePath, resizedFileHolder);
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));
@ -207,11 +208,11 @@ public class DuplicateSearch
{
foreach (MappingFromItem? mappingFromItem in keyValuePair.Value)
{
if (mappingFromItem?.ResizedFileHolder.DirectoryName is null)
if (mappingFromItem?.ResizedFileHolder.DirectoryFullPath is null)
continue;
if (mappingFromItem.ResizedFileHolder.Exists)
continue;
collection.Add(mappingFromItem.ResizedFileHolder.DirectoryName);
collection.Add(mappingFromItem.ResizedFileHolder.DirectoryFullPath);
results.Add(new(mappingFromItem.FilePath, mappingFromItem.ResizedFileHolder.FullName));
}
}

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Face</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -33,7 +33,7 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />

View File

@ -6,19 +6,20 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using View_by_Distance.FaceRecognitionDotNet;
using View_by_Distance.Metadata.Models;
using View_by_Distance.Metadata.Models.Stateless.Methods;
using View_by_Distance.Property.Models;
using View_by_Distance.Property.Models.Stateless;
using View_by_Distance.Resize.Models;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Face.Models;
/// <summary>
// List<D_Faces>
/// </summary>
public class D_Face
public class D_Face : IFaceD
{
protected readonly string _FileNameExtension;
@ -41,11 +42,12 @@ public class D_Face
private readonly int _FaceDistanceHiddenImageFactor;
private readonly EncoderParameters _EncoderParameters;
private readonly ImageCodecInfo _HiddenImageCodecInfo;
private readonly Dictionary<string, string[]> _FileGroups;
private readonly bool _ForceFaceLastWriteTimeToCreationTime;
private readonly EncoderParameters _HiddenEncoderParameters;
private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly JsonSerializerOptions _WriteIndentedAndWhenWritingNull;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultContentFileGroups;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultCollectionFileGroups;
public D_Face(
string argZero,
@ -68,7 +70,6 @@ public class D_Face
float[] rectangleIntersectMinimums)
{
_ArgZero = argZero;
_FileGroups = [];
_ImageCodecInfo = imageCodecInfo;
_EncoderParameters = encoderParameters;
_FileNameExtension = filenameExtension;
@ -83,6 +84,8 @@ public class D_Face
_RectangleIntersectMinimum = rectangleIntersectMinimums.Min();
_FaceDistanceHiddenImageFactor = faceDistanceHiddenImageFactor;
_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;
@ -92,6 +95,73 @@ public class D_Face
_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()
{
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
@ -100,120 +170,175 @@ public class D_Face
public void Update(string dResultsFullGroupDirectory)
{
_FileGroups.Clear();
ReadOnlyDictionary<string, string[]> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, dResultsFullGroupDirectory, [_PropertyConfiguration.ResultCollection, _PropertyConfiguration.ResultContent]);
foreach (KeyValuePair<string, string[]> keyValuePair in keyValuePairs)
_FileGroups.Add(keyValuePair.Key, keyValuePair.Value);
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, 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)
{
if (keyValuePair.Key == _PropertyConfiguration.ResultContent)
_ResultContentFileGroups[0] = keyValuePair.Value;
else if (keyValuePair.Key == _PropertyConfiguration.ResultCollection)
_ResultCollectionFileGroups[0] = keyValuePair.Value;
else
throw new Exception();
}
}
private static (Model model, PredictorModel predictorModel, ModelParameter modelParameter) GetModel(string modelDirectory, string modelName, string predictorModelName)
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)
{
(Model, PredictorModel, ModelParameter) result;
Array array;
Model? model = null;
PredictorModel? predictorModel = null;
array = Enum.GetValues(typeof(Model));
foreach (Model check in array)
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)
{
if (modelName.Contains(check.ToString()))
save = false;
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
{
model = check;
break;
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 (model is null)
throw new Exception("Destination directory must have Model name!");
model = model.Value;
array = Enum.GetValues(typeof(PredictorModel));
foreach (PredictorModel check in array)
if (results.Any(l => l.Save))
{
if (predictorModelName.Contains(check.ToString()))
{
predictorModel = check;
break;
}
if (!directoryInfo.Exists)
_ = Directory.CreateDirectory(directoryInfo.FullName);
SaveFaces(mappingFromItem.ResizedFileHolder, exifDirectory, results);
}
if (predictorModel is null)
throw new Exception("Destination directory must have Predictor Model name!");
predictorModel = predictorModel.Value;
ModelParameter modelParameter = new()
{
CnnFaceDetectorModel = File.ReadAllBytes(Path.Combine(modelDirectory, "mmod_human_face_detector.dat")),
FaceRecognitionModel = File.ReadAllBytes(Path.Combine(modelDirectory, "dlib_face_recognition_resnet_model_v1.dat")),
PosePredictor5FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_5_face_landmarks.dat")),
PosePredictor68FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_68_face_landmarks.dat"))
};
result = new(model.Value, predictorModel.Value, modelParameter);
return result;
return results;
}
#pragma warning disable CA1416
private void SaveFaces(FileHolder resizedFileHolder, List<(Shared.Models.Face, FileInfo?, string, bool)> collection)
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo)
{
int width;
int height;
Bitmap bitmap;
short type = 2;
Graphics graphics;
Location? location;
Rectangle rectangle;
string locationJson;
string faceEncodingJson;
PropertyItem? propertyItem;
string outputResolutionJson;
using Bitmap source = new(resizedFileHolder.FullName);
int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist;
int fileSource = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagFileSource;
int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment;
foreach ((Shared.Models.Face face, FileInfo? fileInfo, string fileName, bool save) in collection)
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))
{
if (!save)
continue;
if (fileInfo is null)
continue;
if (face.FaceEncoding is null || face?.Location is null || face?.OutputResolution is null)
continue;
location = Shared.Models.Stateless.Methods.ILocation.GetLocation(face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count);
if (location is null)
continue;
width = location.Right - location.Left;
height = location.Bottom - location.Top;
locationJson = JsonSerializer.Serialize(face.Location);
faceEncodingJson = JsonSerializer.Serialize(face.FaceEncoding);
outputResolutionJson = JsonSerializer.Serialize(face.OutputResolution);
rectangle = new Rectangle(location.Left, location.Top, width, height);
using (bitmap = new(width, height))
string checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
{
using (graphics = Graphics.FromImage(bitmap))
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, fileSource, type, locationJson);
bitmap.SetPropertyItem(propertyItem);
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, artist, type, outputResolutionJson);
bitmap.SetPropertyItem(propertyItem);
propertyItem = IProperty.GetPropertyItem(_ConstructorInfo, userComment, type, faceEncodingJson);
bitmap.SetPropertyItem(propertyItem);
bitmap.Save(fileInfo.FullName, _ImageCodecInfo, _EncoderParameters);
File.Move(checkFile, fileInfo.FullName);
fileInfo.Refresh();
}
if (File.Exists(fileName))
File.Delete(fileName);
location = Shared.Models.Stateless.Methods.ILocation.GetLocation(_FaceDistanceHiddenImageFactor, face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count);
if (location is null)
continue;
width = location.Right - location.Left;
height = location.Bottom - location.Top;
rectangle = new Rectangle(location.Left, location.Top, width, height);
using (bitmap = new(width, height))
{
using (graphics = Graphics.FromImage(bitmap))
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
bitmap.Save(fileName, _HiddenImageCodecInfo, _HiddenEncoderParameters);
}
File.SetAttributes(fileName, FileAttributes.Hidden);
}
}
private List<Shared.Models.Face> GetFaces(string outputResolution, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, List<Location>? locations)
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, 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();
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, 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;
}
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));
@ -222,7 +347,24 @@ public class D_Face
List<Shared.Models.Face> results = [];
FaceRecognitionDotNet.Image? unknownImage;
try
{ unknownImage = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName); }
{
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)
@ -258,180 +400,128 @@ public class D_Face
return results;
}
#pragma warning restore CA1416
private static List<LocationContainer> GetLocationContainers(string outputResolution, ReadOnlyCollection<LocationContainer> locationContainers, Dictionary<string, int[]> outputResolutionToResize, List<Shared.Models.Face> faces)
private static (Model model, PredictorModel predictorModel, ModelParameter modelParameter) GetModel(string modelDirectory, string modelName, string predictorModelName)
{
List<LocationContainer> results = [];
string? json;
(Model, PredictorModel, ModelParameter) result;
Array array;
Model? model = null;
array = Enum.GetValues<Model>();
PredictorModel? predictorModel = null;
foreach (Model check in array)
{
if (modelName.Contains(check.ToString()))
{
model = check;
break;
}
}
if (model is null)
throw new Exception("Destination directory must have Model name!");
model = model.Value;
array = Enum.GetValues<PredictorModel>();
foreach (PredictorModel check in array)
{
if (predictorModelName.Contains(check.ToString()))
{
predictorModel = check;
break;
}
}
if (predictorModel is null)
throw new Exception("Destination directory must have Predictor Model name!");
predictorModel = predictorModel.Value;
ModelParameter modelParameter = new()
{
CnnFaceDetectorModel = File.ReadAllBytes(Path.Combine(modelDirectory, "mmod_human_face_detector.dat")),
FaceRecognitionModel = File.ReadAllBytes(Path.Combine(modelDirectory, "dlib_face_recognition_resnet_model_v1.dat")),
PosePredictor5FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_5_face_landmarks.dat")),
PosePredictor68FaceLandmarksModel = File.ReadAllBytes(Path.Combine(modelDirectory, "shape_predictor_68_face_landmarks.dat"))
};
result = new(model.Value, predictorModel.Value, modelParameter);
return result;
}
private void SaveFaces(FileHolder resizedFileHolder, ExifDirectory exifDirectory, List<(Shared.Models.Face, FileHolder?, string, bool)> collection)
{
int width;
int height;
Bitmap bitmap;
short type = 2;
FaceFile faceFile;
Graphics graphics;
Location? location;
Rectangle? rectangle;
List<int> skip = [];
OutputResolution? outputResolutionCheck = null;
(int outputResolutionWidth, int outputResolutionHeight, int outputResolutionOrientation) = Resize.Models.Stateless.Methods.IResize.Get(outputResolution, outputResolutionToResize);
foreach (Shared.Models.Face face in faces)
Rectangle rectangle;
string faceFileJson;
string faceEncodingJson;
PropertyItem? propertyItem;
string? maker = IMetadata.GetMaker(exifDirectory);
string? model = IMetadata.GetModel(exifDirectory);
#pragma warning disable CA1416
using Bitmap source = new(resizedFileHolder.FullName);
MetadataExtractor.GeoLocation? geoLocation = IMetadata.GeoLocation(exifDirectory);
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
const int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment;
foreach ((Shared.Models.Face face, FileHolder? fileHolder, string fileName, bool save) in collection)
{
if (face.Location is null || face.OutputResolution is null)
if (!save)
continue;
skip.Add(Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution));
}
foreach (LocationContainer locationContainer in locationContainers)
{
if (locationContainer.ExifDirectory is null)
if (fileHolder is null)
continue;
if (skip.Contains(locationContainer.WholePercentages))
if (face.FaceEncoding is null || face?.Location is null || face?.OutputResolution is null)
continue;
foreach (Shared.Models.Face face in faces)
if (_OverrideForFaceImages && fileHolder.Exists)
{
if (face.Location is not null && face.OutputResolution is not null)
continue;
json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(locationContainer.ExifDirectory);
if (json is null)
continue;
outputResolutionCheck = JsonSerializer.Deserialize<OutputResolution>(json);
if (outputResolutionCheck is null || outputResolutionCheck.Width != outputResolutionWidth || outputResolutionCheck.Height != outputResolutionHeight)
continue;
rectangle = Shared.Models.Stateless.Methods.ILocation.GetRectangle(Shared.Models.Stateless.ILocation.Digits, outputResolutionCheck, locationContainer.WholePercentages);
if (rectangle is null)
continue;
location = Shared.Models.Stateless.Methods.ILocation.GetLocation(outputResolutionHeight, rectangle.Value, outputResolutionWidth);
if (location is null)
continue;
if (!results.Any(l => l.WholePercentages == locationContainer.WholePercentages))
results.Add(new(locationContainer.CreationDateOnly,
locationContainer.ExifDirectory,
locationContainer.DirectoryNumber,
locationContainer.DisplayDirectoryName,
locationContainer.FilePath,
locationContainer.FromDistanceContent,
locationContainer.Id,
location,
locationContainer.PersonKey,
rectangle.Value,
locationContainer.WholePercentages));
}
}
if (results.Count > 0)
outputResolutionCheck = null;
return results;
}
public List<Shared.Models.Face> GetFaces(string outputResolution, string dResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary<string, int[]> outputResolutionToResize, ReadOnlyCollection<LocationContainer>? locationContainers, List<MappingFromPhotoPrism>? mappingFromPhotoPrismCollection)
{
List<Shared.Models.Face>? results;
if (string.IsNullOrEmpty(dResultsFullGroupDirectory))
throw new NullReferenceException(nameof(dResultsFullGroupDirectory));
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));
}
}
List<LocationContainer> collection;
if (results is null || locationContainers is null)
collection = [];
else
collection = GetLocationContainers(outputResolution, locationContainers, outputResolutionToResize, results);
if (!_LoadPhotoPrismLocations || mappingFromPhotoPrismCollection is null || results is null)
locations = (from l in collection where l is not null select l.Location).ToList();
else
locations = Shared.Models.Stateless.Methods.ILocation.GetLocations(collection, results, mappingFromPhotoPrismCollection, _RectangleIntersectMinimum);
if (results is null || (locations is not null && locations.Count > 0))
{
results = GetFaces(outputResolution, 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, FileInfo?, string, bool)> SaveFaces(string f, string dResultsFullGroupDirectory, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, List<Shared.Models.Face> faces)
{
List<(Shared.Models.Face, FileInfo?, string, bool Save)> results = [];
bool save;
FileInfo fileInfo;
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));
IFaceD dFace = this;
FilePath filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null);
dFace.ReSaveFace(exifDirectory, filePath, face, mappedFile: false);
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 (!directoryExists)
save = true;
else if (_OverrideForFaceImages)
save = true;
else if (!fileInfo.Exists)
save = true;
else if (_CheckDFaceAndUpWriteDates && dateTimes.Count > 0 && dateTimes.Max() > fileInfo.LastWriteTime)
save = true;
results.Add(new(face, fileInfo, Path.Combine(directory, $"{deterministicHashCodeKey}{mappingFromItem.FilePath.ExtensionLowered}{_HiddenFileNameExtension}"), save));
location = Shared.Models.Stateless.Methods.ILocation.GetLocation(face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count);
if (location is null)
continue;
width = location.Right - location.Left;
height = location.Bottom - location.Top;
faceEncodingJson = JsonSerializer.Serialize(face.FaceEncoding);
rectangle = new Rectangle(location.Left, location.Top, width, height);
faceFile = new(face.Mapping?.MappingFromLocation?.AreaPermyriad,
face.Mapping?.MappingFromLocation?.ConfidencePercent,
geoLocation?.ToDmsString(),
face.DateTime,
null,
face.FaceParts,
face.Location,
maker,
null,
model,
face.OutputResolution);
faceFileJson = JsonSerializer.Serialize(faceFile, FaceFileGenerationContext.Default.FaceFile);
using (bitmap = new(width, height))
{
using (graphics = Graphics.FromImage(bitmap))
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, artist, type, faceFileJson);
bitmap.SetPropertyItem(propertyItem);
propertyItem = Property.Models.Stateless.IProperty.GetPropertyItem(_ConstructorInfo, userComment, type, faceEncodingJson);
bitmap.SetPropertyItem(propertyItem);
bitmap.Save(fileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
}
if (File.Exists(fileName))
File.Delete(fileName);
location = Shared.Models.Stateless.Methods.ILocation.GetLocation(_FaceDistanceHiddenImageFactor, face.Location, Shared.Models.Stateless.ILocation.Digits, Shared.Models.Stateless.ILocation.Factor, source.Height, source.Width, collection.Count);
if (location is null)
continue;
width = location.Right - location.Left;
height = location.Bottom - location.Top;
rectangle = new Rectangle(location.Left, location.Top, width, height);
using (bitmap = new(width, height))
{
using (graphics = Graphics.FromImage(bitmap))
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
bitmap.Save(fileName, _HiddenImageCodecInfo, _HiddenEncoderParameters);
}
#pragma warning restore CA1416
File.SetAttributes(fileName, FileAttributes.Hidden);
}
if (results.Any(l => l.Save))
{
if (!directoryExists)
_ = Directory.CreateDirectory(directory);
SaveFaces(mappingFromItem.ResizedFileHolder, results);
}
return results;
}
}

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.FaceParts</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -33,7 +33,7 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />

View File

@ -2,9 +2,12 @@ using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Reflection;
using System.Text;
using System.Text.Json;
using View_by_Distance.Face.Models;
using View_by_Distance.Metadata.Models;
using View_by_Distance.Metadata.Models.Stateless.Methods;
using View_by_Distance.Property.Models;
using View_by_Distance.Property.Models.Stateless;
using View_by_Distance.Resize.Models;
@ -25,22 +28,23 @@ public class D2_FaceParts
private readonly ImageCodecInfo _ImageCodecInfo;
private readonly bool _CheckDFaceAndUpWriteDates;
private readonly ConstructorInfo _ConstructorInfo;
private readonly bool _OverrideForFaceLandmarkImages;
private readonly EncoderParameters _EncoderParameters;
private readonly List<string> _AngleBracketCollection;
private readonly Dictionary<string, string[]> _FileGroups;
private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultContentFileGroups;
public D2_FaceParts(IPropertyConfiguration propertyConfiguration, ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension, bool checkDFaceAndUpWriteDates, bool overrideForFaceLandmarkImages)
{
_FileGroups = [];
_ImageCodecInfo = imageCodecInfo;
_EncoderParameters = encoderParameters;
_FileNameExtension = filenameExtension;
_AngleBracketCollection = [];
_PropertyConfiguration = propertyConfiguration;
_CheckDFaceAndUpWriteDates = checkDFaceAndUpWriteDates;
_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;
}
public override string ToString()
@ -51,67 +55,241 @@ public class D2_FaceParts
public void Update(string dResultsFullGroupDirectory)
{
_FileGroups.Clear();
ReadOnlyDictionary<string, string[]> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, dResultsFullGroupDirectory, [_PropertyConfiguration.ResultContent]);
foreach (KeyValuePair<string, string[]> keyValuePair in keyValuePairs)
_FileGroups.Add(keyValuePair.Key, keyValuePair.Value);
}
public void SetAngleBracketCollection(Configuration configuration, string d2ResultsFullGroupDirectory, string sourceDirectory)
{
_AngleBracketCollection.Clear();
_AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(configuration,
sourceDirectory,
d2ResultsFullGroupDirectory,
contentDescription: "n gif file(s) for each face found",
singletonDescription: string.Empty,
collectionDescription: string.Empty,
converted: true));
}
public string GetFacePartsDirectory(Configuration configuration, string dResultsFullGroupDirectory, Item item, bool includeNameWithoutExtension)
{
string result;
bool angleBracketCollectionAny = _AngleBracketCollection.Count != 0;
if (!angleBracketCollectionAny)
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, dResultsFullGroupDirectory, [_PropertyConfiguration.ResultContent]);
foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
{
if (item.FilePath.DirectoryName is null)
throw new NullReferenceException(nameof(item.FilePath.DirectoryName));
SetAngleBracketCollection(configuration, dResultsFullGroupDirectory, item.FilePath.DirectoryName);
if (keyValuePair.Key == _PropertyConfiguration.ResultContent)
_ResultContentFileGroups[0] = keyValuePair.Value;
else
throw new Exception();
}
if (includeNameWithoutExtension)
result = Path.Combine(_AngleBracketCollection[0].Replace("<>", _PropertyConfiguration.ResultContent), item.FilePath.NameWithoutExtension);
}
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
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
{
result = _AngleBracketCollection[0].Replace("<>", $"[{_PropertyConfiguration.ResultContent}]");
if (!Directory.Exists(result))
_ = Directory.CreateDirectory(result);
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;
}
if (!angleBracketCollectionAny)
_AngleBracketCollection.Clear();
return result;
}
private static void GetPointBounds(PointF[] points, out float xMinimum, out float xMaximum, out float yMinimum, out float yMaximum)
private string GetSeasonDirectory(string d2ResultsFullGroupDirectory, MappingFromItem mappingFromItem, bool any)
{
xMinimum = points[0].X;
xMaximum = xMinimum;
yMinimum = points[0].Y;
yMaximum = yMinimum;
foreach (PointF point in points)
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
{
if (xMinimum > point.X)
xMinimum = point.X;
if (xMaximum < point.X)
xMaximum = point.X;
if (yMinimum > point.Y)
yMinimum = point.Y;
if (yMaximum < point.Y)
yMaximum = point.Y;
#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)
{
@ -123,6 +301,7 @@ public class D2_FaceParts
#elif Windows
// Make save Matrix to represent rotation
// by this angle.
#pragma warning disable CA1416
Matrix rotate_at_origin = new();
rotate_at_origin.Rotate(angle);
@ -169,175 +348,42 @@ public class D2_FaceParts
int y = (hgt - bitmap.Height) / 2;
gr.DrawImage(bitmap, x, y);
}
#pragma warning restore CA1416
#endif
// Return the result bitmap.
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;
Bitmap bitmap = new(image);
result = RotateBitmap(bitmap, angle);
bitmap?.Dispose();
return result;
}
private void SaveFaceParts(int pointSize, FileHolder resizedFileHolder, bool saveRotated, List<(Shared.Models.Face, string, string)> collection)
{
int x;
int y;
double? α;
int width;
int height;
Bitmap rotated;
foreach ((Shared.Models.Face face, string fileName, string rotatedFileName) in collection)
xMinimum = points[0].X;
xMaximum = xMinimum;
yMinimum = points[0].Y;
yMaximum = yMinimum;
foreach (PointF point in points)
{
if (face.FaceEncoding is null)
continue;
try
{
using (Image image = Image.FromFile(resizedFileHolder.FullName))
{
using Graphics graphic = Graphics.FromImage(image);
if (face.FaceParts is null || face.FaceParts.Count == 0)
{
if (face.Location is null)
continue;
width = face.Location.Right - face.Location.Left;
height = face.Location.Bottom - face.Location.Top;
graphic.DrawEllipse(Pens.Red, face.Location.Left, face.Location.Top, width, height);
}
else
{
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
{
foreach (FacePoint facePoint in facePoints)
graphic.DrawEllipse(Pens.GreenYellow, 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();
graphic.DrawEllipse(Pens.Purple, x - pointSize, y - pointSize, pointSize * 2, pointSize * 2);
}
}
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
}
if (saveRotated && face.FaceParts is not null)
{
(_, α) = Shared.Models.Stateless.Methods.IFace.GetEyeα(face.FaceParts);
if (α is null)
continue;
using Image image = Image.FromFile(resizedFileHolder.FullName);
rotated = RotateBitmap(image, (float)α.Value);
if (rotated is not null)
{
rotated.Save(rotatedFileName, _ImageCodecInfo, _EncoderParameters);
rotated.Dispose();
}
}
}
catch (Exception) { }
if (xMinimum > point.X)
xMinimum = point.X;
if (xMaximum < point.X)
xMaximum = point.X;
if (yMinimum > point.Y)
yMinimum = point.Y;
if (yMaximum < point.Y)
yMaximum = point.Y;
}
}
#pragma warning restore CA1416
public void SaveFaceLandmarkImages(Configuration configuration, FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, MappingFromItem mappingFromItem, List<Shared.Models.Face> faces, bool saveRotated)
{
FileInfo fileInfo;
bool check = false;
const int pointSize = 2;
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(pointSize, mappingFromItem.ResizedFileHolder, saveRotated, collection);
}
}
#pragma warning disable CA1416
private void SaveFaceLandmarkImage(MappingFromItem mappingFromItem, List<(Shared.Models.Face, FileInfo?, string, bool)> faceCollection, string fileName)
{
Pen pen;
using Image image = Image.FromFile(mappingFromItem.ResizedFileHolder.FullName);
using Graphics graphic = Graphics.FromImage(image);
foreach ((Shared.Models.Face face, FileInfo? fileInfo, string _, bool _) in faceCollection)
{
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.FaceParts is null || face.FaceParts.Count == 0)
continue;
pen = face.Mapping?.MappingFromPerson is null ? Pens.Red : Pens.GreenYellow;
try
{
foreach ((FacePart facePart, FacePoint[] facePoints) in face.FaceParts)
{
for (int i = 0; i < facePoints.Length - 1; i++)
graphic.DrawLine(pen, new Point(facePoints[i].X, facePoints[i].Y), new Point(facePoints[i + 1].X, facePoints[i + 1].Y));
}
}
catch (Exception) { }
}
image.Save(fileName, _ImageCodecInfo, _EncoderParameters);
}
#pragma warning restore CA1416
private static bool GetNotMapped(string facePartsCollectionDirectory, List<(Shared.Models.Face Face, FileInfo?, string, bool)> faceCollection)
private static bool GetNotMapped(string facePartsCollectionDirectory, List<(Shared.Models.Face Face, FileHolder?, string, bool)> faceCollection)
{
bool results = false;
string checkFile;
foreach ((Shared.Models.Face face, FileInfo? fileInfo, string _, bool _) in faceCollection)
foreach ((Shared.Models.Face face, FileHolder? fileHolder, string _, bool _) in faceCollection)
{
if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
continue;
if (fileInfo is not null)
if (fileHolder is not null)
{
checkFile = Path.Combine(facePartsCollectionDirectory, fileInfo.Name);
checkFile = Path.Combine(facePartsCollectionDirectory, fileHolder.Name);
if (face.Mapping?.MappingFromPerson is not null)
{
if (File.Exists(checkFile))
@ -353,26 +399,123 @@ public class D2_FaceParts
if (!results)
results = true;
if (!File.Exists(checkFile))
File.Copy(fileInfo.FullName, checkFile);
File.Copy(fileHolder.FullName, checkFile);
}
}
}
return results;
}
public void CopyFacesAndSaveFaceLandmarkImage(string facePartsCollectionDirectory, MappingFromItem mappingFromItem, List<(Shared.Models.Face Face, FileInfo?, string, bool)> faceCollection)
private void SaveImage(string fileName, Image image, FaceFile faceFile)
{
bool hasNotMapped = GetNotMapped(facePartsCollectionDirectory, faceCollection);
string fileName = Path.Combine(facePartsCollectionDirectory, $"{mappingFromItem.FilePath.Name}{_FileNameExtension}");
bool save = faceCollection.Any(l => l.Face.FaceEncoding is not null && l.Face.Location is not null && l.Face.OutputResolution is not null && l.Face.FaceParts is not null && l.Face.FaceParts.Count != 0);
FileInfo fileInfo = new(fileName);
if (save && (!fileInfo.Exists || new TimeSpan(DateTime.Now.Ticks - fileInfo.LastWriteTime.Ticks).TotalDays > 10))
short type = 2;
string faceFileJson;
PropertyItem? propertyItem;
const int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; // 315
try
{
SaveFaceLandmarkImage(mappingFromItem, faceCollection, fileName);
fileInfo.Refresh();
#pragma warning disable CA1416
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);
#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 (!hasNotMapped && !fileInfo.Attributes.HasFlag(FileAttributes.Hidden) && (fileInfo.Exists || save))
File.SetAttributes(fileName, FileAttributes.Hidden);
}
}

View File

@ -167,7 +167,13 @@ public class FaceRecognition : DisposableObject
}
else
{
ShapePredictor posePredictor = _PredictorModel switch { PredictorModel.Large => _PosePredictor68Point, PredictorModel.Small => _PosePredictor5Point, _ => throw new Exception() };
ShapePredictor posePredictor = _PredictorModel switch
{
PredictorModel.Large => _PosePredictor68Point,
PredictorModel.Small => _PosePredictor5Point,
PredictorModel.Custom => throw new NotImplementedException(),
_ => throw new Exception()
};
foreach (Location location in locations)
{
DlibDotNet.Rectangle rectangle = new(location.Left, location.Top, location.Right, location.Bottom);
@ -197,7 +203,7 @@ public class FaceRecognition : DisposableObject
return results;
}
public List<(Location, FaceEncoding?, Dictionary<FacePart, FacePoint[]>?)> GetCollection(Image image, List<Location>? locations, bool includeFaceEncoding, bool includeFaceParts)
public List<(Location, FaceEncoding?, Dictionary<FacePart, FacePoint[]>?)> GetCollection(Image image, List<Location> locations, bool includeFaceEncoding, bool includeFaceParts)
{
List<(Location, FaceEncoding?, Dictionary<FacePart, FacePoint[]>?)> results = [];
if (image is null)
@ -206,9 +212,7 @@ public class FaceRecognition : DisposableObject
ThrowIfDisposed();
if (_PredictorModel == PredictorModel.Custom)
throw new NotSupportedException("FaceRecognition.PredictorModel.Custom is not supported.");
if (locations is null)
locations = GetLocations(image);
else if (locations.Count == 0)
if (locations.Count == 0)
locations.AddRange(GetLocations(image));
List<FullObjectDetection> fullObjectDetections = GetFullObjectDetections(image, locations);
if (fullObjectDetections.Count != locations.Count)
@ -408,22 +412,50 @@ public class FaceRecognition : DisposableObject
return null;
}
public static ReadOnlyCollection<LocationContainer> GetLocationContainers(int permyriad, ReadOnlyCollection<LocationContainer> readOnlyLocationContainers, LocationContainer locationContainer)
{
List<LocationContainer> results = [];
int lengthPermyriad;
if (readOnlyLocationContainers.Count != 0)
{
double length;
LocationContainer result;
if (locationContainer.Encoding is not FaceEncoding faceEncodingToCompare)
throw new NullReferenceException(nameof(locationContainer));
faceEncodingToCompare.ThrowIfDisposed();
foreach (LocationContainer item in readOnlyLocationContainers)
{
#pragma warning disable CA1513
if (item.Encoding is not FaceEncoding faceEncoding || faceEncoding.IsDisposed)
throw new ObjectDisposedException($"{nameof(item)} contains disposed object.");
#pragma warning restore CA1513
using (Matrix<double> diff = faceEncoding.Encoding - faceEncodingToCompare.Encoding)
length = DlibDotNet.Dlib.Length(diff);
lengthPermyriad = (int)(length * permyriad);
result = LocationContainer.Get(locationContainer, item, lengthPermyriad, keepExifDirectory: false, keepEncoding: false);
results.Add(result);
}
}
LocationContainer[] array = results.OrderBy(l => l.LengthPermyriad).ToArray();
return new(array);
}
public static List<FaceDistance> FaceDistances(ReadOnlyCollection<FaceDistance> faceDistances, FaceDistance faceDistanceToCompare)
{
List<FaceDistance> results = [];
if (faceDistances is null)
throw new NullReferenceException(nameof(faceDistances));
if (faceDistances.Count != 0)
{
double length;
FaceDistance result;
if (faceDistanceToCompare is null || faceDistanceToCompare.Encoding is not FaceEncoding faceEncodingToCompare)
if (faceDistanceToCompare.Encoding is not FaceEncoding faceEncodingToCompare)
throw new NullReferenceException(nameof(faceDistanceToCompare));
faceEncodingToCompare.ThrowIfDisposed();
foreach (FaceDistance faceDistance in faceDistances)
{
#pragma warning disable CA1513
if (faceDistance.Encoding is not FaceEncoding faceEncoding || faceEncoding.IsDisposed)
throw new ObjectDisposedException($"{nameof(faceDistances)} contains disposed object.");
#pragma warning restore CA1513
using (Matrix<double> diff = faceEncoding.Encoding - faceEncodingToCompare.Encoding)
length = DlibDotNet.Dlib.Length(diff);
result = new(faceDistance, length);

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.FaceRecognitionDotNet</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>

File diff suppressed because it is too large Load Diff

View File

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>2999dda1-5329-4d9f-9d68-cccfabe0e47f</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Instance</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -34,12 +34,12 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
@ -51,6 +51,7 @@
<ProjectReference Include="..\FaceParts\FaceParts.csproj" />
<ProjectReference Include="..\FaceRecognitionDotNet\FaceRecognitionDotNet.csproj" />
<ProjectReference Include="..\Map\Map.csproj" />
<ProjectReference Include="..\Container\Container.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" />
<ProjectReference Include="..\PhotoPrism\PhotoPrism.csproj" />
<ProjectReference Include="..\Property-Compare\Property-Compare.csproj" />

View File

@ -9,7 +9,6 @@ public class Configuration
public bool? CheckDFaceAndUpWriteDates { get; set; }
public bool? CheckJsonForDistanceResults { get; set; }
public string[]? CopyFacesAndSaveFaceLandmarkForOutputResolutions { get; set; }
public int? CrossDirectoryMaxItemsInDistanceCollection { get; set; }
public bool? DeletePossibleDuplicates { get; set; }
public int? DistanceFactor { get; set; }
@ -28,6 +27,8 @@ public class Configuration
public bool? ForceResizeLastWriteTimeToCreationTime { get; set; }
public string? GenealogicalDataCommunicationFile { get; set; }
public string? ImmichAssetsFile { get; set; }
public string? ImmichOwnerId { get; set; }
public string? ImmichRoot { get; set; }
public string[]? IgnoreExtensions { get; set; }
public string[]? JLinks { get; set; }
public string? LinkedAlpha { get; set; }
@ -77,6 +78,7 @@ public class Configuration
public string[]? SaveBlurHashForOutputResolutions { get; set; }
public string[]? SaveFaceDistancesForOutputResolutions { get; set; }
public string[]? SaveFaceLandmarkForOutputResolutions { get; set; }
public string[]? SaveFaceLandmarkForOutputResolutionsV2 { get; set; }
public string[]? SaveFilteredOriginalImagesFromJLinksForOutputResolutions { get; set; }
public bool? SaveFullYearOfRandomFiles { get; set; }
public bool? SaveIndividually { get; set; }
@ -129,7 +131,6 @@ public class Configuration
if (configuration.CheckDFaceAndUpWriteDates is null) throw new NullReferenceException(nameof(configuration.CheckDFaceAndUpWriteDates));
if (configuration?.CheckDFaceAndUpWriteDates is null) throw new NullReferenceException(nameof(configuration.CheckDFaceAndUpWriteDates));
if (configuration?.CheckJsonForDistanceResults is null) throw new NullReferenceException(nameof(configuration.CheckJsonForDistanceResults));
// if (configuration?.CopyFacesAndSaveFaceLandmarkForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.CopyFacesAndSaveFaceLandmarkForOutputResolutions));
if (configuration?.CrossDirectoryMaxItemsInDistanceCollection is null) throw new NullReferenceException(nameof(configuration.CrossDirectoryMaxItemsInDistanceCollection));
if (configuration?.DeletePossibleDuplicates is null) throw new NullReferenceException(nameof(configuration.DeletePossibleDuplicates));
if (configuration?.DistanceFactor is null) throw new NullReferenceException(nameof(configuration.DistanceFactor));
@ -148,6 +149,8 @@ public class Configuration
if (configuration?.ForceResizeLastWriteTimeToCreationTime is null) throw new NullReferenceException(nameof(configuration.ForceResizeLastWriteTimeToCreationTime));
if (configuration?.GenealogicalDataCommunicationFile is null) throw new NullReferenceException(nameof(configuration.GenealogicalDataCommunicationFile));
if (configuration?.ImmichAssetsFile is null) throw new NullReferenceException(nameof(configuration.ImmichAssetsFile));
if (configuration?.ImmichOwnerId is null) throw new NullReferenceException(nameof(configuration.ImmichOwnerId));
if (configuration?.ImmichRoot is null) throw new NullReferenceException(nameof(configuration.ImmichRoot));
// if (configuration?.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
// if (configuration?.JLinks is null) throw new NullReferenceException(nameof(configuration.JLinks));
// if (configuration?.LinkedAlpha is null) throw new NullReferenceException(nameof(configuration.LinkedAlpha));
@ -197,6 +200,7 @@ public class Configuration
// if (configuration?.SaveBlurHashForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.SaveBlurHashForOutputResolutions));
// if (configuration?.SaveFaceDistancesForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.SaveFaceDistancesForOutputResolutions));
// if (configuration?.SaveFaceLandmarkForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.SaveFaceLandmarkForOutputResolutions));
// if (configuration?.SaveFaceLandmarkForOutputResolutionsV2 is null) throw new NullReferenceException(nameof(configuration.SaveFaceLandmarkForOutputResolutionsV2));
// if (configuration?.SaveFilteredOriginalImagesFromJLinksForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions));
if (configuration?.SaveFullYearOfRandomFiles is null) throw new NullReferenceException(nameof(configuration.SaveFullYearOfRandomFiles));
if (configuration?.SaveIndividually is null) throw new NullReferenceException(nameof(configuration.SaveIndividually));
@ -221,7 +225,6 @@ public class Configuration
result = new(propertyConfiguration,
configuration.CheckDFaceAndUpWriteDates.Value,
configuration.CheckJsonForDistanceResults.Value,
configuration.CopyFacesAndSaveFaceLandmarkForOutputResolutions ?? [],
configuration.CrossDirectoryMaxItemsInDistanceCollection.Value,
configuration.DeletePossibleDuplicates.Value,
configuration.DistanceFactor.Value,
@ -240,6 +243,8 @@ public class Configuration
configuration.ForceResizeLastWriteTimeToCreationTime.Value,
configuration.GenealogicalDataCommunicationFile,
configuration.ImmichAssetsFile,
configuration.ImmichOwnerId,
configuration.ImmichRoot,
configuration.IgnoreExtensions ?? [],
configuration.JLinks ?? [],
configuration.LinkedAlpha,
@ -289,6 +294,7 @@ public class Configuration
configuration.SaveBlurHashForOutputResolutions ?? [],
configuration.SaveFaceDistancesForOutputResolutions ?? [],
configuration.SaveFaceLandmarkForOutputResolutions ?? [],
configuration.SaveFaceLandmarkForOutputResolutionsV2 ?? [],
configuration.SaveFilteredOriginalImagesFromJLinksForOutputResolutions ?? [],
configuration.SaveFullYearOfRandomFiles.Value,
configuration.SaveIndividually.Value,

View File

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

View File

@ -3,7 +3,6 @@ namespace View_by_Distance.Instance.Models;
public record Configuration(Property.Models.Configuration PropertyConfiguration,
bool CheckDFaceAndUpWriteDates,
bool CheckJsonForDistanceResults,
string[] CopyFacesAndSaveFaceLandmarkForOutputResolutions,
int CrossDirectoryMaxItemsInDistanceCollection,
bool DeletePossibleDuplicates,
int DistanceFactor,
@ -22,6 +21,8 @@ public record Configuration(Property.Models.Configuration PropertyConfiguration,
bool ForceResizeLastWriteTimeToCreationTime,
string GenealogicalDataCommunicationFile,
string ImmichAssetsFile,
string ImmichOwnerId,
string ImmichRoot,
string[] IgnoreExtensions,
string[] JLinks,
string? LinkedAlpha,
@ -63,7 +64,7 @@ public record Configuration(Property.Models.Configuration PropertyConfiguration,
int RadomUseBirthdayMinimum,
int[] RangeDaysDeltaTolerance,
float[] RangeDistanceTolerance,
float[] RangeFaceAreaPermyriadTolerance,
float[] RangeFaceAreaTolerance,
float[] RangeFaceConfidence,
float[] RectangleIntersectMinimums,
bool ReMap,
@ -71,6 +72,7 @@ public record Configuration(Property.Models.Configuration PropertyConfiguration,
string[] SaveBlurHashForOutputResolutions,
string[] SaveFaceDistancesForOutputResolutions,
string[] SaveFaceLandmarkForOutputResolutions,
string[] SaveFaceLandmarkForOutputResolutionsV2,
string[] SaveFilteredOriginalImagesFromJLinksForOutputResolutions,
bool SaveFullYearOfRandomFiles,
bool SaveIndividually,

View File

@ -1,27 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Instance.Models;
internal record Identifier(int Id, string PaddedId)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, IdentifierSourceGenerationContext.Default.Identifier);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Identifier))]
internal partial class IdentifierSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Identifier[]))]
internal partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,5 +1,6 @@
using System.Collections.ObjectModel;
using System.Text.Json;
using View_by_Distance.Shared.Models;
namespace View_by_Distance.Instance.Models;
@ -24,16 +25,18 @@ internal class F_Random
return result;
}
private static ReadOnlyDictionary<string, List<string>> GetDayToRelativePaths(ReadOnlyCollection<Shared.Models.Mapping> mappingCollection, string dateFormat, ReadOnlyDictionary<int, List<long>> idToPersonKeys)
private static ReadOnlyDictionary<string, List<string>> GetDayToRelativePaths(ReadOnlyCollection<Mapping> distinctValidImageMappingCollection, string dateFormat, string immichOwnerId, string immichRoot, Dictionary<string, ImmichAsset> immichAssets, ReadOnlyDictionary<int, List<long>> idToPersonKeys)
{
Dictionary<string, List<string>> results = [];
string key;
DateTime dateTime;
List<long>? personKeys;
ImmichAsset? immichAsset;
List<string>? relativePaths;
foreach (Shared.Models.Mapping mapping in mappingCollection)
bool immichAssetsCountIsZero = immichAssets.Count == 0;
foreach (Mapping mapping in distinctValidImageMappingCollection)
{
if (mapping.MappingFromItem.FilePath.DirectoryName is null || mapping.MappingFromPerson is null)
if (mapping.MappingFromItem.FilePath.DirectoryFullPath is null || mapping.MappingFromPerson is null)
continue;
if (!idToPersonKeys.TryGetValue(mapping.MappingFromItem.Id, out personKeys))
continue;
@ -49,39 +52,79 @@ internal class F_Random
if (!results.TryGetValue(key, out relativePaths))
throw new Exception();
}
relativePaths.Add(mapping.MappingFromItem.RelativePath);
if (immichAssetsCountIsZero)
relativePaths.Add(mapping.MappingFromItem.RelativePath);
else
{
if (!immichAssets.TryGetValue($"{immichRoot}{mapping.MappingFromItem.RelativePath}", out immichAsset))
continue;
if (!immichAsset.Path.Contains(immichOwnerId))
continue;
relativePaths.Add(immichAsset.Path.Split(immichOwnerId)[1]);
}
}
return new(results);
return results.AsReadOnly();
}
internal void Random(Property.Models.Configuration configuration, int radomUseBirthdayMinimum, string[] validKeyWordsToIgnoreInRandom, ReadOnlyDictionary<long, List<int>> personKeyToIds, ReadOnlyCollection<int>? notNineCollection, ReadOnlyCollection<Shared.Models.Mapping> mappingCollection)
private static Dictionary<string, ImmichAsset> GetImmichAssets(string immichAssetsFile)
{
Dictionary<string, ImmichAsset> results = [];
if (!string.IsNullOrEmpty(immichAssetsFile) && File.Exists(immichAssetsFile))
{
string json = File.ReadAllText(immichAssetsFile);
ImmichAsset[]? immichAssets = JsonSerializer.Deserialize(json, ImmichAssetCollectionSourceGenerationContext.Default.ImmichAssetArray);
if (immichAssets is not null)
{
foreach (ImmichAsset immichAsset in immichAssets)
results.Add(immichAsset.OriginalPath, immichAsset);
}
}
return results;
}
internal void Random(Property.Models.Configuration configuration, string immichAssetsFile, string immichOwnerId, string immichRoot, int radomUseBirthdayMinimum, string[] validKeyWordsToIgnoreInRandom, ReadOnlyDictionary<long, List<int>> personKeyToIds, ReadOnlyDictionary<int, Identifier> splatNineIdentifiers, ReadOnlyCollection<Mapping> distinctValidImageMappingCollection)
{
string key;
string json;
string jsonFile;
Random random = new();
List<string>? collection;
ImmichAsset? immichAsset;
string dateFormat = "MM-dd";
List<string> relativePaths = [];
List<int> distinctCollection = [];
DateTime dateTime = new(2024, 1, 1); //Leap year
Dictionary<string, ImmichAsset> immichAssets = GetImmichAssets(immichAssetsFile);
int[] splatNineIdentifiersKeys = splatNineIdentifiers.Select(l => l.Key).ToArray();
ReadOnlyDictionary<int, List<long>> idToPersonKeys = Map.Models.Stateless.Methods.IMapLogic.GetIdToPersonKeys(personKeyToIds);
ReadOnlyDictionary<string, List<string>> dayToRelativePaths = GetDayToRelativePaths(mappingCollection, dateFormat, idToPersonKeys);
ReadOnlyDictionary<string, List<string>> dayToRelativePaths = GetDayToRelativePaths(distinctValidImageMappingCollection, dateFormat, immichOwnerId, immichRoot, immichAssets, idToPersonKeys);
string fRandomCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(F_Random), "[]");
string[] files = Directory.GetFiles(fRandomCollectionDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
File.Delete(file);
foreach (Shared.Models.Mapping mapping in mappingCollection)
bool immichAssetsCountIsZero = immichAssets.Count == 0;
foreach (Mapping mapping in distinctValidImageMappingCollection)
{
if (mapping.MappingFromItem.IsArchive is not null && mapping.MappingFromItem.IsArchive.Value)
continue;
if (distinctCollection.Contains(mapping.MappingFromItem.Id))
continue;
if (mapping.MappingFromItem.FilePath.DirectoryName is null)
if (mapping.MappingFromItem.FilePath.DirectoryFullPath is null)
continue;
if (notNineCollection is not null && notNineCollection.Contains(mapping.MappingFromItem.Id))
if (!splatNineIdentifiersKeys.Contains(mapping.MappingFromItem.Id))
continue;
if (mapping.MappingFromItem.Keywords is not null && mapping.MappingFromItem.Keywords.Any(l => validKeyWordsToIgnoreInRandom.Contains(l)))
continue;
relativePaths.Add(mapping.MappingFromItem.RelativePath);
if (immichAssetsCountIsZero)
relativePaths.Add(mapping.MappingFromItem.RelativePath);
else
{
if (!immichAssets.TryGetValue($"{immichRoot}{mapping.MappingFromItem.RelativePath}", out immichAsset))
continue;
if (!immichAsset.Path.Contains(immichOwnerId))
continue;
relativePaths.Add(immichAsset.Path.Split(immichOwnerId)[1]);
}
distinctCollection.Add(mapping.MappingFromItem.Id);
}
if (relativePaths.Count > 0)

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Map</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -37,10 +37,12 @@
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
<ProjectReference Include="..\Container\Container.csproj" />
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Metadata\Metadata.csproj" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,8 @@
namespace View_by_Distance.Map.Models;
public record Configuration(bool DeletePossibleDuplicates,
public record Configuration(Shared.Models.Methods.IDistanceLimits DistanceLimits,
Shared.Models.Properties.IPropertyConfiguration PropertyConfiguration,
bool DeletePossibleDuplicates,
bool DistanceMoveUnableToMatch,
bool DistanceRenameToMatch,
int FaceConfidencePercent,

File diff suppressed because it is too large Load Diff

View File

@ -32,9 +32,23 @@ internal abstract class DecadeLogic
return result;
}
internal static void SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers)
internal static void SetCreationTime(MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers)
{
DateTime dateTime;
foreach (LocationContainer locationContainer in locationContainers)
{
dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value;
if (locationContainer.CreationDateOnly.Year != dateTime.Year || locationContainer.CreationDateOnly.Month != dateTime.Month || locationContainer.CreationDateOnly.Day != dateTime.Day)
{
if (!File.Exists(locationContainer.FilePath.FullName))
continue;
File.SetCreationTime(locationContainer.FilePath.FullName, dateTime);
}
}
}
internal static void MoveToDecade(Property.Models.Configuration propertyConfiguration, MappingFromItem mappingFromItem, ReadOnlyCollection<LocationContainer> locationContainers)
{
string halfDecade;
string checkDirectory;
string? yearDirectory;
@ -44,17 +58,10 @@ internal abstract class DecadeLogic
string? personKeyFormattedDirectoryName;
foreach (LocationContainer locationContainer in locationContainers)
{
if (!File.Exists(locationContainer.FilePath.FullName))
if (string.IsNullOrEmpty(locationContainer.FilePath.DirectoryFullPath))
continue;
dateTime = mappingFromItem.DateTimeOriginal is null ? mappingFromItem.MinimumDateTime : mappingFromItem.DateTimeOriginal.Value;
if (locationContainer.CreationDateOnly.Year != dateTime.Year || locationContainer.CreationDateOnly.Month != dateTime.Month || locationContainer.CreationDateOnly.Day != dateTime.Day)
File.SetCreationTime(locationContainer.FilePath.FullName, dateTime);
if (!moveToDecade)
continue;
if (string.IsNullOrEmpty(locationContainer.FilePath.DirectoryName))
continue;
personNameDirectoryName = Path.GetFileName(locationContainer.FilePath.DirectoryName);
yearDirectory = Path.GetDirectoryName(locationContainer.FilePath.DirectoryName);
personNameDirectoryName = Path.GetFileName(locationContainer.FilePath.DirectoryFullPath);
yearDirectory = Path.GetDirectoryName(locationContainer.FilePath.DirectoryFullPath);
if (string.IsNullOrEmpty(yearDirectory))
continue;
yearDirectoryName = Path.GetFileName(yearDirectory);
@ -68,6 +75,8 @@ internal abstract class DecadeLogic
if (halfDecade == yearDirectoryName)
continue;
checkDirectory = Path.Combine(personKeyFormattedDirectory, halfDecade, personNameDirectoryName);
if (!File.Exists(locationContainer.FilePath.FullName))
continue;
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
File.Move(locationContainer.FilePath.FullName, Path.Combine(checkDirectory, locationContainer.FilePath.Name));

View File

@ -22,62 +22,190 @@ internal abstract class DistanceLogic
internal record TicksDirectory(DateTime AlternateDirectoryDateTime,
string Directory,
DateTime DirectoryDateTime,
string DirectoryName,
bool? IsLocationContainerDebugDirectory,
float? TotalDays);
private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames)
{
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)
internal static List<Record> DeleteEmptyDirectoriesAndGetCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, string eDistanceContentDirectory, ReadOnlyDictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted, ReadOnlyCollection<string> personKeyFormattedCollection)
{
List<Record> results = [];
bool check;
string message;
string[] files;
string checkFile;
bool? isDefault;
int? linksCount;
int totalSeconds;
DateTime dateTime;
TimeSpan timeSpan;
int directoryNumber;
List<Record> records;
string? checkDirectory;
string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
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++)
{
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory));
if (!Directory.Exists(checkDirectory))
Directory.Move(directory, checkDirectory);
else
check = false;
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)
{
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
foreach (string file in files)
progressBar.Tick();
personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly);
foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories)
{
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);
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);
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)
@ -86,53 +214,55 @@ internal abstract class DistanceLogic
float? totalDays;
long? next = null;
string? checkDirectory;
string ticksDirectoryName;
DateTime directoryDateTime;
DirectoryInfo directoryInfo;
TicksDirectory ticksDirectory;
long? lastDirectoryTicks = null;
DateTime dateTime = DateTime.Now;
DateTime alternateDirectoryDateTime;
string ticksDirectoryNameFirstSegment;
bool? isLocationContainerDebugDirectory;
long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks;
for (int i = 1; i < 5; i++)
_ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory);
if (!Directory.Exists(eDistanceContentDirectory))
_ = Directory.CreateDirectory(eDistanceContentDirectory);
string[] ticksDirectories = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string ticksDirectory in ticksDirectories)
string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string ticksFullPath in ticksFullPaths)
{
ticksDirectoryName = Path.GetFileName(ticksDirectory);
if (ticksDirectoryName.Length < 3)
ticksDirectoryNameFirstSegment = Path.GetFileName(ticksFullPath).Split('.')[0];
if (ticksDirectoryNameFirstSegment.Length < 3)
continue;
if (!long.TryParse(ticksDirectoryName, out long directoryTicks))
if (!long.TryParse(ticksDirectoryNameFirstSegment, out long directoryTicks))
throw new NotSupportedException();
if (next is null)
next = new DateTime(directoryTicks).Ticks;
else
{
next += month;
checkDirectory = Path.GetDirectoryName(ticksDirectory);
checkDirectory = Path.GetDirectoryName(ticksFullPath);
if (string.IsNullOrEmpty(checkDirectory))
{
if (string.IsNullOrEmpty(checkDirectory))
continue;
checkDirectory = Path.Combine(checkDirectory, next.Value.ToString());
if (ticksDirectory == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern))
if (ticksFullPath == checkDirectory || !checkDirectory.EndsWith(configuration.LocationContainerDirectoryPattern))
continue;
Directory.Move(ticksDirectory, checkDirectory);
Directory.Move(ticksFullPath, checkDirectory);
continue;
}
}
directoryInfo = new(ticksDirectory);
directoryInfo = new(ticksFullPath);
directoryDateTime = new DateTime(directoryTicks);
if (directoryInfo.CreationTime.Ticks != directoryTicks)
Directory.SetCreationTime(ticksDirectory, new DateTime(directoryTicks));
Directory.SetCreationTime(ticksFullPath, new DateTime(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);
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;
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)
continue;
lastDirectoryTicks = directoryTicks;
@ -216,9 +346,114 @@ 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)
{
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 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)
{
List<string> results = [];
string checkFile;
foreach (string file in files)
{
if (file.EndsWith(".brt"))
{
results.Add(file);
continue;
}
checkFile = $"{file}.brt";
if (File.Exists(checkFile))
{
results.Add(file);
continue;
}
File.Move(file, checkFile);
results.Add(checkFile);
}
return results.ToArray();
}
private static void MovedToNewestPersonKeyFormatted(string personKeyFormatted, string newestPersonKeyFormatted, TicksDirectory ticksDirectory, string personKeyDirectory)
{
string newestPersonKeyDirectory = Path.Combine(ticksDirectory.Directory, newestPersonKeyFormatted);
if (Directory.Exists(newestPersonKeyDirectory))
MoveFiles(personKeyFormatted, personKeyDirectory, newestPersonKeyFormatted, newestPersonKeyDirectory);
else
Directory.Move(personKeyDirectory, newestPersonKeyDirectory);
}
private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
{
string[] files;
string checkFile;
string? checkDirectory;
string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
{
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory));
if (!Directory.Exists(checkDirectory))
Directory.Move(directory, checkDirectory);
else
{
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);
}
}
}
_ = IPath.DeleteEmptyDirectories(personKeyDirectory);
}
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 = [];
string @enum;
Record record;
string fileName;
string checkFile;
FilePath filePath;
@ -244,244 +479,54 @@ internal abstract class DistanceLogic
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);
results.Add(new(directoryNumber, isDefault, linksCount, filePath, personDisplayDirectoryName, personKeyFormatted));
record = new(DirectoryNumber: directoryNumber,
IsDefault: isDefault,
LinksCount: linksCount,
MappedFaceFilePath: filePath,
PersonDisplayDirectoryName: personDisplayDirectoryName,
PersonKeyFormatted: personKeyFormatted);
results.Add(record);
}
return results;
}
private static void RenameUnknown(string[] files)
private static string GetCheckFile(TicksDirectory ticksDirectory, string @enum, string fileName, string file)
{
foreach (string file in files)
string result;
string checkDirectory;
string directory = file;
List<string> collection = [];
for (int i = 0; i < file.Length; i++)
{
if (file.EndsWith(".unk"))
continue;
File.Move(file, $"{file}.unk");
}
}
private static void MovedToNewestPersonKeyFormatted(string personKeyFormatted, string newestPersonKeyFormatted, TicksDirectory ticksDirectory, string personKeyDirectory)
{
string newestPersonKeyDirectory = Path.Combine(ticksDirectory.Directory, newestPersonKeyFormatted);
if (Directory.Exists(newestPersonKeyDirectory))
MoveFiles(personKeyFormatted, personKeyDirectory, newestPersonKeyFormatted, newestPersonKeyDirectory);
else
Directory.Move(personKeyDirectory, newestPersonKeyDirectory);
}
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;
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;
}
internal static List<Record> DeleteEmptyDirectoriesAndGetCollection(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, string eDistanceContentDirectory, ReadOnlyDictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted, ReadOnlyCollection<string> personKeyFormattedCollection)
{
List<Record> results = [];
bool check;
string message;
string[] files;
bool? isDefault;
int? linksCount;
int totalSeconds;
DateTime dateTime;
TimeSpan timeSpan;
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;
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();
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)
RenameUnknown(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;
break;
}
return results;
}
}

View File

@ -0,0 +1,207 @@
using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Text.Json;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Stateless.Methods;
using static View_by_Distance.Map.Models.Stateless.MapLogic;
namespace View_by_Distance.Map.Models.Stateless;
internal abstract class FaceFileLogic
{
private static void MappedParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyDictionary<int, List<(string, int)>> skipCollection, List<LocationContainer> locationContainers, MappedFile mappedFile)
{
int? id;
string checkFile;
DateOnly dateOnly;
FilePath filePath;
string[] fileMatches;
FileHolder fileHolder;
int? wholePercentages;
const string lnk = ".lnk";
ExifDirectory? exifDirectory;
string personDisplayDirectoryName;
const bool fromDistanceContent = true;
List<(string File, int WholePercentages)>? wholePercentagesCollection;
if (!mappedFile.FilePath.Name.EndsWith(lnk))
{
if (mappedFile.FilePath.Id is null)
return;
id = mappedFile.FilePath.Id;
wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, mappedFile.FilePath);
}
else
{
fileHolder = IFileHolder.Get(mappedFile.FilePath.FullName[..^4]);
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (filePath.Id is null)
return;
id = filePath.Id;
wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath);
}
if (wholePercentages is null)
return;
if (configuration.LinkedAlpha is null && string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory) && skipCollection.TryGetValue(id.Value, out wholePercentagesCollection))
{
fileMatches = (from l in wholePercentagesCollection where l.WholePercentages == wholePercentages select l.File).ToArray();
foreach (string fileMatch in fileMatches)
{
if (string.IsNullOrEmpty(fileMatch) || !File.Exists(fileMatch))
continue;
checkFile = $"{fileMatch}.dup";
if (File.Exists(checkFile))
continue;
File.Move(fileMatch, checkFile);
continue;
}
}
dateOnly = DateOnly.FromDateTime(new DateTime(mappedFile.FilePath.CreationTicks));
if (mappedFile.FilePath.Name.EndsWith(lnk) || !File.Exists(mappedFile.FilePath.FullName))
exifDirectory = null;
else
exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(mappedFile.FilePath);
RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value);
personDisplayDirectoryName = mappedFile.PersonDisplayDirectoryName is null ? configuration.MappingDefaultName : mappedFile.PersonDisplayDirectoryName;
LocationContainer locationContainer = new(dateOnly,
exifDirectory,
mappedFile.DirectoryNumber,
personDisplayDirectoryName,
null,
null,
mappedFile.FilePath,
fromDistanceContent,
id.Value,
null,
null,
mappedFile.PersonKey,
rectangle,
wholePercentages.Value);
lock (locationContainers)
locationContainers.Add(locationContainer);
}
private static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetReadOnly(Dictionary<int, Dictionary<int, LocationContainer>> keyValuePairs)
{
Dictionary<int, ReadOnlyDictionary<int, LocationContainer>> results = [];
foreach (KeyValuePair<int, Dictionary<int, LocationContainer>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
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)
{
Dictionary<int, Dictionary<int, LocationContainer>> results = [];
List<LocationContainer> locationContainers = [];
List<string> personKeyFormattedCollection = [];
Dictionary<int, LocationContainer>? keyValuePairs;
Dictionary<int, List<(string, int)>> skipCollection = [];
Dictionary<int, List<(string, int)>> skipNotSkipCollection = [];
Dictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted = [];
SetSkipCollections(configuration, personContainers, a2PeopleSingletonDirectory, skipCollection, skipNotSkipCollection);
SetPersonCollectionsAfterSetSkipCollections(configuration, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection);
List<Record> records = DistanceLogic.DeleteEmptyDirectoriesAndGetCollection(propertyConfiguration, configuration, ticks, eDistanceContentDirectory, personKeyFormattedToNewestPersonKeyFormatted.AsReadOnly(), personKeyFormattedCollection.AsReadOnly());
List<MappedFile> mappedFiles = GetMappedFiles(propertyConfiguration, configuration, personContainers, records);
if (mappedFiles.Count > 0)
{
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)";
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(mappedFiles.Count, message, options);
_ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
{
progressBar.Tick();
MappedParallelFor(propertyConfiguration, configuration, skipCollection.AsReadOnly(), locationContainers, mappedFiles[i]);
});
}
foreach (LocationContainer locationContainer in locationContainers)
{
if (!results.TryGetValue(locationContainer.Id, out keyValuePairs))
{
results.Add(locationContainer.Id, []);
if (!results.TryGetValue(locationContainer.Id, out keyValuePairs))
throw new Exception();
}
if (keyValuePairs.ContainsKey(locationContainer.WholePercentages))
continue;
keyValuePairs.Add(locationContainer.WholePercentages, locationContainer);
}
return GetReadOnly(results);
}
private static void MoveUnableToMatch(FilePath filePath)
{
string checkFile = $"{filePath.FullName}.unk";
if (File.Exists(filePath.FullName) && !File.Exists(checkFile))
File.Move(filePath.FullName, checkFile);
}
private static void AvailableParallelFor(Configuration configuration, IFaceD dFace, List<LocationContainer> locationContainers, FilePath filePath)
{
string? json;
const bool fromDistanceContent = false;
if (filePath.Id is null)
return;
DateOnly dateOnly = DateOnly.FromDateTime(new DateTime(filePath.CreationTicks));
int? wholePercentages = IMapping.GetWholePercentages(dFace.FileNameExtension, filePath);
if (wholePercentages is null)
{
if (configuration.DistanceMoveUnableToMatch)
MoveUnableToMatch(filePath);
return;
}
ExifDirectory exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath);
json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(exifDirectory);
if (json is null || !json.Contains(nameof(DateTime)))
{
if (configuration.DistanceMoveUnableToMatch)
MoveUnableToMatch(filePath);
return;
}
FaceFile? faceFile = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
if (faceFile is null || faceFile.Location is null)
{
if (configuration.DistanceMoveUnableToMatch)
MoveUnableToMatch(filePath);
return;
}
RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value);
if (rectangle is null)
return;
LocationContainer locationContainer = new(dateOnly,
exifDirectory,
null,
null,
null,
faceFile,
filePath,
fromDistanceContent,
filePath.Id.Value,
null,
null,
null,
rectangle,
wholePercentages.Value);
lock (locationContainers)
locationContainers.Add(locationContainer);
}
internal static List<LocationContainer> GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection<FilePath> filePaths)
{
List<LocationContainer> results = [];
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
string message = $") Building Available Face Files Collection - {totalSeconds} total second(s)";
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(filePaths.Count, message, options);
_ = Parallel.For(0, filePaths.Count, parallelOptions, (i, state) =>
{
progressBar.Tick();
AvailableParallelFor(configuration, dFace, results, filePaths[i]);
});
return results;
}
}

View File

@ -6,13 +6,12 @@ namespace View_by_Distance.Map.Models.Stateless;
internal abstract class LookForAbandonedLogic
{
internal static void LookForAbandoned(Property.Models.Configuration propertyConfiguration, List<int> distinctFilteredIds, string directory, string directoryName)
internal static void LookForAbandoned(Property.Models.Configuration propertyConfiguration, List<string> distinctFilteredFileNameFirstSegments, string directory, string directoryName)
{
FilePath filePath;
FileHolder fileHolder;
string fileNameFirstSegment;
List<string> renameCollection = [];
string[] distinctFilteredIdsValues = distinctFilteredIds.Select(l => l.ToString()).ToArray();
string[] files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
@ -21,7 +20,7 @@ internal abstract class LookForAbandonedLogic
fileNameFirstSegment = fileHolder.NameWithoutExtension.Split('.')[0];
if (!filePath.IsIntelligentIdFormat && filePath.SortOrder is null)
continue;
if (distinctFilteredIdsValues.Contains(fileNameFirstSegment))
if (distinctFilteredFileNameFirstSegments.Contains(fileNameFirstSegment))
continue;
renameCollection.Add(file);
}
@ -36,7 +35,7 @@ internal abstract class LookForAbandonedLogic
}
}
internal static void LookForAbandoned(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, List<int> distinctFilteredIds)
internal static void LookForAbandoned(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, List<string> distinctFilteredFileNameFirstSegments)
{
string[] directories = Directory.GetDirectories(bResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
@ -44,7 +43,7 @@ internal abstract class LookForAbandonedLogic
string? directoryName = Path.GetFileName(directory);
if (string.IsNullOrEmpty(directoryName) || (directoryName.Length != 2 && directoryName.Length != 4))
continue;
LookForAbandoned(propertyConfiguration, distinctFilteredIds, directory, directoryName);
LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,99 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Map.Models.Stateless.Methods;
public interface IMapLogic
{
ReadOnlyDictionary<int, List<long>> TestStatic_GetIdToPersonKeys(ReadOnlyDictionary<long, List<int>> personKeyToIds) =>
GetIdToPersonKeys(personKeyToIds);
static ReadOnlyDictionary<int, List<long>> GetIdToPersonKeys(ReadOnlyDictionary<long, List<int>> personKeyToIds) =>
MapLogic.GetIdToPersonKeys(personKeyToIds);
static string Get(bool saveIndividually, string forceSingleImageHumanized, int by, bool isDefaultName) =>
$"{by switch
{
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<Shared.Models.Face> TestStatic_GetFaces(ReadOnlyCollection<Shared.Models.Item> items) =>
GetFaces(items);
static ReadOnlyCollection<Shared.Models.Face> GetFaces(ReadOnlyCollection<Shared.Models.Item> items) =>
MapLogic.GetFaces(items);
Shared.Models.Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection<Shared.Models.Item> items) =>
GetSelectedMappingCollection(items);
static Shared.Models.Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Shared.Models.Item> items) =>
public static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Item> items) =>
MapLogic.GetSelectedMappingCollection(items);
Shared.Models.Mapping[] TestStatic_GetSelectedMappingCollection(ReadOnlyCollection<Shared.Models.Face> faces) =>
GetSelectedMappingCollection(faces);
static Shared.Models.Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Shared.Models.Face> faces) =>
public static Mapping[] GetSelectedMappingCollection(ReadOnlyCollection<Face> faces) =>
MapLogic.GetSelectedMappingCollection(faces);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, Shared.Models.Mapping>> TestStatic_GetIdToWholePercentagesToFace(ReadOnlyCollection<Shared.Models.Mapping> mappingCollection) =>
GetIdToWholePercentagesToFace(mappingCollection);
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, Shared.Models.Mapping>> GetIdToWholePercentagesToFace(ReadOnlyCollection<Shared.Models.Mapping> mappingCollection) =>
MapLogic.GetIdToWholePercentagesToFace(mappingCollection);
public static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, int wholePercentages) =>
MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, wholePercentages);
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);
public static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers, MappingFromLocation mappingFromLocation) =>
MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation.WholePercentages);
void TestStatic_SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, Shared.Models.MappingFromItem mappingFromItem, ReadOnlyCollection<Shared.Models.LocationContainer> locationContainers) =>
SetCreationTimeMaybeMoveToDecade(propertyConfiguration, moveToDecade, mappingFromItem, locationContainers);
static void SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, Shared.Models.MappingFromItem mappingFromItem, ReadOnlyCollection<Shared.Models.LocationContainer> locationContainers) =>
DecadeLogic.SetCreationTimeMaybeMoveToDecade(propertyConfiguration, moveToDecade, mappingFromItem, locationContainers);
bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<Shared.Models.PersonContainer>>? wholePercentagesToPersonContainers, Shared.Models.MappingFromLocation mappingFromLocation) =>
CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);
static bool? CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary<int, ReadOnlyCollection<Shared.Models.PersonContainer>>? wholePercentagesToPersonContainers, Shared.Models.MappingFromLocation mappingFromLocation) =>
MapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);
string TestStatic_GetDecade(Shared.Models.MappingFromItem mappingFromItem) =>
GetDecade(mappingFromItem);
static string GetDecade(Shared.Models.MappingFromItem mappingFromItem) =>
public static string GetDecade(MappingFromItem mappingFromItem) =>
DecadeLogic.GetDecade(mappingFromItem, null);
public static ReadOnlyCollection<Face> GetFaces(ReadOnlyCollection<Item> items) =>
MapLogic.GetFaces(items);
public static ReadOnlyDictionary<int, List<long>> GetIdToPersonKeys(ReadOnlyDictionary<long, List<int>> personKeyToIds) =>
MapLogic.GetIdToPersonKeys(personKeyToIds);
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);
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,34 +8,48 @@ namespace View_by_Distance.Map.Models.Stateless;
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;
Dictionary<int, List<LocationContainer>>? yearTo;
Dictionary<long, Dictionary<int, List<LocationContainer>>> personKeyTo = [];
foreach (LocationContainer locationContainer in locationContainers)
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 (!locationContainer.FromDistanceContent)
if (configuration.LocationContainerDistanceTolerance is null)
break;
progressBar.Tick();
if (group.RelationContainersCollection.Count == 0)
continue;
if (!locationContainer.FilePath.FullName.Contains(configuration.LocationContainerDirectoryPattern))
continue;
if (!personKeyTo.TryGetValue(locationContainer.PersonKey, out yearTo))
{
personKeyTo.Add(locationContainer.PersonKey, []);
if (!personKeyTo.TryGetValue(locationContainer.PersonKey, 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);
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);
}
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)
@ -45,6 +59,7 @@ internal abstract class RelationLogic
int lastIndex;
List<int> years = [];
List<int> indices = [];
LocationContainer locationContainer;
List<(int Index, int Year)> sort = [];
List<LocationContainer> collection = [];
KeyValuePair<int, List<LocationContainer>> keyValue;
@ -76,12 +91,130 @@ internal abstract class RelationLogic
key = $"{years.Min()}-{keyValue.Key}";
if (collection.Count == 0)
continue;
results.Add(new(key, collection[0].PersonKey, new(collection)));
locationContainer = collection[0];
if (locationContainer.PersonKey is null)
continue;
results.Add(new(key, locationContainer.PersonKey.Value, collection.AsReadOnly()));
collection = [];
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)
@ -98,7 +231,7 @@ internal abstract class RelationLogic
string? personKeyFormattedDirectory;
foreach ((FileHolder fileHolder, _) in relationContainers)
{
personNameDirectory = fileHolder.DirectoryName;
personNameDirectory = fileHolder.DirectoryFullPath;
yearDirectory = Path.GetDirectoryName(personNameDirectory);
personNameDirectoryName = Path.GetFileName(personNameDirectory);
personKeyFormattedDirectory = Path.GetDirectoryName(yearDirectory);
@ -196,92 +329,7 @@ internal abstract class RelationLogic
_ = Directory.CreateDirectory(checkDirectory);
}
}
return new(results);
}
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 = "{ \"[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;
return results.AsReadOnly();
}
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)
@ -392,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.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

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>f89b7242-dbb0-4349-b950-657eb8cf87ef</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Metadata.Query</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -35,10 +35,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />

View File

@ -34,17 +34,15 @@ public class Configuration
}
}
private static Models.Configuration Get(Configuration? configuration)
private static Models.Configuration Get(Configuration? configuration, Property.Models.Configuration propertyConfiguration)
{
Models.Configuration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.PersonBirthdayFormat is null) throw new NullReferenceException(nameof(configuration.PersonBirthdayFormat));
if (configuration.PropertyConfiguration is null) throw new NullReferenceException(nameof(configuration.PropertyConfiguration));
result = new(
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat,
configuration.PropertyConfiguration);
result = new(propertyConfiguration,
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat);
return result;
}
@ -66,7 +64,7 @@ public class Configuration
#pragma warning restore IL3050, IL2026
}
PreVerify(configurationRoot, configuration);
result = Get(configuration);
result = Get(configuration, propertyConfiguration);
return result;
}

View File

@ -13,10 +13,9 @@ public class Configuration
public Property.Models.Configuration PropertyConfiguration => _PropertyConfiguration;
[JsonConstructor]
public Configuration(
string[] ignoreExtensions,
string personBirthdayFormat,
Property.Models.Configuration propertyConfiguration)
public Configuration(Property.Models.Configuration propertyConfiguration,
string[] ignoreExtensions,
string personBirthdayFormat)
{
IgnoreExtensions = ignoreExtensions;
PersonBirthdayFormat = personBirthdayFormat;

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Metadata</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -34,10 +34,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -16,16 +16,24 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
private readonly bool _PropertiesChangedForMetadata;
private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly bool _ForceMetadataLastWriteTimeToCreationTime;
private readonly ReadOnlyDictionary<string, string[]> _FileGroups;
private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups;
public B_Metadata(IPropertyConfiguration propertyConfiguration)
{
_PropertiesChangedForMetadata = false;
_PropertyConfiguration = propertyConfiguration;
_ForceMetadataLastWriteTimeToCreationTime = false;
_ResultSingletonFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
_WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
_FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, null, [propertyConfiguration.ResultSingleton]);
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, null, [propertyConfiguration.ResultSingleton]);
foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
{
if (keyValuePair.Key == _PropertyConfiguration.ResultSingleton)
_ResultSingletonFileGroups[0] = keyValuePair.Value;
else
throw new Exception();
}
}
public B_Metadata(IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, string bResultsFullGroupDirectory)
@ -33,8 +41,16 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
_PropertyConfiguration = propertyConfiguration;
_PropertiesChangedForMetadata = propertiesChangedForMetadata;
_ForceMetadataLastWriteTimeToCreationTime = forceMetadataLastWriteTimeToCreationTime;
_ResultSingletonFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
_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();
}
}
public override string ToString()
@ -43,12 +59,34 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
return result;
}
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo)
{
string[] segments = directory.Split(cei.Combined);
string? checkDirectory = segments.Length == 1 ?
Path.Combine(segments[0], $"{cei.Combined[2..]}") :
segments.Length == 2 ?
$"{segments[0]}{cei.Combined[2..]}{segments[1]}" :
null;
if (checkDirectory is not null && System.IO.Directory.Exists(checkDirectory))
{
string checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
{
File.Move(checkFile, fileInfo.FullName);
fileInfo.Refresh();
}
}
}
public ExifDirectory GetMetadataCollection(FilePath filePath, List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, string[] changesFrom, MappingFromItem mappingFromItem)
{
ExifDirectory? result = null;
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"));
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 (_ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
@ -110,7 +148,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
DateTime? result = null;
DateTime? dateTime;
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();
results.Add(new DateTime(filePath.CreationTicks));
results.Add(new DateTime(filePath.LastWriteTicks));
@ -120,7 +158,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
results.Add(checkDateTime);
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)
results.Add(dateTime.Value);
}
@ -128,7 +166,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
results.Add(checkDateTime);
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)
results.Add(dateTime.Value);
}
@ -139,7 +177,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
}
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)
{
result ??= dateTime.Value;
@ -157,7 +195,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
}
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)
{
result ??= dateTime.Value;
@ -175,7 +213,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
}
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)
{
result ??= dateTime.Value;
@ -193,7 +231,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
}
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)
{
result ??= dateTime.Value;

View File

@ -1,3 +1,4 @@
using System.Globalization;
using View_by_Distance.Shared.Models;
namespace View_by_Distance.Metadata.Models.Stateless.Methods;
@ -47,4 +48,23 @@ internal static class Base
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();
}
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.AviDirectory[] aviDirectories = GetAviDirectories(directories);
@ -502,12 +502,12 @@ internal abstract class Exif
result = new(aviDirectories,
exifBaseDirectories,
fileMetadataDirectories,
filePath,
gifHeaderDirectories,
gpsDirectories,
size?.Height,
jpegDirectories,
makernoteDirectories,
filePath.Name,
photoshopDirectories,
pngDirectories,
quickTimeMovieHeaderDirectories,
@ -520,13 +520,13 @@ internal abstract class Exif
internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath)
{
Shared.Models.ExifDirectory result;
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName);
System.Drawing.Size? size;
try
{ size = Dimensions.GetDimensions(filePath.FullName); }
catch (Exception)
{ size = null; }
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName);
result = Covert(filePath, size, directories);
result = Covert(filePath, directories, size);
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) =>
// 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

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>fa06c6db-0226-42ca-8728-68b1e336184d</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Mirror.Length</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -35,10 +35,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />

View File

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

View File

@ -34,17 +34,15 @@ public class Configuration
}
}
private static Models.Configuration Get(Configuration? configuration)
private static Models.Configuration Get(Configuration? configuration, Property.Models.Configuration propertyConfiguration)
{
Models.Configuration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.PersonBirthdayFormat is null) throw new NullReferenceException(nameof(configuration.PersonBirthdayFormat));
if (configuration.PropertyConfiguration is null) throw new NullReferenceException(nameof(configuration.PropertyConfiguration));
result = new(
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat,
configuration.PropertyConfiguration);
result = new(propertyConfiguration,
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat);
return result;
}
@ -66,7 +64,7 @@ public class Configuration
#pragma warning restore IL3050, IL2026
}
PreVerify(configurationRoot, configuration);
result = Get(configuration);
result = Get(configuration, propertyConfiguration);
return result;
}

View File

@ -13,10 +13,9 @@ public class Configuration
public Property.Models.Configuration PropertyConfiguration => _PropertyConfiguration;
[JsonConstructor]
public Configuration(
string[] ignoreExtensions,
string personBirthdayFormat,
Property.Models.Configuration propertyConfiguration)
public Configuration(Property.Models.Configuration propertyConfiguration,
string[] ignoreExtensions,
string personBirthdayFormat)
{
IgnoreExtensions = ignoreExtensions;
PersonBirthdayFormat = personBirthdayFormat;

View File

@ -34,17 +34,15 @@ public class Configuration
}
}
private static Models.Configuration Get(Configuration? configuration)
private static Models.Configuration Get(Configuration? configuration, Property.Models.Configuration propertyConfiguration)
{
Models.Configuration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.PersonBirthdayFormat is null) throw new NullReferenceException(nameof(configuration.PersonBirthdayFormat));
if (configuration.PropertyConfiguration is null) throw new NullReferenceException(nameof(configuration.PropertyConfiguration));
result = new(
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat,
configuration.PropertyConfiguration);
result = new(propertyConfiguration,
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat);
return result;
}
@ -66,7 +64,7 @@ public class Configuration
#pragma warning restore IL3050, IL2026
}
PreVerify(configurationRoot, configuration);
result = Get(configuration);
result = Get(configuration, propertyConfiguration);
return result;
}

View File

@ -13,10 +13,9 @@ public class Configuration
public Property.Models.Configuration PropertyConfiguration => _PropertyConfiguration;
[JsonConstructor]
public Configuration(
string[] ignoreExtensions,
string personBirthdayFormat,
Property.Models.Configuration propertyConfiguration)
public Configuration(Property.Models.Configuration propertyConfiguration,
string[] ignoreExtensions,
string personBirthdayFormat)
{
IgnoreExtensions = ignoreExtensions;
PersonBirthdayFormat = personBirthdayFormat;

View File

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>ce31220e-ef92-4e68-89c5-91b027a94dca</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Move.By.Id</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -35,9 +35,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />

View File

@ -115,7 +115,7 @@ public class MoveById
progressBar.Tick();
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(file);
filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: null);
if (fileHolder.ExtensionLowered == ".id" || fileHolder.DirectoryName is null)
if (fileHolder.ExtensionLowered == ".id" || fileHolder.DirectoryFullPath is null)
continue;
if (allFiles.Contains($"{fileHolder.FullName}.id"))
continue;

View File

@ -34,17 +34,15 @@ public class Configuration
}
}
private static Models.Configuration Get(Configuration? configuration)
private static Models.Configuration Get(Configuration? configuration, Property.Models.Configuration propertyConfiguration)
{
Models.Configuration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.PersonBirthdayFormat is null) throw new NullReferenceException(nameof(configuration.PersonBirthdayFormat));
if (configuration.PropertyConfiguration is null) throw new NullReferenceException(nameof(configuration.PropertyConfiguration));
result = new(
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat,
configuration.PropertyConfiguration);
result = new(propertyConfiguration,
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat);
return result;
}
@ -66,7 +64,7 @@ public class Configuration
#pragma warning restore IL3050, IL2026
}
PreVerify(configurationRoot, configuration);
result = Get(configuration);
result = Get(configuration, propertyConfiguration);
return result;
}

View File

@ -13,10 +13,9 @@ public class Configuration
public Property.Models.Configuration PropertyConfiguration => _PropertyConfiguration;
[JsonConstructor]
public Configuration(
string[] ignoreExtensions,
string personBirthdayFormat,
Property.Models.Configuration propertyConfiguration)
public Configuration(Property.Models.Configuration propertyConfiguration,
string[] ignoreExtensions,
string personBirthdayFormat)
{
IgnoreExtensions = ignoreExtensions;
PersonBirthdayFormat = personBirthdayFormat;

View File

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>ae069946-d0c0-4e4f-94f6-9c526e4ae4e7</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Offset.Date.Time.Original</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -35,10 +35,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Property\Property.csproj" />

View File

@ -216,6 +216,17 @@ public class OffsetDateTimeOriginal
{
TimeSpan timeSpan = new(targetDateTimeOriginal.Value.Ticks - badDateTimeOriginal.Value.Ticks);
DateFix(sourceDirectory, asciiEncoding, checkDirectory, minimumDateTime, maximumDateTime, timeSpan.Ticks);
// DateTime a = new(2020, 01, 04, 02, 12, 51, 32);
// // DateTime a = new(2020, 01, 05, 18, 17, 15, 97);
// DateTime b = new(2020, 01, 05, 19, 52, 30, 17);
// DateTime c = new(2024, 12, 07, 11, 19, 06, 13);
// // DateTime d = new(2024, 12, 07, 12, 07, 30, 38);
// long e = new DateTime(c.Ticks - b.Ticks).AddMinutes(-5).Ticks;
// if (a != DateTime.MinValue)
// minimumDateTime = a.AddTicks(e).AddHours(-1);
// if (c != DateTime.MinValue)
// maximumDateTime = c;
// DateFix(sourceDirectory, asciiEncoding, checkDirectory, minimumDateTime, maximumDateTime, e);
}
}
}

View File

@ -169,7 +169,7 @@ public class F_PhotoPrism
}
}
public static void WriteMatches(string fPhotoPrismContentDirectory, string personBirthdayFormat, float[] rectangleIntersectMinimums, long ticks, ReadOnlyCollection<Face> distinctFilteredFaces, Shared.Models.Methods.IMapLogic mapLogic)
public static void WriteMatches(string fPhotoPrismContentDirectory, string personBirthdayFormat, float[] rectangleIntersectMinimums, long ticks, ReadOnlyCollection<Face> distinctValidImageFaces, Shared.Models.Methods.IMapLogic mapLogic)
{
string file;
string text;
@ -189,7 +189,7 @@ public class F_PhotoPrism
ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers;
(MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, float Percent)[] sortedCollection;
List<(MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, float Percent)> collection = [];
foreach (Face face in distinctFilteredFaces)
foreach (Face face in distinctValidImageFaces)
{
collection.Clear();
wholePercentages = face.Mapping?.MappingFromLocation?.WholePercentages;

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.PhotoPrism</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -37,7 +37,7 @@
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.PrepareForOld</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -33,14 +33,14 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Property.Compare</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -34,8 +34,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<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" />

View File

@ -22,19 +22,27 @@ public class A_Property
private readonly Configuration _Configuration;
private readonly List<string> _AngleBracketCollection;
private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly ReadOnlyDictionary<string, string[]> _FileGroups;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups;
public A_Property(int maxDegreeOfParallelism, Configuration propertyConfiguration, string outputExtension, bool reverse, string aResultsFullGroupDirectory)
{
Reverse = reverse;
_ExceptionsDirectories = [];
_AngleBracketCollection = [];
_OutputExtension = outputExtension;
_ASCIIEncoding = new ASCIIEncoding();
_Configuration = propertyConfiguration;
_AngleBracketCollection = [];
_PropertyConfiguration = propertyConfiguration;
_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()
@ -43,6 +51,113 @@ public class A_Property
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));
}
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);
}
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);
}
});
}
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);
lock (sourceDirectoryChanges)
sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
lock (item)
item.Update(property);
}
}
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;
@ -57,8 +172,11 @@ public class A_Property
fileInfo = new(Path.Combine(angleBracket.Replace("<>", _PropertyConfiguration.ResultSingleton), $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json"));
else
{
(_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, item.FilePath);
fileInfo = new(Path.Combine(_FileGroups[_PropertyConfiguration.ResultSingleton][directoryIndex], $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json"));
string fileName = $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json";
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, item.FilePath);
string directory = _ResultSingletonFileGroups[0][cei.Enum][cei.Index];
fileInfo = new(Path.Combine(directory, fileName));
MoveIf(fileName, cei, directory, fileInfo);
}
List<DateTime> dateTimes = (from l in sourceDirectoryFileTuples where l is not null && changesFrom.Contains(l.Item1) select l.Item2).ToList();
if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
@ -164,110 +282,22 @@ public class A_Property
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();
if (!anyNullOrNoIsUniqueFileName)
_AngleBracketCollection.AddRange(new[] { Path.Combine(aResultsFullGroupDirectory, "<>") });
else
_AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(_PropertyConfiguration,
sourceDirectory,
aResultsFullGroupDirectory,
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)
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))
{
property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension);
lock (sourceDirectoryChanges)
sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
lock (item)
item.Update(property);
}
}
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, List<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;
string checkFile = Path.Combine(checkDirectory, fileName);
if (File.Exists(checkFile))
{
File.Move(checkFile, fileInfo.FullName);
fileInfo.Refresh();
}
}
}
@ -277,9 +307,9 @@ public class A_Property
bool angleBracketCollectionAny = _AngleBracketCollection.Count != 0;
if (!angleBracketCollectionAny)
{
if (item.FilePath.DirectoryName is null)
throw new NullReferenceException(nameof(item.FilePath.DirectoryName));
SetAngleBracketCollection(item.FilePath.DirectoryName, !item.IsUniqueFileName);
if (item.FilePath.DirectoryFullPath is null)
throw new NullReferenceException(nameof(item.FilePath.DirectoryFullPath));
SetAngleBracketCollection(item.FilePath.DirectoryFullPath, !item.IsUniqueFileName);
}
bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered);
result = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension);

View File

@ -28,9 +28,11 @@ public class Configuration
public int? ResultAllInOneSubdirectoryLength { get; set; }
public string? ResultCollection { get; set; }
public string? ResultContent { get; set; }
public string? ResultContentCollection { get; set; }
public string? ResultSingleton { get; set; }
public string? RootDirectory { get; set; }
public string[]? ValidImageFormatExtensions { get; set; }
public string[]? ValidVideoFormatExtensions { get; set; }
public string[]? VerifyToSeason { get; set; }
public override string ToString()
@ -81,9 +83,11 @@ public class Configuration
if (configuration.ResultAllInOneSubdirectoryLength is null) throw new NullReferenceException(nameof(configuration.ResultAllInOneSubdirectoryLength));
if (configuration.ResultCollection is null) throw new NullReferenceException(nameof(configuration.ResultCollection));
if (configuration.ResultContent is null) throw new NullReferenceException(nameof(configuration.ResultContent));
if (configuration.ResultContentCollection is null) throw new NullReferenceException(nameof(configuration.ResultContentCollection));
if (configuration.ResultSingleton is null) throw new NullReferenceException(nameof(configuration.ResultSingleton));
if (configuration.RootDirectory is null) throw new NullReferenceException(nameof(configuration.RootDirectory));
if (configuration.ValidImageFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidImageFormatExtensions));
if (configuration.ValidVideoFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidVideoFormatExtensions));
// if (configuration.VerifyToSeason is null) throw new NullReferenceException(nameof(configuration.VerifyToSeason));
result = new(configuration.DateGroup,
configuration.FileNameDirectorySeparator,
@ -106,9 +110,11 @@ public class Configuration
configuration.ResultAllInOneSubdirectoryLength.Value,
configuration.ResultCollection,
configuration.ResultContent,
configuration.ResultContentCollection,
configuration.ResultSingleton,
Path.GetFullPath(configuration.RootDirectory),
configuration.ValidImageFormatExtensions,
configuration.ValidVideoFormatExtensions,
configuration.VerifyToSeason ?? []);
return result;
}

View File

@ -31,8 +31,10 @@ public class Configuration : Shared.Models.Properties.IPropertyConfiguration
public int ResultAllInOneSubdirectoryLength { init; get; }
public string ResultCollection { init; get; }
public string ResultContent { init; get; }
public string ResultContentCollection { init; get; }
public string ResultSingleton { init; get; }
public string[] ValidImageFormatExtensions { init; get; }
public string[] ValidVideoFormatExtensions { init; get; }
[JsonConstructor]
public Configuration(string dateGroup,
@ -56,9 +58,11 @@ public class Configuration : Shared.Models.Properties.IPropertyConfiguration
int resultAllInOneSubdirectoryLength,
string resultCollection,
string resultContent,
string resultContentCollection,
string resultSingleton,
string rootDirectory,
string[] validImageFormatExtensions,
string[] validVideoFormatExtensions,
string[] verifyToSeason)
{
DateGroup = dateGroup;
@ -82,9 +86,11 @@ public class Configuration : Shared.Models.Properties.IPropertyConfiguration
ResultAllInOneSubdirectoryLength = resultAllInOneSubdirectoryLength;
ResultCollection = resultCollection;
ResultContent = resultContent;
ResultContentCollection = resultContentCollection;
ResultSingleton = resultSingleton;
_RootDirectory = rootDirectory;
ValidImageFormatExtensions = validImageFormatExtensions;
ValidVideoFormatExtensions = validVideoFormatExtensions;
}
public override string ToString()

View File

@ -10,49 +10,80 @@ namespace View_by_Distance.Property.Models.Stateless;
public interface IProperty
{
string TestStatic_DateTimeFormat() =>
DateTimeFormat();
static string DateTimeFormat() =>
"yyyy:MM:dd HH:mm:ss";
const string DateTimeFormat = "yyyy:MM:dd HH:mm:ss";
byte[] TestStatic_GetBytes(string value) =>
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);
// public
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);
(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);
static (DateTime?, DateTime[], int?, string?) Get(IPropertyConfiguration propertyConfiguration, bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FileHolder fileHolder, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding) =>
// public
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);
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);
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
{
string TestStatic_GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) =>
GetRelativePath(propertyConfiguration, path);
static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) =>
public static string GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) =>
Result.GetRelativePath(propertyConfiguration, path);
string TestStatic_GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) =>
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) =>
public static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
Result.GetResultsGroupDirectory(propertyConfiguration, description, create: true);
string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
GetResultsDateGroupDirectory(propertyConfiguration, description);
static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
public static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description) =>
Result.GetResultsDateGroupDirectory(propertyConfiguration, description);
string TestStatic_GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) =>
GetResultsDateGroupDirectory(propertyConfiguration, description, jsonGroup);
static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string jsonGroup) =>
public static string GetResultsGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, bool create) =>
Result.GetResultsGroupDirectory(propertyConfiguration, description, create);
public static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description, string 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) =>
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) =>
public 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);
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);
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 dateGroupDirectory, string contentDescription, string singletonDescription, string collectionDescription, bool converted) =>
Result.GetDirectoryInfoCollection(propertyConfiguration, sourceDirectory, dateGroupDirectory, contentDescription, singletonDescription, collectionDescription, converted);
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);
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
{
[GeneratedRegex(@"\D+")]
private static partial Regex Digit();
private static List<DateTime> GetDateTimes(DateTime dateTimeFromName, DateTime?[] dateTimes)
internal static TimeSpan GetThreeStandardDeviationHigh(int minimum, Container.Models.Container container)
{
List<DateTime> results = [dateTimeFromName];
foreach (DateTime? dateTime in dateTimes)
TimeSpan result;
DateTime? minimumDateTime;
List<long> ticksCollection = [];
foreach (Item item in container.Items)
{
if (dateTime is null)
if (item.Property is null)
continue;
results.Add(dateTime.Value);
minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
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 = [];
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;
long result;
ticksCollection = (from l in ticksCollection select l - min).ToList();
double sum = ticksCollection.Sum();
double average = sum / ticksCollection.Count;
double standardDeviation = GetStandardDeviation(ticksCollection, average);
result = (long)Math.Ceiling(average + min + (standardDeviation * 3));
return result;
}
private static List<DateTime> GetDateTimes(FilePath filePath, DateTime?[] dateTimes)
internal static double GetStandardDeviation(List<long> values, double average)
{
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;
double result = 0;
if (values.Count == 0)
throw new Exception("Collection must have at least one value!");
double sum = values.Sum(l => (l - average) * (l - average));
result = Math.Sqrt(sum / values.Count);
return result;
}
internal static byte[] GetBytes(string value)
@ -126,22 +123,40 @@ internal partial class Property
return result;
}
private static List<DateTime> GetDateTimes(DateTime?[] metadataDateTimes)
internal static bool Any(Container.Models.Container[] containers)
{
List<DateTime> results = [];
foreach (DateTime? dateTime in metadataDateTimes)
bool result = false;
foreach (Container.Models.Container container in containers)
{
if (dateTime is null || results.Contains(dateTime.Value))
if (container.Items.Count == 0)
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)
{
#pragma warning disable CA1416
PropertyItem result = (PropertyItem)constructorInfo.Invoke(null);
int length;
byte[] bytes;
@ -161,27 +176,65 @@ internal partial class Property
result.Len = length;
result.Type = type;
result.Value = bytes;
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
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.Property is null)
continue;
minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
if (minimumDateTime is null)
continue;
for (int k = j + 1; k < container.Items.Count; k++)
{
nextItem = container.Items[k];
if (nextItem.Property is null)
continue;
nextMinimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(nextItem.Property);
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)
{
@ -225,6 +278,7 @@ internal partial class Property
{
try
{
#pragma warning disable CA1416
using Image image = Image.FromFile(filePath.FullName);
width = image.Width;
height = image.Height;
@ -240,7 +294,7 @@ internal partial class Property
bitmap.UnlockBits(bitmapData);
id ??= Shared.Models.Stateless.Methods.IId.GetDeterministicHashCode(bytes);
}
dateTimeFormat = IProperty.DateTimeFormat();
dateTimeFormat = IProperty.DateTimeFormat;
if (image.PropertyIdList.Contains(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(';');
}
}
#pragma warning restore CA1416
message = null;
dateTimes = [new(filePath.LastWriteTicks), new(filePath.CreationTicks), dateTime, dateTimeDigitized, dateTimeOriginal, gpsDateStamp];
}
@ -351,19 +406,68 @@ internal partial class Property
return (message, dateTimesByLogic.ToArray(), result);
}
#pragma warning restore CA1416
internal static (DateTime?, DateTime[], int?, string?) Get(bool populateId, IMetadata<MetadataExtractor.Directory>? metadata, FilePath filePath, bool isIgnoreExtension, bool isValidImageFormatExtension, ASCIIEncoding asciiEncoding)
private static List<DateTime> GetDateTimes(DateTime?[] metadataDateTimes)
{
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);
List<DateTime> results = [];
foreach (DateTime? dateTime in metadataDateTimes)
{
if (dateTime is null || results.Contains(dateTime.Value))
continue;
results.Add(dateTime.Value);
}
return results;
}
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 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 GetRelativePath(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string path) =>
Shared.Models.Stateless.Methods.IPath.GetRelativePath(path, propertyConfiguration.RootDirectory.Length);
internal static string GetResultsDateGroupDirectory(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, string description)
{
@ -21,12 +16,66 @@ internal class 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;
}
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)
{
string checkDirectory;
@ -74,58 +123,6 @@ internal class Result
_ = 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)
{
List<string> results;

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Property</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -40,12 +40,13 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
<PackageReference Include="System.Text.Json" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Container\Container.csproj" />
<ProjectReference Include="..\Shared\View-by-Distance.Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -20,7 +20,7 @@ public class Rename
bool IsIgnoreExtension,
bool IsValidImageFormatExtension,
List<FileHolder> FileHolders,
bool FfmpegFilesPresent,
bool FastForwardMovingPictureExpertsGroupFilesPresent,
DateTime? DateTimeOriginal,
DateTime[] DateTimes,
int? Id);
@ -218,11 +218,11 @@ public class Rename
FilePath filePath;
DateTime[] dateTimes;
FileHolder fileHolder;
string[]? ffmpegFiles;
bool isIgnoreExtension;
DateTime? dateTimeOriginal;
bool isValidImageFormatExtension;
ASCIIEncoding asciiEncoding = new();
string[]? fastForwardMovingPictureExpertsGroupFiles;
IReadOnlyList<MetadataExtractor.Directory> directories;
for (int i = 0; i < files.Length; i++)
{
@ -234,7 +234,7 @@ public class Rename
if (string.IsNullOrEmpty(directory))
continue;
filePath = FilePath.Get(_Configuration.PropertyConfiguration, fileHolder, index: i);
if (fileHolder.ExtensionLowered == ".paddedId" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null)
if (fileHolder.ExtensionLowered == ".paddedId" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryFullPath is null)
continue;
if (files.Contains($"{fileHolder.FullName}.paddedId"))
continue;
@ -243,7 +243,7 @@ public class Rename
isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered);
isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered);
if (!isIgnoreExtension && isValidImageFormatExtension)
ffmpegFiles = null;
fastForwardMovingPictureExpertsGroupFiles = null;
else
{
try
@ -251,33 +251,33 @@ public class Rename
catch (Exception) { continue; }
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[] { "-i", files[i], "-vframes", "1", $"{fileHolder.Name}-%4d.jpg" })
.WithWorkingDirectory(fileHolder.DirectoryName)
.WithArguments(["-i", files[i], "-vframes", "1", $"{fileHolder.Name}-%4d.jpg"])
.WithWorkingDirectory(fileHolder.DirectoryFullPath)
.ExecuteAsync();
result.Task.Wait();
ffmpegFiles = Directory.GetFiles(fileHolder.DirectoryName, $"{fileHolder.Name}-*.jpg", SearchOption.TopDirectoryOnly);
if (ffmpegFiles.Length == 0)
fastForwardMovingPictureExpertsGroupFiles = Directory.GetFiles(fileHolder.DirectoryFullPath, $"{fileHolder.Name}-*.jpg", SearchOption.TopDirectoryOnly);
if (fastForwardMovingPictureExpertsGroupFiles.Length == 0)
continue;
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(ffmpegFiles.First());
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(fastForwardMovingPictureExpertsGroupFiles.First());
if (!fileHolder.Name.EndsWith("-0001.jpg"))
throw new Exception();
isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered);
isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered);
if (isIgnoreExtension || !isValidImageFormatExtension)
continue;
if (fileHolder.DirectoryName is null)
if (fileHolder.DirectoryFullPath is null)
continue;
}
(dateTimeOriginal, dateTimes, id, message) = Property.Models.Stateless.IProperty.Get(_PropertyConfiguration, _PropertyConfiguration.PopulatePropertyId, metadata, fileHolder, isIgnoreExtension, isValidImageFormatExtension, asciiEncoding);
if (ffmpegFiles is not null)
if (fastForwardMovingPictureExpertsGroupFiles is not null)
{
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(files[i]);
foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile);
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
if (message is not null)
throw new Exception(message);
results.Add(new(i + offset, isIgnoreExtension, isValidImageFormatExtension, [fileHolder], ffmpegFiles is null, dateTimeOriginal, dateTimes, id));
results.Add(new(i + offset, isIgnoreExtension, isValidImageFormatExtension, [fileHolder], fastForwardMovingPictureExpertsGroupFiles is null, dateTimeOriginal, dateTimes, id));
}
return results;
}
@ -311,7 +311,7 @@ public class Rename
fileHolder = record.FileHolders.First();
if (!fileHolder.Exists)
continue;
if (string.IsNullOrEmpty(fileHolder.DirectoryName))
if (string.IsNullOrEmpty(fileHolder.DirectoryFullPath))
continue;
dateTimeFromName = record.DateTimes.Length == 0 ? null : record.DateTimes.First();
if (fileHolder.ExtensionLowered == jpeg)
@ -320,30 +320,30 @@ public class Rename
{
if (File.Exists($"{fileHolder.FullName}.paddedId"))
{
checkFile = Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}.paddedId");
checkFile = Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension}{jpg}.paddedId");
if (File.Exists(checkFile))
continue;
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName}.paddedId"), fileHolder.DirectoryName, checkFile));
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName}.paddedId"), fileHolder.DirectoryFullPath, checkFile));
}
checkFile = Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}");
checkFile = Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension}{jpg}");
if (File.Exists(checkFile))
continue;
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
results.Add(new(fileHolder, fileHolder.DirectoryName, checkFile));
results.Add(new(fileHolder, fileHolder.DirectoryFullPath, checkFile));
if (nefPresent)
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName[..^4]}.tif"), fileHolder.DirectoryName, $"{checkFile[..^4]}.tif"));
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName[..^4]}.tif"), fileHolder.DirectoryFullPath, $"{checkFile[..^4]}.tif"));
if (nefPresent)
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName[..^4]}.nef"), fileHolder.DirectoryName, $"{checkFile[..^4]}.nef"));
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName[..^4]}.nef"), fileHolder.DirectoryFullPath, $"{checkFile[..^4]}.nef"));
if (File.Exists(checkFile))
continue;
File.Move(fileHolder.FullName, checkFile);
fileHolder = Shared.Models.Stateless.Methods.IFileHolder.Get(checkFile);
if (fileHolder.DirectoryName is null)
if (fileHolder.DirectoryFullPath is null)
continue;
}
}
@ -361,13 +361,13 @@ public class Rename
if (timeSpan is null || timeSpan.Value.TotalMinutes > _AppSettings.MaxMinutesDelta)
{
if (string.IsNullOrEmpty(_AppSettings.DefaultUnknownDirectoryName))
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : fileHolder.DirectoryName);
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : fileHolder.DirectoryFullPath);
else
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : Path.Combine(fileHolder.DirectoryName, _AppSettings.DefaultUnknownDirectoryName));
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : Path.Combine(fileHolder.DirectoryFullPath, _AppSettings.DefaultUnknownDirectoryName));
}
else
{
directoryName = Path.GetFileName(fileHolder.DirectoryName);
directoryName = Path.GetFileName(fileHolder.DirectoryFullPath);
directoryNameSegments = directoryName.Split(' ');
if (dateTimeFromName is null)
isWrongYear = null;
@ -375,7 +375,7 @@ public class Rename
(isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(directoryNameSegments, dateTimeFromName.Value.ToString("yyyy"));
dateTime = minimumDateTime.Value.AddTicks(timeSpan.Value.Ticks);
(season, seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
seasonDirectory = Path.Combine(fileHolder.DirectoryName, $"{dateTime.Year}.{season} {seasonName}");
seasonDirectory = Path.Combine(fileHolder.DirectoryFullPath, $"{dateTime.Year}.{season} {seasonName}");
}
if (seasonDirectory is null || (isWrongYear is not null && isWrongYear.Value))
{
@ -386,23 +386,23 @@ public class Rename
if (minimumDateTime is null)
continue;
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
checkFile = Path.Combine(fileHolder.DirectoryName, $"{minimumDateTime.Value:yyyy-MM-dd}.{minimumDateTime.Value.Ticks}.{fileHolder.Length}{checkFileExtension}");
checkFile = Path.Combine(fileHolder.DirectoryFullPath, $"{minimumDateTime.Value:yyyy-MM-dd}.{minimumDateTime.Value.Ticks}.{fileHolder.Length}{checkFileExtension}");
if (checkFile == fileHolder.FullName)
continue;
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
results.Add(new(fileHolder, fileHolder.DirectoryName, checkFile));
results.Add(new(fileHolder, fileHolder.DirectoryFullPath, checkFile));
if (nefPresent)
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName[..^4]}.tif"), fileHolder.DirectoryName, $"{checkFile[..^4]}.tif"));
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName[..^4]}.tif"), fileHolder.DirectoryFullPath, $"{checkFile[..^4]}.tif"));
if (nefPresent)
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName[..^4]}.nef"), fileHolder.DirectoryName, $"{checkFile[..^4]}.nef"));
results.Add(new(Shared.Models.Stateless.Methods.IFileHolder.Get($"{fileHolder.FullName[..^4]}.nef"), fileHolder.DirectoryFullPath, $"{checkFile[..^4]}.nef"));
}
else
{
if (record.Id is null)
continue;
paddedId = IId.GetPaddedId(_PropertyConfiguration, record.Id.Value, ignore: null, record.Index);
paddedId = IId.GetPaddedId(_PropertyConfiguration, record.Id.Value, hasIgnoreKeyword: null, hasDateTimeOriginal: null, record.Index);
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
checkFile = Path.Combine(seasonDirectory, $"{paddedId}{checkFileExtension}");
if (checkFile == fileHolder.FullName)

View File

@ -4,13 +4,13 @@
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>6e026d2f-9edf-4c6c-a042-162758114e9a</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Rename</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -34,11 +34,11 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.6" />
<PackageReference Include="CliWrap" Version="3.8.1" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Metadata\Metadata.csproj" />

View File

@ -37,14 +37,13 @@ public class C_Resize
private readonly int _OutputResolutionHeightIndex;
private readonly EncoderParameters _EncoderParameters;
private readonly int _OutputResolutionOrientationIndex;
private readonly Dictionary<string, string[]> _FileGroups;
private readonly bool _ForceResizeLastWriteTimeToCreationTime;
private readonly IPropertyConfiguration _PropertyConfiguration;
private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions;
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)
{
_FileGroups = [];
_Original = "Original";
_TempResolutionWidth = 3;
_TempResolutionHeight = 4;
@ -61,11 +60,104 @@ public class C_Resize
_OverrideForResizeImages = overrideForResizeImages;
_PropertiesChangedForResize = propertiesChangedForResize;
_ForceResizeLastWriteTimeToCreationTime = forceResizeLastWriteTimeToCreationTime;
_ResultSingletonFileGroups = [new(new Dictionary<byte, ReadOnlyCollection<string>>())];
_WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception();
_ConstructorInfo = constructorInfo;
}
public static byte[] GetBitmapData(Bitmap bitmap)
{
byte[] results;
#pragma warning disable CA1416
Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height);
BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
IntPtr intPtr = bitmapData.Scan0;
int length = bitmapData.Stride * bitmap.Height;
results = new byte[length];
Marshal.Copy(intPtr, results, 0, length);
bitmap.UnlockBits(bitmapData);
#pragma warning restore CA1416
return results;
}
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetPngLowQuality()
{
(ImageCodecInfo, EncoderParameters, string) result;
#pragma warning disable CA1416
ImageFormat imageFormat = ImageFormat.Png;
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;
}
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;
}
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetTuple(string outputExtension, int outputQuality)
{
(ImageCodecInfo, EncoderParameters, string) result;
#pragma warning disable CA1416
ImageFormat imageFormat = outputExtension switch
{
".gif" => ImageFormat.Gif,
".jfif" => ImageFormat.Jpeg,
".jpe" => ImageFormat.Jpeg,
".jpeg" => ImageFormat.Jpeg,
".jpg" => ImageFormat.Jpeg,
".png" => ImageFormat.Png,
".tif" => ImageFormat.Tiff,
".tiff" => ImageFormat.Tiff,
_ => throw new Exception(),
};
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(Encoder.Quality, CType(75L, Int32)) 'Default
// encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(95L, Int32)) 'Paint
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, outputQuality);
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;
}
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)
{
FileHolder result;
if (outputResolutionHasNumber)
result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(AngleBracketCollection[0].Replace("<>", _PropertyConfiguration.ResultContent), fileName));
else
{
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, filePath);
result = Shared.Models.Stateless.Methods.IFileHolder.Get(Path.Combine(cResultsFullGroupDirectory, _PropertyConfiguration.ResultContent, cei.Combined, fileName));
}
return result;
}
public override string ToString()
{
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
@ -74,10 +166,14 @@ public class C_Resize
public void Update(string cResultsFullGroupDirectory)
{
_FileGroups.Clear();
ReadOnlyDictionary<string, string[]> keyValuePairs = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, [_PropertyConfiguration.ResultSingleton]);
foreach (KeyValuePair<string, string[]> keyValuePair in keyValuePairs)
_FileGroups.Add(keyValuePair.Key, keyValuePair.Value);
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)
@ -92,358 +188,20 @@ public class C_Resize
converted: true));
}
#pragma warning disable CA1416
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetGifLowQuality()
{
(ImageCodecInfo, EncoderParameters, string) result;
ImageFormat imageFormat = ImageFormat.Gif;
ImageCodecInfo imageCodecInfo = (from l in ImageCodecInfo.GetImageEncoders() 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..]);
return result;
}
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetPngLowQuality()
{
(ImageCodecInfo, EncoderParameters, string) result;
ImageFormat imageFormat = ImageFormat.Png;
ImageCodecInfo imageCodecInfo = (from l in ImageCodecInfo.GetImageEncoders() 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..]);
return result;
}
public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetTuple(string outputExtension, int outputQuality)
{
(ImageCodecInfo, EncoderParameters, string) result;
ImageFormat imageFormat = outputExtension switch
{
".gif" => ImageFormat.Gif,
".jfif" => ImageFormat.Jpeg,
".jpe" => ImageFormat.Jpeg,
".jpeg" => ImageFormat.Jpeg,
".jpg" => ImageFormat.Jpeg,
".png" => ImageFormat.Png,
".tif" => ImageFormat.Tiff,
".tiff" => ImageFormat.Tiff,
_ => throw new Exception(),
};
ImageCodecInfo imageCodecInfo = (from l in ImageCodecInfo.GetImageEncoders() where l.FormatID == imageFormat.Guid select l).First();
EncoderParameters encoderParameters = new(1);
// encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(75L, Int32)) 'Default
// encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(95L, Int32)) 'Paint
encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, outputQuality);
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 byte[] GetBitmapData(Bitmap bitmap)
{
byte[] results;
Rectangle rectangle = new(0, 0, bitmap.Width, bitmap.Height);
BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
IntPtr intPtr = bitmapData.Scan0;
int length = bitmapData.Stride * bitmap.Height;
results = new byte[length];
Marshal.Copy(intPtr, results, 0, length);
bitmap.UnlockBits(bitmapData);
return results;
}
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;
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);
}
}
private void SaveResizedSubfile3(MappingFromItem mappingFromItem, int[] resize, byte[] bytes)
{
Bitmap bitmap;
int outputResolutionWidth = resize[_OutputResolutionWidthIndex];
using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false);
int outputResolutionHeight = resize[_OutputResolutionHeightIndex];
PropertyItem[] propertyItems = temp.PropertyItems;
int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex];
bitmap = new(temp, outputResolutionWidth, outputResolutionHeight);
switch (outputResolutionOrientation) // exif 274
{
case 0:
break;
case 1:
break; // 1 = Horizontal (normal)
case 2:
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX);
break; // 2 = Mirror horizontal
case 3:
bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
break; // 3 = Rotate 180
case 4:
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
break; // 4 = Mirror vertical
case 5:
bitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
break; // 5 = Mirror horizontal and rotate 270 CW
case 6:
bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
break; // 6 = Rotate 90 CW
case 7:
bitmap.RotateFlip(RotateFlipType.Rotate90FlipX);
break; // 7 = Mirror horizontal and rotate 90 CW
case 8:
bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
break; // 8 = Rotate 270 CW
default:
break;
}
CopyPropertyItems(bytes, propertyItems, bitmap);
bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
bitmap.Dispose();
}
private void SaveResizedSubfile5(MappingFromItem mappingFromItem, int[] resize, byte[] bytes)
{
Bitmap bitmap;
using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false);
PropertyItem[] propertyItems = temp.PropertyItems;
int tempResolutionWidth = resize[_TempResolutionWidth];
int tempResolutionHeight = resize[_TempResolutionHeight];
int outputResolutionWidth = resize[_OutputResolutionWidthIndex];
int outputResolutionHeight = resize[_OutputResolutionHeightIndex];
int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex];
bitmap = new(temp, tempResolutionWidth, tempResolutionHeight);
switch (outputResolutionOrientation) // exif 274
{
case 0:
break;
case 1:
break; // 1 = Horizontal (normal)
case 2:
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX);
break; // 2 = Mirror horizontal
case 3:
bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
break; // 3 = Rotate 180
case 4:
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
break; // 4 = Mirror vertical
case 5:
bitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
break; // 5 = Mirror horizontal and rotate 270 CW
case 6:
bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
break; // 6 = Rotate 90 CW
case 7:
bitmap.RotateFlip(RotateFlipType.Rotate90FlipX);
break; // 7 = Mirror horizontal and rotate 90 CW
case 8:
bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
break; // 8 = Rotate 270 CW
default:
break;
}
Bitmap preRotated;
Rectangle rectangle;
if (tempResolutionHeight < tempResolutionWidth)
rectangle = new Rectangle((int)((tempResolutionWidth - outputResolutionWidth) * .5), 0, outputResolutionWidth, outputResolutionHeight);
else
rectangle = new Rectangle(0, (int)((tempResolutionHeight - outputResolutionHeight) * .5), outputResolutionWidth, outputResolutionHeight);
using (preRotated = new Bitmap(outputResolutionWidth, outputResolutionHeight))
{
using (Graphics graphics = Graphics.FromImage(preRotated))
graphics.DrawImage(bitmap, new Rectangle(0, 0, outputResolutionWidth, outputResolutionHeight), rectangle, GraphicsUnit.Pixel);
CopyPropertyItems(bytes, propertyItems, bitmap);
bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
}
bitmap.Dispose();
}
#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"));
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);
@ -499,4 +257,285 @@ public class C_Resize
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 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 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, 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 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();
}
private void SaveResizedSubfile3(MappingFromItem mappingFromItem, int[] resize, byte[] bytes)
{
Bitmap bitmap;
#pragma warning disable CA1416
int outputResolutionWidth = resize[_OutputResolutionWidthIndex];
using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false);
int outputResolutionHeight = resize[_OutputResolutionHeightIndex];
PropertyItem[] propertyItems = temp.PropertyItems;
int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex];
bitmap = new(temp, outputResolutionWidth, outputResolutionHeight);
switch (outputResolutionOrientation) // exif 274
{
case 0:
break;
case 1:
break; // 1 = Horizontal (normal)
case 2:
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX);
break; // 2 = Mirror horizontal
case 3:
bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
break; // 3 = Rotate 180
case 4:
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
break; // 4 = Mirror vertical
case 5:
bitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
break; // 5 = Mirror horizontal and rotate 270 CW
case 6:
bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
break; // 6 = Rotate 90 CW
case 7:
bitmap.RotateFlip(RotateFlipType.Rotate90FlipX);
break; // 7 = Mirror horizontal and rotate 90 CW
case 8:
bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
break; // 8 = Rotate 270 CW
default:
break;
}
CopyPropertyItems(bytes, propertyItems, bitmap);
bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
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)
{
Bitmap bitmap;
#pragma warning disable CA1416
using Bitmap temp = new(mappingFromItem.FilePath.FullName, useIcm: false);
PropertyItem[] propertyItems = temp.PropertyItems;
int tempResolutionWidth = resize[_TempResolutionWidth];
int tempResolutionHeight = resize[_TempResolutionHeight];
int outputResolutionWidth = resize[_OutputResolutionWidthIndex];
int outputResolutionHeight = resize[_OutputResolutionHeightIndex];
int outputResolutionOrientation = resize[_OutputResolutionOrientationIndex];
bitmap = new(temp, tempResolutionWidth, tempResolutionHeight);
switch (outputResolutionOrientation) // exif 274
{
case 0:
break;
case 1:
break; // 1 = Horizontal (normal)
case 2:
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipX);
break; // 2 = Mirror horizontal
case 3:
bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
break; // 3 = Rotate 180
case 4:
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
break; // 4 = Mirror vertical
case 5:
bitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
break; // 5 = Mirror horizontal and rotate 270 CW
case 6:
bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
break; // 6 = Rotate 90 CW
case 7:
bitmap.RotateFlip(RotateFlipType.Rotate90FlipX);
break; // 7 = Mirror horizontal and rotate 90 CW
case 8:
bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
break; // 8 = Rotate 270 CW
default:
break;
}
Bitmap preRotated;
Rectangle rectangle;
if (tempResolutionHeight < tempResolutionWidth)
rectangle = new Rectangle((int)((tempResolutionWidth - outputResolutionWidth) * .5), 0, outputResolutionWidth, outputResolutionHeight);
else
rectangle = new Rectangle(0, (int)((tempResolutionHeight - outputResolutionHeight) * .5), outputResolutionWidth, outputResolutionHeight);
using (preRotated = new Bitmap(outputResolutionWidth, outputResolutionHeight))
{
using (Graphics graphics = Graphics.FromImage(preRotated))
graphics.DrawImage(bitmap, new Rectangle(0, 0, outputResolutionWidth, outputResolutionHeight), rectangle, GraphicsUnit.Pixel);
CopyPropertyItems(bytes, propertyItems, bitmap);
bitmap.Save(mappingFromItem.ResizedFileHolder.FullName, _ImageCodecInfo, _EncoderParameters);
}
bitmap.Dispose();
#pragma warning restore CA1416
}
}

View File

@ -4,12 +4,12 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Resize</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>8.0.101.1</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>
@ -34,8 +34,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
<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" />

View File

@ -34,17 +34,15 @@ public class Configuration
}
}
private static Models.Configuration Get(Configuration? configuration)
private static Models.Configuration Get(Configuration? configuration, Property.Models.Configuration propertyConfiguration)
{
Models.Configuration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.PersonBirthdayFormat is null) throw new NullReferenceException(nameof(configuration.PersonBirthdayFormat));
if (configuration.PropertyConfiguration is null) throw new NullReferenceException(nameof(configuration.PropertyConfiguration));
result = new(
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat,
configuration.PropertyConfiguration);
result = new(propertyConfiguration,
configuration.IgnoreExtensions,
configuration.PersonBirthdayFormat);
return result;
}
@ -66,7 +64,7 @@ public class Configuration
#pragma warning restore IL3050, IL2026
}
PreVerify(configurationRoot, configuration);
result = Get(configuration);
result = Get(configuration, propertyConfiguration);
return result;
}

View File

@ -13,10 +13,9 @@ public class Configuration
public Property.Models.Configuration PropertyConfiguration => _PropertyConfiguration;
[JsonConstructor]
public Configuration(
string[] ignoreExtensions,
string personBirthdayFormat,
Property.Models.Configuration propertyConfiguration)
public Configuration(Property.Models.Configuration propertyConfiguration,
string[] ignoreExtensions,
string personBirthdayFormat)
{
IgnoreExtensions = ignoreExtensions;
PersonBirthdayFormat = personBirthdayFormat;

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