15 Commits

Author SHA1 Message Date
ba11c04f4a Pack and published 8.0.118.14728
Metadata alignment
2025-07-19 09:25:58 -07:00
05fb8685d9 Removed Rectangle from LocationContainer
mapped-ids-then-whole-percentages-to-location-container

save-extracted-face

save-extracted-java-script-object-notation
2025-07-06 10:45:59 -07:00
2c9b1a68c0 House Cleaning 2025-07-05 13:37:47 -07:00
8f7fd02ba8 Interface over passing ticks 2025-07-04 09:11:05 -07:00
93598255c0 Changed GetDimensions to handle a stream at the end and one exit 2025-06-29 08:33:58 -07:00
9a51d995cc validation-image-file 2025-04-20 14:16:03 -07:00
23256c8152 Alignment with Console 2025-04-06 15:45:10 -07:00
1bbe583359 Pack for nuget 2025-04-06 09:34:23 -07:00
9fb6f0fa05 Mostly Sorting
Video Merge as 4, 5, and 6

Enum change to last character
2025-03-22 17:18:30 -07:00
ae23e803fa added-logic-to-rename-to-3-and-7-like-should-ignore-for-missing-date-time-original 2025-03-16 22:22:29 -07:00
c15c854481 Ready to test DownloadFile
DirectoryDictionary

Add http file

.7-Question

JustMediaDate

CombinedEnumAndIndex
2025-03-08 17:28:30 -07:00
ef4672aaf0 Removed extra method 2025-03-02 08:39:32 -07:00
3ea4926f5e Ready to test Windows Project 2025-02-16 21:30:17 -07:00
039355f31e net9.0 2025-02-15 12:18:58 -07:00
68dff9c24c Nuget Pack
net8
2025-02-08 17:03:02 -07:00
81 changed files with 4254 additions and 2408 deletions

2
.gitignore vendored
View File

@ -474,3 +474,5 @@ Compare/.vscode/.UserSecrets/secrets.json
Rename/.vscode/.UserSecrets/secrets.json Rename/.vscode/.UserSecrets/secrets.json
Rename/.vscode/.UserSecretsOld/rename.json Rename/.vscode/.UserSecretsOld/rename.json
Rename/.vscode/.UserSecretsOld/secrets.json Rename/.vscode/.UserSecretsOld/secrets.json
Windows/.vscode/.UserSecrets/secrets.json
Windows/.vscode/.7-Question

23
.vscode/launch.json vendored
View File

@ -1,6 +1,23 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"name": "Compare",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "Build-Compare",
"program": "${workspaceFolder}/Compare/bin/Debug/net9.0/win-x64/AA.Compare.dll",
"args": [
"s"
],
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"stopAtEntry": false,
"requireExactSource": false
},
{ {
"name": "Rename", "name": "Rename",
"type": "coreclr", "type": "coreclr",
@ -19,11 +36,11 @@
"requireExactSource": false "requireExactSource": false
}, },
{ {
"name": "Compare", "name": "Windows",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "Build-Compare", "preLaunchTask": "Build-Windows",
"program": "${workspaceFolder}/Compare/bin/Debug/net9.0/win-x64/AA.Compare.dll", "program": "${workspaceFolder}/Windows/bin/Debug/net9.0/win-x64/AA.Windows.dll",
"args": [ "args": [
"s" "s"
], ],

2
.vscode/mklink.md vendored
View File

@ -26,6 +26,8 @@ mklink /J "L:\Git\AA\Compare\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roami
```bash 1736011475498 = 638716082754980000 = Sat Jan 04 2025 10:24:35 GMT-0700 (Mountain Standard Time) ```bash 1736011475498 = 638716082754980000 = Sat Jan 04 2025 10:24:35 GMT-0700 (Mountain Standard Time)
mklink /J "L:\Git\AA\Rename\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\843db3e1-e18f-4cba-8b00-967529a32635" mklink /J "L:\Git\AA\Rename\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\843db3e1-e18f-4cba-8b00-967529a32635"
mklink /J "L:\Git\AA\Compare\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\770b6ae3-266e-4d5f-970a-173709b064de" mklink /J "L:\Git\AA\Compare\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\770b6ae3-266e-4d5f-970a-173709b064de"
mklink /J "L:\Git\AA\Windows\.vscode\.UserSecrets" "C:\Users\mikep\AppData\Roaming\Microsoft\UserSecrets\076c87e8-c7f0-40a3-aba3-73eb7f9ea892"
mklink /J "L:\Git\AA\Windows\.vscode\.iCloudPhotos2025" "D:\7-Question\iCloud Photos 2025"
``` ```
```json 1735493575037 = 638710903750370000 = Sun Dec 29 2024 10:32:54 GMT-0700 (Mountain Standard Time) ```json 1735493575037 = 638710903750370000 = Sun Dec 29 2024 10:32:54 GMT-0700 (Mountain Standard Time)

1
.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

View File

@ -40,6 +40,7 @@
"Rects", "Rects",
"resnet", "resnet",
"Serilog", "Serilog",
"snupkg",
"Subfile", "Subfile",
"Subfiles", "Subfiles",
"Syncthing", "Syncthing",

173
.vscode/tasks.json vendored
View File

@ -8,7 +8,7 @@
"args": [ "args": [
"user-secrets", "user-secrets",
"-p", "-p",
"${workspaceFolder}/Rename/AA.Rename.csproj", "${workspaceFolder}/Windows/AA.Windows.csproj",
"init" "init"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
@ -20,10 +20,10 @@
"args": [ "args": [
"user-secrets", "user-secrets",
"-p", "-p",
"${workspaceFolder}/Rename/AA.Rename.csproj", "${workspaceFolder}/Windows/AA.Windows.csproj",
"set", "set",
"_Application", "_Application",
"Rename" "Windows"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
@ -123,6 +123,126 @@
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{
"label": "Build-Windows",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/Windows/AA.Windows.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-Compare",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/Compare/AA.Compare.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-Distance",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/Distance/AA.Distance.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-Face",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/Face/AA.Face.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-Face-Recognition-DotNet",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/FaceRecognitionDotNet/AA.FaceRecognitionDotNet.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-Metadata",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/Metadata/AA.Metadata.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-People",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/People/AA.People.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-Rename",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/Rename/AA.Rename.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-Shared",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/Shared/AA.Shared.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "Pack-Windows",
"command": "dotnet",
"type": "process",
"args": [
"pack",
"${workspaceFolder}/Windows/AA.Windows.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{ {
"label": "Format-Compare-Whitespaces", "label": "Format-Compare-Whitespaces",
"command": "dotnet", "command": "dotnet",
@ -339,6 +459,22 @@
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{
"label": "Format-Windows",
"command": "dotnet",
"type": "process",
"args": [
"format",
"${workspaceFolder}/Windows/AA.Windows.csproj",
"--report",
".vscode",
"--verbosity",
"detailed",
"--severity",
"warn"
],
"problemMatcher": "$msCompile"
},
{ {
"label": "AOT-Compare", "label": "AOT-Compare",
"command": "dotnet", "command": "dotnet",
@ -475,5 +611,36 @@
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
{
"label": "AOT-Windows",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"-r",
"win-x64",
"-c",
"Release",
"-p:PublishAot=true",
"${workspaceFolder}/Windows/AA.Windows.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/AA",
"Day-Helper-2025-03-20",
"false",
"4"
],
"problemMatcher": []
}
] ]
} }

1
Compare/.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
@ -7,14 +7,19 @@
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<UserSecretsId>770b6ae3-266e-4d5f-970a-173709b064de</UserSecretsId> <UserSecretsId>770b6ae3-266e-4d5f-970a-173709b064de</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>Phares.View.by.Distance.Compare</PackageId> <PackageId>Phares.View.by.Distance.Compare</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>9.0.100.0</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company> <Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<PackageReadmeFile>read-me.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows> <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@ -34,10 +39,13 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="9.0.0" /> <None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> </ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.14" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,6 +1,7 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using ShellProgressBar; using ShellProgressBar;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Drawing;
using View_by_Distance.Compare.Models; using View_by_Distance.Compare.Models;
using View_by_Distance.Distance.Models.Stateless; using View_by_Distance.Distance.Models.Stateless;
using View_by_Distance.Face.Models.Stateless; using View_by_Distance.Face.Models.Stateless;
@ -14,9 +15,27 @@ namespace View_by_Distance.Compare;
public partial class Compare : ICompare, IDisposable public partial class Compare : ICompare, IDisposable
{ {
public long Ticks { get; init; }
public int? CurrentTick => _ProgressBar?.CurrentTick;
private ProgressBar? _ProgressBar; private ProgressBar? _ProgressBar;
private readonly ProgressBarOptions _ProgressBarOptions; private readonly ProgressBarOptions _ProgressBarOptions;
void ICompare.Tick() =>
_ProgressBar?.Tick();
void IDisposable.Dispose()
{
_ProgressBar?.Dispose();
GC.SuppressFinalize(this);
}
void ICompare.ConstructProgressBar(int maxTicks, string message)
{
_ProgressBar?.Dispose();
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
}
public Compare(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console) public Compare(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
{ {
if (isSilent) if (isSilent)
@ -26,27 +45,56 @@ public partial class Compare : ICompare, IDisposable
if (console is null) if (console is null)
throw new NullReferenceException(nameof(console)); throw new NullReferenceException(nameof(console));
ICompare compare = this; ICompare compare = this;
long ticks = DateTime.Now.Ticks; Ticks = DateTime.Now.Ticks;
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; _ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
CompareWork(logger, appSettings, compare, ticks); CompareWork(logger, appSettings, compare);
} }
void ICompare.Tick() => private void CompareWork(ILogger<Program>? logger, AppSettings appSettings, ICompare compare)
_ProgressBar?.Tick();
void ICompare.ConstructProgressBar(int maxTicks, string message)
{ {
_ProgressBar?.Dispose(); const int updated = 0;
_ProgressBar = new(maxTicks, message, _ProgressBarOptions); DistanceLimits? distanceLimits;
} ReadOnlyCollection<LocationContainer> matrix;
logger?.LogInformation("{Ticks}", compare.Ticks);
void IDisposable.Dispose() ReadOnlyCollection<SaveContainer> saveContainers;
ReadOnlyCollection<ExifDirectory> exifDirectories;
ReadOnlyCollection<LocationContainer> preFiltered;
ReadOnlyCollection<LocationContainer> postFiltered;
ReadOnlyDictionary<string, LocationContainer> onlyOne;
bool runToDoCollectionFirst = GetRunToDoCollectionFirst(appSettings, compare);
ReadOnlyCollections readOnlyCollections = GetReadOnlyCollections(appSettings);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer = GetMappedIdsThenWholePercentagesToLocationContainer(appSettings, compare, readOnlyCollections);
if (appSettings.CompareSettings.SaveExtractedFaces || appSettings.CompareSettings.SaveExtractedJavaScriptObjectNotation)
SaveExtracted(appSettings, mappedIdsThenWholePercentagesToLocationContainer);
foreach (string outputResolution in appSettings.CompareSettings.OutputResolutions)
{ {
_ProgressBar?.Dispose(); if (runToDoCollectionFirst || outputResolution.Any(char.IsNumber))
GC.SuppressFinalize(this); continue;
logger?.LogInformation("{outputResolution}", outputResolution);
exifDirectories = IFace.GetExifDirectories(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, outputResolution);
preFiltered = IDistance.GetPreFilterLocationContainer(appSettings.DistanceSettings, appSettings.CompareSettings, compare, readOnlyCollections, mappedIdsThenWholePercentagesToLocationContainer, exifDirectories);
if (preFiltered.Count == 0)
continue;
if (appSettings.CompareSettings.SaveExtractedFaces || appSettings.CompareSettings.SaveExtractedJavaScriptObjectNotation)
SaveExtracted(appSettings, preFiltered);
distanceLimits = new(appSettings.DistanceSettings);
postFiltered = IDistance.GetPostFilterLocationContainer(preFiltered, distanceLimits);
if (postFiltered.Count == 0)
continue;
matrix = IDistance.GetMatrixLocationContainers(appSettings.DistanceSettings, compare, mappedIdsThenWholePercentagesToLocationContainer, distanceLimits, postFiltered);
if (matrix.Count == 0)
continue;
onlyOne = IDistance.GetOnlyOne(appSettings.DistanceSettings, matrix);
if (onlyOne.Count == 0)
continue;
saveContainers = IDistance.GetSaveContainers(appSettings.ResultSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, outputResolution, onlyOne);
if (saveContainers.Count == 0)
continue;
IDistance.SaveContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, updated, saveContainers);
}
} }
private static bool GetRunToDoCollectionFirst(AppSettings appSettings, long ticks) private static bool GetRunToDoCollectionFirst(AppSettings appSettings, ICompare compare)
{ {
bool result = appSettings.DistanceSettings.SaveSortingWithoutPerson; bool result = appSettings.DistanceSettings.SaveSortingWithoutPerson;
if (!result) if (!result)
@ -61,7 +109,7 @@ public partial class Compare : ICompare, IDisposable
{ {
string seasonDirectory; string seasonDirectory;
DirectoryInfo directoryInfo; DirectoryInfo directoryInfo;
DateTime dateTime = new(ticks); DateTime dateTime = new(compare.Ticks);
string rootDirectory = appSettings.ResultSettings.RootDirectory; string rootDirectory = appSettings.ResultSettings.RootDirectory;
(int season, string seasonName) = IDate.GetSeason(dateTime.DayOfYear); (int season, string seasonName) = IDate.GetSeason(dateTime.DayOfYear);
string eDistanceContentDirectory = IResult.GetResultsDateGroupDirectory(appSettings.ResultSettings, nameof(E_Distance), appSettings.ResultSettings.ResultContent); string eDistanceContentDirectory = IResult.GetResultsDateGroupDirectory(appSettings.ResultSettings, nameof(E_Distance), appSettings.ResultSettings.ResultContent);
@ -111,58 +159,127 @@ public partial class Compare : ICompare, IDisposable
return result; return result;
} }
private static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(AppSettings appSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) private static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMappedIdsThenWholePercentagesToLocationContainer(AppSettings appSettings, ICompare compare, ReadOnlyCollections readOnlyCollections)
{ {
ReadOnlyCollection<ExifDirectory> results;
ReadOnlyCollection<ExifDirectory> exifDirectories = IDistance.GetMapped(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.PeopleSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, readOnlyCollections); ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> results;
if (exifDirectories.Count == 0 && !appSettings.DistanceSettings.SaveSortingWithoutPerson) results = IDistance.GetMappedIdsThenWholePercentagesToLocationContainer(appSettings.ResultSettings,
throw new NotSupportedException($"Switch {nameof(appSettings.DistanceSettings.SaveSortingWithoutPerson)}!"); appSettings.MetadataSettings,
results = IDistance.GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories); appSettings.PeopleSettings,
appSettings.DistanceSettings,
appSettings.CompareSettings,
compare,
readOnlyCollections);
if (results.Count == 0 && !appSettings.DistanceSettings.SaveSortingWithoutPerson) if (results.Count == 0 && !appSettings.DistanceSettings.SaveSortingWithoutPerson)
throw new NotSupportedException($"Switch {nameof(appSettings.DistanceSettings.SaveSortingWithoutPerson)}!"); throw new NotSupportedException($"Switch {nameof(appSettings.DistanceSettings.SaveSortingWithoutPerson)}!");
return results; return results;
} }
private void CompareWork(ILogger<Program>? logger, AppSettings appSettings, ICompare compare, long ticks) private void SaveExtracted(AppSettings appSettings, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer)
{ {
const int updated = 0; ReadOnlyCollection<string>? paths;
DistanceLimits? distanceLimits; ReadOnlyDictionary<string, ReadOnlyCollection<string>> rootDirectoryFileNameToPaths = GetRootDirectoryFileNameToPaths(appSettings.ResultSettings, appSettings.CompareSettings);
logger?.LogInformation("{Ticks}", ticks); foreach (KeyValuePair<int, ReadOnlyDictionary<int, LocationContainer>> keyValuePair in mappedIdsThenWholePercentagesToLocationContainer)
ReadOnlyCollection<LocationContainer> matrix;
ReadOnlyCollection<SaveContainer> saveContainers;
ReadOnlyCollection<ExifDirectory> exifDirectories;
ReadOnlyCollection<LocationContainer> preFiltered;
ReadOnlyCollection<LocationContainer> postFiltered;
ReadOnlyDictionary<string, LocationContainer> onlyOne;
bool runToDoCollectionFirst = GetRunToDoCollectionFirst(appSettings, ticks);
ReadOnlyCollections readOnlyCollections = GetReadOnlyCollections(appSettings);
ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding = GetMappedExifDirectoryWithEncoding(appSettings, compare, ticks, readOnlyCollections);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs = IDistance.Extract(appSettings.CompareSettings, mappedExifDirectoryWithEncoding);
foreach (string outputResolution in appSettings.CompareSettings.OutputResolutions)
{ {
if (runToDoCollectionFirst || outputResolution.Any(char.IsNumber)) foreach (KeyValuePair<int, LocationContainer> keyValue in keyValuePair.Value)
{
if (keyValue.Value.ExifDirectory is null || keyValue.Value.FaceFile?.Location is null)
continue; continue;
_ProgressBar?.Dispose(); if (rootDirectoryFileNameToPaths.TryGetValue(keyValue.Value.FilePath.FileNameFirstSegment, out paths))
logger?.LogInformation("{outputResolution}", outputResolution); {
exifDirectories = IFace.GetExifDirectories(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, outputResolution); if (appSettings.CompareSettings.SaveExtractedJavaScriptObjectNotation && keyValue.Value.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is not null)
preFiltered = IDistance.GetPreFilterLocationContainer(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories); SaveExtractedJavaScriptObjectNotation(keyValue.Key, keyValue.Value, paths);
if (preFiltered.Count == 0) if (appSettings.CompareSettings.SaveExtractedFaces)
continue; SaveExtractedFace(keyValuePair.Key, keyValue.Key, keyValue.Value.ExifDirectory, keyValue.Value.FaceFile.Location, keyValue.Value.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, paths);
distanceLimits = new(appSettings.DistanceSettings); }
postFiltered = IDistance.GetPostFilterLocationContainer(preFiltered, distanceLimits); }
if (postFiltered.Count == 0)
continue;
matrix = IDistance.GetMatrixLocationContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
if (matrix.Count == 0)
continue;
onlyOne = IDistance.GetOnlyOne(appSettings.DistanceSettings, matrix);
if (onlyOne.Count == 0)
continue;
saveContainers = IDistance.GetSaveContainers(appSettings.ResultSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, outputResolution, onlyOne);
if (saveContainers.Count == 0)
continue;
IDistance.SaveContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, updated, saveContainers);
} }
} }
private static void SaveExtracted(AppSettings appSettings, ReadOnlyCollection<LocationContainer> preFiltered)
{
ReadOnlyCollection<string>? paths;
ReadOnlyDictionary<string, ReadOnlyCollection<string>> rootDirectoryFileNameToPaths = GetRootDirectoryFileNameToPaths(appSettings.ResultSettings, appSettings.CompareSettings);
foreach (LocationContainer locationContainer in preFiltered)
{
if (locationContainer?.FilePath?.Id is null || locationContainer?.WholePercentages is null || locationContainer?.ExifDirectory is null || locationContainer?.FaceFile?.Location is null)
continue;
if (rootDirectoryFileNameToPaths.TryGetValue(locationContainer.FilePath.FileNameFirstSegment, out paths))
{
if (appSettings.CompareSettings.SaveExtractedJavaScriptObjectNotation && locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is not null)
SaveExtractedJavaScriptObjectNotation(locationContainer.WholePercentages.Value, locationContainer, paths);
if (appSettings.CompareSettings.SaveExtractedFaces)
SaveExtractedFace(locationContainer.FilePath.Id.Value, locationContainer.WholePercentages.Value, locationContainer.ExifDirectory, locationContainer.FaceFile.Location, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, paths);
}
}
}
private static ReadOnlyDictionary<string, ReadOnlyCollection<string>> GetRootDirectoryFileNameToPaths(ResultSettings resultSettings, CompareSettings compareSettings)
{
Dictionary<string, ReadOnlyCollection<string>> results = [];
string key;
string extension;
List<string>? collection;
Dictionary<string, List<string>> keyValuePairs = [];
string[] files = !compareSettings.SaveExtractedFaces && !compareSettings.SaveExtractedJavaScriptObjectNotation ? [] : Directory.GetFiles(resultSettings.RootDirectory, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
extension = Path.GetExtension(file);
if (resultSettings.IgnoreExtensions.Contains(extension))
continue;
key = Path.GetFileNameWithoutExtension(file);
if (!keyValuePairs.TryGetValue(key, out collection))
{
keyValuePairs.Add(key, []);
if (!keyValuePairs.TryGetValue(key, out collection))
throw new Exception();
}
collection.Add(file);
}
foreach (KeyValuePair<string, List<string>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return results.AsReadOnly();
}
private static void SaveExtractedJavaScriptObjectNotation(int wholePercentages, LocationContainer locationContainer, ReadOnlyCollection<string> paths)
{
string file;
string json;
foreach (string path in paths)
{
json = locationContainer.GetWithoutEncoding();
file = $"{path}-{wholePercentages}.json";
_ = IPath.WriteAllText(file, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
}
}
private static void SaveExtractedFace(int id, int wholePercentages, ExifDirectory _, Location location, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, ReadOnlyCollection<string> paths)
{
int width;
int height;
string person;
foreach (string path in paths)
{
width = location.Right - location.Left;
height = location.Bottom - location.Top;
person = personKeyFormattedAndKeyTicksAndDisplayDirectoryName is null ? string.Empty : $"-{personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName}";
ExtractFace(file: path,
width: width,
height: height,
left: location.Left,
top: location.Top,
suffix: $"-{id}-{wholePercentages}{person}-face.jpg");
}
}
private static void ExtractFace(string file, float width, float height, double left, double top, string suffix)
{
RectangleF rectangle = new((float)left, (float)top, width, height);
using Bitmap source = new(file);
using Bitmap bitmap = new((int)width, (int)height);
using (Graphics graphics = Graphics.FromImage(bitmap))
graphics.DrawImage(source, new RectangleF(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
bitmap.Save($"{file}{suffix}");
}
} }

View File

@ -20,16 +20,16 @@ public record AppSettings(ResultSettings ResultSettings,
private static void Verify(AppSettings appSettings) private static void Verify(AppSettings appSettings)
{ {
if (appSettings.DistanceSettings.RangeDaysDeltaTolerance.Length != 3) if (appSettings.DistanceSettings.RangeDaysDeltaTolerance.Length is not 3 and not 6)
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDaysDeltaTolerance)); throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDaysDeltaTolerance));
if (appSettings.DistanceSettings.RangeDistanceTolerance.Length != 3) if (appSettings.DistanceSettings.RangeDistanceTolerance.Length is not 3 and not 6)
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDistanceTolerance)); throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDistanceTolerance));
if (appSettings.DistanceSettings.RangeFaceAreaTolerance.Length != 3) if (appSettings.DistanceSettings.RangeFaceAreaTolerance.Length is not 3 and not 6)
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceAreaTolerance)); throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceAreaTolerance));
if (appSettings.DistanceSettings.RangeFaceConfidence.Length != 3) if (appSettings.DistanceSettings.RangeFaceConfidence.Length is not 3 and not 6)
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceConfidence)); throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceConfidence));
_ = DateTime.Now.AddDays(-appSettings.DistanceSettings.RangeDaysDeltaTolerance[1]); _ = DateTime.Now.AddDays(-appSettings.DistanceSettings.RangeDaysDeltaTolerance[1]);
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && appSettings.PeopleSettings.JLinks.Length > 0) if (appSettings.DistanceSettings.SaveSortingWithoutPerson && appSettings.PeopleSettings.JLinks.Where(l => !string.IsNullOrEmpty(l)).Any())
throw new Exception("Settings has SaveSortingWithoutPerson and JLinks!"); throw new Exception("Settings has SaveSortingWithoutPerson and JLinks!");
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && !string.IsNullOrEmpty(appSettings.DistanceSettings.FocusModel)) if (appSettings.DistanceSettings.SaveSortingWithoutPerson && !string.IsNullOrEmpty(appSettings.DistanceSettings.FocusModel))
throw new Exception("Settings has SaveSortingWithoutPerson and FocusModel!"); throw new Exception("Settings has SaveSortingWithoutPerson and FocusModel!");

View File

@ -8,11 +8,10 @@ public record CompareSettings(string Company,
string FacesFileNameExtension, string FacesFileNameExtension,
string FacesHiddenFileNameExtension, string FacesHiddenFileNameExtension,
string FacesPartsFileNameExtension, string FacesPartsFileNameExtension,
string[] IgnoreExtensions,
int MaxDegreeOfParallelism, int MaxDegreeOfParallelism,
string[] OutputResolutions, string[] OutputResolutions,
string[] ValidImageFormatExtensions, bool SaveExtractedFaces,
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.ICompareSettings bool SaveExtractedJavaScriptObjectNotation) : Shared.Models.Properties.ICompareSettings
{ {
public override string ToString() public override string ToString()

1
Distance/.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

View File

@ -6,14 +6,19 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>Phares.AA.Distance</PackageId> <PackageId>Phares.AA.Distance</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>9.0.100.0</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company> <Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<PackageReadmeFile>read-me.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows> <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@ -32,10 +37,13 @@
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'"> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,6 +1,7 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Text.Json; using System.Text.Json;
using View_by_Distance.Shared.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;
namespace View_by_Distance.Distance.Models.Stateless; namespace View_by_Distance.Distance.Models.Stateless;
@ -8,27 +9,135 @@ namespace View_by_Distance.Distance.Models.Stateless;
internal static class FaceEncodingLogic internal static class FaceEncodingLogic
{ {
internal static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) internal static void MoveUnableToMatch(FilePath filePath)
{ {
List<ExifDirectory> results = []; string checkFile = $"{filePath.FullName}.unk";
string? json; if (File.Exists(filePath.FullName) && !File.Exists(checkFile))
FaceEncoding? faceEncoding; File.Move(filePath.FullName, checkFile);
ExifDirectory exifDirectory; }
FaceRecognitionDotNet.Models.FaceEncoding? encoding;
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMappedIdsThenWholePercentagesToLocationContainer(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections)
{
ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> results;
List<LocationContainer?> locationContainers = [];
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ReadOnlyCollection<ExifDirectory> exifDirectories = MappedLogicA.GetMapped(resultSettings,
metadataSettings,
peopleSettings,
distanceSettings,
compareSettings,
compare,
readOnlyCollections);
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds);
string message = $") Building Mapped with Encoding Face Files Collection - {totalSeconds} total second(s)"; string message = $") Building Mapped with Encoding Face Files Collection - {totalSeconds} total second(s)";
compare.ConstructProgressBar(exifDirectories.Count, message); compare.ConstructProgressBar(exifDirectories.Count, message);
foreach (ExifDirectory e in exifDirectories) _ = Parallel.For(0, exifDirectories.Count, parallelOptions, (i, state) =>
LocationContainersParallelFor(distanceSettings, compareSettings, compare, exifDirectories, i, locationContainers));
results = GetMappedIdsThenWholePercentagesToLocationContainer(locationContainers);
return results;
}
private static void LocationContainersParallelFor(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection<ExifDirectory> exifDirectories, int i, List<LocationContainer?> results)
{ {
compare.Tick(); compare.Tick();
json = Metadata.Models.Stateless.IMetadata.GetFaceEncoding(e); ExifDirectory exifDirectory = exifDirectories[i];
LocationContainer? locationContainer = GetLocationContainer(distanceSettings, compareSettings, exifDirectory);
results.Add(locationContainer);
}
internal static LocationContainer? GetLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ExifDirectory exifDirectory)
{
LocationContainer? result;
string? json;
DateOnly dateOnly;
FaceFile? faceFile;
int? wholePercentages;
FaceEncoding? faceEncoding;
FaceRecognitionDotNet.Models.FaceEncoding? encoding;
wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath);
if (wholePercentages is null)
{
if (distanceSettings.DistanceMoveUnableToMatch)
MoveUnableToMatch(exifDirectory.FilePath);
result = null;
}
else
{
faceFile = GetFaceFile(distanceSettings, exifDirectory);
if (faceFile is null)
result = null;
else
{
dateOnly = DateOnly.FromDateTime(new DateTime(exifDirectory.FilePath.CreationTicks));
json = Metadata.Models.Stateless.IMetadata.GetFaceEncoding(exifDirectory);
faceEncoding = json is null ? null : JsonSerializer.Deserialize(json, FaceEncodingGenerationContext.Default.FaceEncoding); faceEncoding = json is null ? null : JsonSerializer.Deserialize(json, FaceEncodingGenerationContext.Default.FaceEncoding);
if (faceEncoding is null) if (faceEncoding is null)
continue; result = null;
else
{
encoding = FaceRecognitionDotNet.Models.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding); encoding = FaceRecognitionDotNet.Models.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding);
exifDirectory = ExifDirectory.Get(encoding, e); result = new(CreationDateOnly: dateOnly,
results.Add(exifDirectory); ExifDirectory: exifDirectory,
Encoding: encoding,
FaceFile: faceFile,
FilePath: exifDirectory.FilePath,
LengthPermyriad: null,
LengthSource: null,
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: exifDirectory.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
WholePercentages: wholePercentages);
} }
}
}
return result;
}
private static FaceFile? GetFaceFile(DistanceSettings distanceSettings, ExifDirectory exifDirectory)
{
FaceFile? result;
string? json = Metadata.Models.Stateless.IMetadata.GetOutputResolution(exifDirectory);
if (json is null || !json.Contains(nameof(DateTime)))
{
if (distanceSettings.DistanceMoveUnableToMatch)
MoveUnableToMatch(exifDirectory.FilePath);
result = null;
}
else
{
result = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
if (result is null || result.Location is null)
{
if (distanceSettings.DistanceMoveUnableToMatch)
MoveUnableToMatch(exifDirectory.FilePath);
result = null;
}
}
return result;
}
private static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMappedIdsThenWholePercentagesToLocationContainer(List<LocationContainer?> locationContainers)
{
Dictionary<int, ReadOnlyDictionary<int, LocationContainer>> results = [];
int id;
int wholePercentages;
Dictionary<int, LocationContainer>? keyValue;
Dictionary<int, Dictionary<int, LocationContainer>> keyValuePairs = [];
foreach (LocationContainer? locationContainer in locationContainers)
{
if (locationContainer?.FilePath.Id is null || locationContainer.WholePercentages is null)
continue;
id = locationContainer.FilePath.Id.Value;
wholePercentages = locationContainer.WholePercentages.Value;
if (!keyValuePairs.TryGetValue(id, out keyValue))
{
keyValuePairs.Add(id, []);
if (!keyValuePairs.TryGetValue(id, out keyValue))
throw new Exception();
}
keyValue.Add(wholePercentages, locationContainer);
}
foreach (KeyValuePair<int, Dictionary<int, LocationContainer>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return results.AsReadOnly(); return results.AsReadOnly();
} }

View File

@ -32,7 +32,7 @@ internal static class FilterLogicA
return result; return result;
} }
internal static ReadOnlyCollection<LocationContainer> GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) internal static ReadOnlyCollection<LocationContainer> GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer, ReadOnlyCollection<ExifDirectory> exifDirectories)
{ {
List<LocationContainer> results = []; List<LocationContainer> results = [];
string? json; string? json;
@ -41,11 +41,11 @@ internal static class FilterLogicA
bool? isFocusPerson; bool? isFocusPerson;
bool? inSkipCollection; bool? inSkipCollection;
FaceEncoding? faceEncoding; FaceEncoding? faceEncoding;
ReadOnlyDictionary<int, FilePath>? keyValues;
FaceRecognitionDotNet.Models.FaceEncoding? encoding; FaceRecognitionDotNet.Models.FaceEncoding? encoding;
ReadOnlyDictionary<int, LocationContainer>? keyValues;
List<FilePathAndWholePercentages>? wholePercentagesCollection; List<FilePathAndWholePercentages>? wholePercentagesCollection;
ReadOnlyCollection<LocationContainer> locationContainers = FilterLogicB.GetLocationContainers(distanceSettings, compareSettings, compare, ticks, exifDirectories, nameof(FilterLogicA)); ReadOnlyCollection<LocationContainer> locationContainers = FilterLogicB.GetLocationContainers(distanceSettings, compareSettings, compare, exifDirectories, nameof(FilterLogicA));
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds);
string message = $") PreFiltering LocationContainers Face Files Collection - {totalSeconds} total second(s)"; string message = $") PreFiltering LocationContainers Face Files Collection - {totalSeconds} total second(s)";
compare.ConstructProgressBar(locationContainers.Count, message); compare.ConstructProgressBar(locationContainers.Count, message);
foreach (LocationContainer locationContainer in locationContainers) foreach (LocationContainer locationContainer in locationContainers)
@ -53,7 +53,7 @@ internal static class FilterLogicA
compare.Tick(); compare.Tick();
if (locationContainer.FilePath.Id is null || locationContainer.WholePercentages is null) if (locationContainer.FilePath.Id is null || locationContainer.WholePercentages is null)
continue; continue;
if (keyValuePairs.TryGetValue(locationContainer.FilePath.Id.Value, out keyValues)) if (mappedIdsThenWholePercentagesToLocationContainer.TryGetValue(locationContainer.FilePath.Id.Value, out keyValues))
{ {
if (keyValues.ContainsKey(locationContainer.WholePercentages.Value)) if (keyValues.ContainsKey(locationContainer.WholePercentages.Value))
continue; continue;
@ -71,7 +71,7 @@ internal static class FilterLogicA
continue; continue;
if (!string.IsNullOrEmpty(distanceSettings.FocusModel)) if (!string.IsNullOrEmpty(distanceSettings.FocusModel))
{ {
model = Metadata.Models.Stateless.IMetadata.GetModel(locationContainer.ExifDirectory); model = IMetaBase.GetModel(locationContainer.ExifDirectory);
if (string.IsNullOrEmpty(model) || !model.Contains(distanceSettings.FocusModel)) if (string.IsNullOrEmpty(model) || !model.Contains(distanceSettings.FocusModel))
continue; continue;
} }
@ -85,7 +85,7 @@ internal static class FilterLogicA
if (faceEncoding is null) if (faceEncoding is null)
continue; continue;
encoding = FaceRecognitionDotNet.Models.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding); encoding = FaceRecognitionDotNet.Models.FaceRecognition.LoadFaceEncoding(faceEncoding.RawEncoding);
results.Add(LocationContainer.Get(locationContainer, encoding, keepExifDirectory: false)); results.Add(LocationContainer.Get(locationContainer, encoding));
} }
return results.AsReadOnly(); return results.AsReadOnly();
} }

View File

@ -1,6 +1,4 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Drawing;
using System.Text.Json;
using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless; using View_by_Distance.Shared.Models.Stateless;
@ -10,71 +8,30 @@ namespace View_by_Distance.Distance.Models.Stateless;
internal static class FilterLogicB internal static class FilterLogicB
{ {
private static void MoveUnableToMatch(FilePath filePath) internal static ReadOnlyCollection<LocationContainer> GetLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection<ExifDirectory> exifDirectories, string sourceClass)
{
string checkFile = $"{filePath.FullName}.unk";
if (File.Exists(filePath.FullName) && !File.Exists(checkFile))
File.Move(filePath.FullName, checkFile);
}
private static void LocationContainersParallelFor(DistanceSettings distanceSettings, ICompareSettings compareSettings, List<LocationContainer> locationContainers, ExifDirectory exifDirectory)
{
string? json;
if (exifDirectory.FilePath.Id is null)
return;
DateOnly dateOnly = DateOnly.FromDateTime(new DateTime(exifDirectory.FilePath.CreationTicks));
int? wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath);
if (wholePercentages is null)
{
if (distanceSettings.DistanceMoveUnableToMatch)
MoveUnableToMatch(exifDirectory.FilePath);
return;
}
json = Metadata.Models.Stateless.IMetadata.GetOutputResolution(exifDirectory);
if (json is null || !json.Contains(nameof(DateTime)))
{
if (distanceSettings.DistanceMoveUnableToMatch)
MoveUnableToMatch(exifDirectory.FilePath);
return;
}
FaceFile? faceFile = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
if (faceFile is null || faceFile.Location is null)
{
if (distanceSettings.DistanceMoveUnableToMatch)
MoveUnableToMatch(exifDirectory.FilePath);
return;
}
RectangleF? rectangle = Shared.Models.Stateless.ILocation.GetPercentagesRectangle(distanceSettings, wholePercentages.Value);
if (rectangle is null)
return;
LocationContainer locationContainer = new(dateOnly,
exifDirectory,
exifDirectory.Encoding,
faceFile,
exifDirectory.FilePath,
null,
null,
exifDirectory.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
rectangle,
wholePercentages);
lock (locationContainers)
locationContainers.Add(locationContainer);
}
internal static ReadOnlyCollection<LocationContainer> GetLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories, string sourceClass)
{ {
List<LocationContainer> results = []; List<LocationContainer> results = [];
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism; int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds);
string message = $") Building LocationContainers Face Files Collection {sourceClass} - {totalSeconds} total second(s)"; string message = $") Building LocationContainers Face Files Collection {sourceClass} - {totalSeconds} total second(s)";
compare.ConstructProgressBar(exifDirectories.Count, message); compare.ConstructProgressBar(exifDirectories.Count, message);
_ = Parallel.For(0, exifDirectories.Count, parallelOptions, (i, state) => _ = Parallel.For(0, exifDirectories.Count, parallelOptions, (i, state) =>
{ LocationContainersParallelFor(distanceSettings, compareSettings, compare, exifDirectories, i, results));
compare.Tick();
LocationContainersParallelFor(distanceSettings, compareSettings, results, exifDirectories[i]);
});
return results.AsReadOnly(); return results.AsReadOnly();
} }
private static void LocationContainersParallelFor(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollection<ExifDirectory> exifDirectories, int i, List<LocationContainer> results)
{
compare.Tick();
ExifDirectory exifDirectory = exifDirectories[i];
if (exifDirectory.FilePath.Id is null)
return;
LocationContainer? locationContainer = FaceEncodingLogic.GetLocationContainer(distanceSettings, compareSettings, exifDirectory);
if (locationContainer is null)
return;
lock (results)
results.Add(locationContainer);
}
} }

View File

@ -24,23 +24,25 @@ internal static class FilterLogicC
return results.AsReadOnly(); return results.AsReadOnly();
} }
private static ReadOnlyCollection<LocationContainer> GetCombined(DistanceSettings distanceSettings, Shared.Models.Properties.ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories, ReadOnlyCollection<LocationContainer> postFiltered) private static ReadOnlyCollection<LocationContainer> GetCombined(ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer, ReadOnlyCollection<LocationContainer> postFiltered)
{ {
List<LocationContainer> results = []; List<LocationContainer> results = [];
foreach (LocationContainer locationContainer in postFiltered) foreach (LocationContainer locationContainer in postFiltered)
results.Add(locationContainer); results.Add(locationContainer);
ReadOnlyCollection<LocationContainer> locationContainers = FilterLogicB.GetLocationContainers(distanceSettings, compareSettings, compare, ticks, exifDirectories, nameof(FilterLogicC)); foreach (KeyValuePair<int, ReadOnlyDictionary<int, LocationContainer>> keyValuePair in mappedIdsThenWholePercentagesToLocationContainer)
foreach (LocationContainer locationContainer in locationContainers) {
results.Add(locationContainer); foreach (KeyValuePair<int, LocationContainer> keyValue in keyValuePair.Value)
results.Add(keyValue.Value);
}
return results.AsReadOnly(); return results.AsReadOnly();
} }
internal static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(DistanceSettings distanceSettings, Shared.Models.Properties.ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) internal static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompare compare, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered)
{ {
List<LocationContainer> results = []; List<LocationContainer> results = [];
ReadOnlyCollection<LocationContainer> collection; ReadOnlyCollection<LocationContainer> collection;
ReadOnlyCollection<LocationContainer> locationContainers = GetCombined(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, postFiltered); ReadOnlyCollection<LocationContainer> locationContainers = GetCombined(mappedIdsThenWholePercentagesToLocationContainer, postFiltered);
string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds)} total second(s)";
compare.ConstructProgressBar(postFiltered.Count, message); compare.ConstructProgressBar(postFiltered.Count, message);
foreach (LocationContainer locationContainer in postFiltered) foreach (LocationContainer locationContainer in postFiltered)
{ {

View File

@ -11,11 +11,21 @@ namespace View_by_Distance.Distance.Models.Stateless;
internal static class FilterLogicD internal static class FilterLogicD
{ {
internal record RecordA(long? Ticks, string? Directory); internal static string GetResizeContentDirectory(ResultSettings resultSettings, string cContentDirectory, FilePath filePath)
{
string result;
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
result = Path.Combine(cContentDirectory, cei.Combined);
return result;
}
internal record RecordB(string ByValue, bool IsByMapping, bool IsBySorting); internal static string GetFacePartsDirectoryX(ResultSettings resultSettings, string d2FacePartsContentDirectory, FilePath filePath)
{
internal record RecordC(string? DebugDirectory, string? Directory, long? Ticks, string? PersonDirectory); string result;
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, filePath.NameWithoutExtension);
return result;
}
internal static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName) internal static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName)
{ {
@ -39,142 +49,13 @@ internal static class FilterLogicD
else if (isBySorting && useFiltersCounter.HasValue) else if (isBySorting && useFiltersCounter.HasValue)
byValue = $"{nameof(IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}"; byValue = $"{nameof(IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}";
else else
{ byValue = IDistance.Get(saveIndividually, forceSingleImageHumanized, by.Value, isDefaultName);
byValue = $"{by.Value switch
{
IMapLogic.Mapping => nameof(IMapLogic.Mapping),
IMapLogic.Sorting => saveIndividually ? nameof(IMapLogic.Individually) : nameof(IMapLogic.Sorting),
IMapLogic.ForceSingleImage => forceSingleImageHumanized,
_ => throw new NotImplementedException()
}}{(!isDefaultName ? "-A" : "-Z")}";
}
} }
result = new(byValue, isByMapping, isBySorting); result = new(byValue, isByMapping, isBySorting);
return result; return result;
} }
private static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) => internal static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, int? updated, ReadOnlyCollection<SaveContainer> saveContainers)
Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKeyFormattedAndKeyTicksAndDisplayDirectoryName is null ? null : IMapLogic.Mapping, personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName);
private static RecordC Get(string eDistanceContentTicksDirectory, RecordB recordB, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, string segmentB)
{
RecordC result;
if (string.IsNullOrEmpty(personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName))
throw new NotImplementedException();
long? ticks = null;
string? debugDirectory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName);
string? directory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, segmentB);
string? personDirectory = Path.Combine(directory, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName, "lnk");
result = new(debugDirectory, directory, ticks, personDirectory);
return result;
}
internal static string GetResizeContentDirectory(ResultSettings resultSettings, string cContentDirectory, FilePath filePath)
{
string result;
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
result = Path.Combine(cContentDirectory, directoryName);
return result;
}
internal static string GetFacePartsDirectoryX(ResultSettings resultSettings, string d2FacePartsContentDirectory, FilePath filePath)
{
string result;
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
result = Path.Combine(d2FacePartsContentDirectory, directoryName, filePath.NameWithoutExtension);
return result;
}
internal static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne)
{
List<SaveContainer> results = [];
RecordB recordB;
RecordC recordC;
string segmentB;
string checkFile;
string? directory;
string shortcutFile;
string facesDirectory;
bool isCounterPersonYear;
string facePartsDirectory;
FileHolder? faceFileHolder;
SaveContainer? saveContainer;
FileHolder? resizedFileHolder;
int? useFiltersCounter = null;
string resizeContentDirectory;
FileHolder? facePartsFileHolder;
FileHolder? hiddenFaceFileHolder;
LocationContainer locationContainer;
bool sortingContainersAny = onlyOne.Count > 0;
string eResultsFullGroupDirectory = IResult.GetResultsDateGroupDirectory(resultSettings,
nameof(E_Distance),
resultSettings.ResultContent);
string cResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(C_Resize),
outputResolution,
includeResizeGroup: true,
includeModel: false,
includePredictorModel: false);
string d2ResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(D2_FaceParts),
outputResolution,
includeResizeGroup: true,
includeModel: true,
includePredictorModel: true);
string cContentDirectory = Path.Combine(cResultsFullGroupDirectory, resultSettings.ResultContent);
if (!Directory.Exists(cContentDirectory))
_ = Directory.CreateDirectory(cContentDirectory);
string eDistanceContentTicksDirectory = Path.Combine(eResultsFullGroupDirectory, ticks.ToString());
if (!Directory.Exists(eDistanceContentTicksDirectory))
_ = Directory.CreateDirectory(eDistanceContentTicksDirectory);
string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, resultSettings.ResultContent);
if (!Directory.Exists(d2FacePartsContentDirectory))
_ = Directory.CreateDirectory(d2FacePartsContentDirectory);
string forceSingleImageHumanized = nameof(IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
string message = $") Building Save Container Collection - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
compare.ConstructProgressBar(onlyOne.Count, message);
foreach (KeyValuePair<string, LocationContainer> keyValuePair in onlyOne)
{
if (distanceSettings.SaveIndividually)
break;
locationContainer = keyValuePair.Value;
if (locationContainer.LengthPermyriad is null || locationContainer.LengthSource is null)
continue;
segmentB = locationContainer.LengthPermyriad.Value.ToString().PadLeft(2, '0')[..2];
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
continue;
isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyTicks);
recordB = Get(useFiltersCounter, distanceSettings.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
recordC = Get(eDistanceContentTicksDirectory, recordB, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, segmentB);
if (string.IsNullOrEmpty(recordC.Directory) || string.IsNullOrEmpty(recordC.PersonDirectory))
continue;
directory = recordC.Directory;
if (!string.IsNullOrEmpty(recordC.DebugDirectory))
results.Add(SaveContainer.Get(recordC.DebugDirectory));
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
{
if (!distanceSettings.SaveSortingWithoutPerson)
throw new NotSupportedException();
if (recordC.Ticks is null)
continue;
}
results.Add(SaveContainer.Get(recordC.PersonDirectory));
facesDirectory = locationContainer.LengthSource.DirectoryFullPath;
faceFileHolder = FileHolder.Get(locationContainer.LengthSource.FullName);
checkFile = Path.Combine(directory, $"{locationContainer.LengthSource.Name}");
shortcutFile = Path.Combine(recordC.PersonDirectory, $"{locationContainer.LengthSource.Name}.lnk");
resizeContentDirectory = GetResizeContentDirectory(resultSettings, cContentDirectory, locationContainer.LengthSource);
facePartsDirectory = GetFacePartsDirectoryX(resultSettings, d2FacePartsContentDirectory, locationContainer.LengthSource);
hiddenFaceFileHolder = FileHolder.Get(Path.Combine(facesDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesHiddenFileNameExtension}"));
facePartsFileHolder = FileHolder.Get(Path.Combine(facePartsDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesPartsFileNameExtension}"));
resizedFileHolder = FileHolder.Get(Path.Combine(resizeContentDirectory, $"{locationContainer.LengthSource.FileNameFirstSegment}{Path.GetExtension(locationContainer.LengthSource.NameWithoutExtension)}"));
saveContainer = SaveContainer.Get(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, resizedFileHolder, shortcutFile);
results.Add(saveContainer);
}
return results.AsReadOnly();
}
internal static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers)
{ {
string fileName; string fileName;
string checkFile; string checkFile;
@ -189,7 +70,7 @@ internal static class FilterLogicD
if (!Directory.Exists(directory)) if (!Directory.Exists(directory))
_ = Directory.CreateDirectory(directory); _ = Directory.CreateDirectory(directory);
} }
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds);
string message; string message;
if (updated is null) if (updated is null)
message = $") {saveContainers.Count:000} save(s) - {totalSeconds} total second(s)"; message = $") {saveContainers.Count:000} save(s) - {totalSeconds} total second(s)";
@ -285,4 +166,115 @@ internal static class FilterLogicD
} }
} }
internal static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne)
{
List<SaveContainer> results = [];
RecordB recordB;
RecordC recordC;
string segmentB;
string checkFile;
string? directory;
string shortcutFile;
string facesDirectory;
bool isCounterPersonYear;
string facePartsDirectory;
FileHolder? faceFileHolder;
SaveContainer? saveContainer;
FileHolder? resizedFileHolder;
int? useFiltersCounter = null;
string resizeContentDirectory;
FileHolder? facePartsFileHolder;
FileHolder? hiddenFaceFileHolder;
LocationContainer locationContainer;
bool sortingContainersAny = onlyOne.Count > 0;
string eResultsFullGroupDirectory = IResult.GetResultsDateGroupDirectory(resultSettings,
nameof(E_Distance),
resultSettings.ResultContent);
string cResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(C_Resize),
outputResolution,
includeResizeGroup: true,
includeModel: false,
includePredictorModel: false);
string d2ResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(D2_FaceParts),
outputResolution,
includeResizeGroup: true,
includeModel: true,
includePredictorModel: true);
string cContentDirectory = Path.Combine(cResultsFullGroupDirectory, resultSettings.ResultContent);
if (!Directory.Exists(cContentDirectory))
_ = Directory.CreateDirectory(cContentDirectory);
string eDistanceContentTicksDirectory = Path.Combine(eResultsFullGroupDirectory, compare.Ticks.ToString());
if (!Directory.Exists(eDistanceContentTicksDirectory))
_ = Directory.CreateDirectory(eDistanceContentTicksDirectory);
string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, resultSettings.ResultContent);
if (!Directory.Exists(d2FacePartsContentDirectory))
_ = Directory.CreateDirectory(d2FacePartsContentDirectory);
string forceSingleImageHumanized = nameof(IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
string message = $") Building Save Container Collection - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds)} total second(s)";
compare.ConstructProgressBar(onlyOne.Count, message);
foreach (KeyValuePair<string, LocationContainer> keyValuePair in onlyOne)
{
if (distanceSettings.SaveIndividually)
break;
locationContainer = keyValuePair.Value;
if (locationContainer.LengthPermyriad is null || locationContainer.LengthSource is null)
continue;
segmentB = locationContainer.LengthPermyriad.Value.ToString().PadLeft(2, '0')[..2];
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
continue;
isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyTicks);
recordB = Get(useFiltersCounter, distanceSettings.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
recordC = Get(eDistanceContentTicksDirectory, recordB, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, segmentB);
if (string.IsNullOrEmpty(recordC.Directory) || string.IsNullOrEmpty(recordC.PersonDirectory))
continue;
directory = recordC.Directory;
if (!string.IsNullOrEmpty(recordC.DebugDirectory))
results.Add(SaveContainer.Get(recordC.DebugDirectory));
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
{
if (!distanceSettings.SaveSortingWithoutPerson)
throw new NotSupportedException();
if (recordC.Ticks is null)
continue;
}
results.Add(SaveContainer.Get(recordC.PersonDirectory));
facesDirectory = locationContainer.LengthSource.DirectoryFullPath;
faceFileHolder = FileHolder.Get(locationContainer.LengthSource.FullName);
checkFile = Path.Combine(directory, $"{locationContainer.LengthSource.Name}");
shortcutFile = Path.Combine(recordC.PersonDirectory, $"{locationContainer.LengthSource.Name}.lnk");
resizeContentDirectory = GetResizeContentDirectory(resultSettings, cContentDirectory, locationContainer.LengthSource);
facePartsDirectory = GetFacePartsDirectoryX(resultSettings, d2FacePartsContentDirectory, locationContainer.LengthSource);
hiddenFaceFileHolder = FileHolder.Get(Path.Combine(facesDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesHiddenFileNameExtension}"));
facePartsFileHolder = FileHolder.Get(Path.Combine(facePartsDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesPartsFileNameExtension}"));
resizedFileHolder = FileHolder.Get(Path.Combine(resizeContentDirectory, $"{locationContainer.LengthSource.FileNameFirstSegment}{Path.GetExtension(locationContainer.LengthSource.NameWithoutExtension)}"));
saveContainer = SaveContainer.Get(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, resizedFileHolder, shortcutFile);
results.Add(saveContainer);
}
return results.AsReadOnly();
}
private static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) =>
Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKeyFormattedAndKeyTicksAndDisplayDirectoryName is null ? null : IMapLogic.Mapping, personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName);
private static RecordC Get(string eDistanceContentTicksDirectory, RecordB recordB, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, string segmentB)
{
RecordC result;
if (string.IsNullOrEmpty(personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName))
throw new NotImplementedException();
long? ticks = null;
string? debugDirectory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName);
string? directory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, segmentB);
string? personDirectory = Path.Combine(directory, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName, "lnk");
result = new(debugDirectory, directory, ticks, personDirectory);
return result;
}
internal record RecordA(long? Ticks, string? Directory);
internal record RecordB(string ByValue, bool IsByMapping, bool IsBySorting);
internal record RecordC(string? DebugDirectory, string? Directory, long? Ticks, string? PersonDirectory);
} }

View File

@ -9,49 +9,57 @@ namespace View_by_Distance.Distance.Models.Stateless;
public interface IDistance public interface IDistance
{ {
static ReadOnlyCollection<ExifDirectory> TestStatic_GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) => public static string Get(bool saveIndividually, string forceSingleImageHumanized, int by, bool isDefaultName) =>
GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, readOnlyCollections); $"{by switch
static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) => {
MappedLogicA.GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, readOnlyCollections); IMapLogic.Mapping => nameof(IMapLogic.Mapping),
IMapLogic.Sorting => saveIndividually ?
nameof(IMapLogic.Individually) :
nameof(IMapLogic.Sorting),
IMapLogic.ForceSingleImage => forceSingleImageHumanized,
_ => throw new NotImplementedException()
}}{(!isDefaultName ? "-A" : "-Z")}";
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> TestStatic_Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) => public static ReadOnlyDictionary<string, LocationContainer> GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
Extract(compareSettings, exifDirectories);
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
MappedLogicA.Extract(compareSettings, exifDirectories);
static ReadOnlyCollection<ExifDirectory> TestStatic_GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
FaceEncodingLogic.GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
static ReadOnlyCollection<LocationContainer> TestStatic_GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
static ReadOnlyCollection<LocationContainer> GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
FilterLogicA.GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
static ReadOnlyCollection<LocationContainer> TestStatic_GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
GetPostFilterLocationContainer(preFiltered, distanceLimits);
static ReadOnlyCollection<LocationContainer> GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
FilterLogicC.GetPostFilterLocationContainer(preFiltered, distanceLimits);
static ReadOnlyCollection<LocationContainer> TestStatic_GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
GetMatrixLocationContainers(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
FilterLogicC.GetMatrixLocationContainers(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
static ReadOnlyDictionary<string, LocationContainer> TestStatic_GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
GetOnlyOne(distanceSettings, matrix);
static ReadOnlyDictionary<string, LocationContainer> GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
FilterLogicC.GetOnlyOne(distanceSettings, matrix); FilterLogicC.GetOnlyOne(distanceSettings, matrix);
static ReadOnlyCollection<SaveContainer> TestStatic_GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) => public static ReadOnlyCollection<LocationContainer> GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne); FilterLogicC.GetPostFilterLocationContainer(preFiltered, distanceLimits);
static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
FilterLogicD.GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne);
static void TestStatic_SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) => public static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers); FilterLogicD.SaveContainers(distanceSettings, compareSettings, compare, updated, saveContainers);
static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
FilterLogicD.SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers); public static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
FilterLogicD.GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, outputResolution, onlyOne);
public static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompare compare, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
FilterLogicC.GetMatrixLocationContainers(distanceSettings, compare, mappedIdsThenWholePercentagesToLocationContainer, distanceLimits, postFiltered);
public static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMappedIdsThenWholePercentagesToLocationContainer(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) =>
FaceEncodingLogic.GetMappedIdsThenWholePercentagesToLocationContainer(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, readOnlyCollections);
public static ReadOnlyCollection<LocationContainer> GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
FilterLogicA.GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, readOnlyCollections, mappedIdsThenWholePercentagesToLocationContainer, exifDirectories);
internal static ReadOnlyDictionary<string, LocationContainer> TestStatic_GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
GetOnlyOne(distanceSettings, matrix);
internal static ReadOnlyCollection<LocationContainer> TestStatic_GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
GetPostFilterLocationContainer(preFiltered, distanceLimits);
internal static void TestStatic_SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
SaveContainers(distanceSettings, compareSettings, compare, updated, saveContainers);
internal static ReadOnlyCollection<SaveContainer> TestStatic_GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, outputResolution, onlyOne);
internal static ReadOnlyCollection<LocationContainer> TestStatic_GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompare compare, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
GetMatrixLocationContainers(distanceSettings, compare, mappedIdsThenWholePercentagesToLocationContainer, distanceLimits, postFiltered);
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> TestStatic_GetMappedIdsThenWholePercentagesToLocationContainer(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections) =>
GetMappedIdsThenWholePercentagesToLocationContainer(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, readOnlyCollections);
internal static ReadOnlyCollection<LocationContainer> TestStatic_GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> mappedIdsThenWholePercentagesToLocationContainer, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, readOnlyCollections, mappedIdsThenWholePercentagesToLocationContainer, exifDirectories);
} }

View File

@ -14,33 +14,27 @@ internal static class MappedLogicA
string? PersonDisplayDirectoryName, string? PersonDisplayDirectoryName,
FilePath FilePath); FilePath FilePath);
private static List<MappedFile> GetDisplayDirectoryAllFiles(PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections) internal static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, ReadOnlyCollections readOnlyCollections)
{ {
List<MappedFile> results = []; List<ExifDirectory> results = [];
FilePath filePath; string eDistanceContentDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(E_Distance), resultSettings.ResultContent));
MappedFile mappedFile; ReadOnlyCollection<MappedLogicB.Record> records = MappedLogicB.DeleteEmptyDirectoriesAndGetCollection(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, eDistanceContentDirectory, readOnlyCollections);
string personKeyFormatted; ReadOnlyCollection<MappedFile> mappedFiles = GetMappedFiles(resultSettings, metadataSettings, peopleSettings, compareSettings, readOnlyCollections, records);
List<string> distinct = []; if (mappedFiles.Count > 0)
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
foreach (PersonContainer personContainer in readOnlyCollections.PersonContainers)
{ {
if (personContainer.Key is null) int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
continue; int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds);
for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--) string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)";
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipNotSkipCollection = readOnlyCollections.SkipNotSkipCollection;
compare.ConstructProgressBar(mappedFiles.Count, message);
_ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
{ {
filePath = personContainer.DisplayDirectoryAllFilePaths[i]; compare.Tick();
if (filePath.ExtensionLowered != compareSettings.FacesFileNameExtension) MappedParallelFor(resultSettings, metadataSettings, distanceSettings, compareSettings, results, skipNotSkipCollection, mappedFiles[i]);
continue; });
if (distinct.Contains(filePath.Name))
continue;
distinct.Add(filePath.Name);
personKeyFormatted = IPersonBirthday.GetFormatted(peopleSettings.PersonBirthdayFormat, personContainer.Key.Value);
personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(personKeyFormatted, personContainer.Key.Value, filePath.Name);
mappedFile = new(personKeyFormattedAndKeyTicksAndDisplayDirectoryName, personContainer.DisplayDirectoryName, filePath);
results.Add(mappedFile);
} }
} return results.AsReadOnly();
return results;
} }
private static ReadOnlyCollection<MappedFile> GetMappedFiles(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections, ReadOnlyCollection<MappedLogicB.Record> records) private static ReadOnlyCollection<MappedFile> GetMappedFiles(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections, ReadOnlyCollection<MappedLogicB.Record> records)
@ -102,6 +96,35 @@ internal static class MappedLogicA
return results.AsReadOnly(); return results.AsReadOnly();
} }
private static List<MappedFile> GetDisplayDirectoryAllFiles(PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections)
{
List<MappedFile> results = [];
FilePath filePath;
MappedFile mappedFile;
string personKeyFormatted;
List<string> distinct = [];
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
foreach (PersonContainer personContainer in readOnlyCollections.PersonContainers)
{
if (personContainer.Key is null)
continue;
for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--)
{
filePath = personContainer.DisplayDirectoryAllFilePaths[i];
if (filePath.ExtensionLowered != compareSettings.FacesFileNameExtension)
continue;
if (distinct.Contains(filePath.Name))
continue;
distinct.Add(filePath.Name);
personKeyFormatted = IPersonBirthday.GetFormatted(peopleSettings.PersonBirthdayFormat, personContainer.Key.Value);
personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(personKeyFormatted, personContainer.Key.Value, filePath.Name);
mappedFile = new(personKeyFormattedAndKeyTicksAndDisplayDirectoryName, personContainer.DisplayDirectoryName, filePath);
results.Add(mappedFile);
}
}
return results;
}
private static void MappedParallelFor(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, List<ExifDirectory> exifDirectories, ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipCollection, MappedFile mappedFile) private static void MappedParallelFor(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, List<ExifDirectory> exifDirectories, ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipCollection, MappedFile mappedFile)
{ {
int? id; int? id;
@ -145,58 +168,11 @@ internal static class MappedLogicA
} }
if (mappedFile.FilePath.Name.EndsWith(lnk) || !File.Exists(mappedFile.FilePath.FullName)) if (mappedFile.FilePath.Name.EndsWith(lnk) || !File.Exists(mappedFile.FilePath.FullName))
return; return;
ExifDirectory exifDirectory = IMetadata.GetExifDirectory(mappedFile.FilePath, mappedFile.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName); Stream stream = File.OpenRead(mappedFile.FilePath.FullName);
ExifDirectory exifDirectory = IMetadata.GetExifDirectory(mappedFile.FilePath, stream, mappedFile.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
stream.Dispose();
lock (exifDirectories) lock (exifDirectories)
exifDirectories.Add(exifDirectory); exifDirectories.Add(exifDirectory);
} }
internal static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections)
{
List<ExifDirectory> results = [];
string eDistanceContentDirectory = IResult.GetResultsDateGroupDirectory(resultSettings, nameof(E_Distance), resultSettings.ResultContent);
ReadOnlyCollection<MappedLogicB.Record> records = MappedLogicB.DeleteEmptyDirectoriesAndGetCollection(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, eDistanceContentDirectory, readOnlyCollections);
ReadOnlyCollection<MappedFile> mappedFiles = GetMappedFiles(resultSettings, metadataSettings, peopleSettings, compareSettings, readOnlyCollections, records);
if (mappedFiles.Count > 0)
{
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
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 };
ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipNotSkipCollection = readOnlyCollections.SkipNotSkipCollection;
compare.ConstructProgressBar(mappedFiles.Count, message);
_ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
{
compare.Tick();
MappedParallelFor(resultSettings, metadataSettings, distanceSettings, compareSettings, results, skipNotSkipCollection, mappedFiles[i]);
});
}
return results.AsReadOnly();
}
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories)
{
Dictionary<int, ReadOnlyDictionary<int, FilePath>> results = [];
int? wholePercentages;
Dictionary<int, FilePath>? keyValues;
Dictionary<int, Dictionary<int, FilePath>> keyValuePairs = [];
foreach (ExifDirectory exifDirectory in exifDirectories)
{
if (exifDirectory.FilePath.Id is null)
continue;
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
{
keyValuePairs.Add(exifDirectory.FilePath.Id.Value, []);
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
throw new Exception();
}
wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath);
if (wholePercentages is null)
continue;
keyValues.Add(wholePercentages.Value, exifDirectory.FilePath);
}
foreach (KeyValuePair<int, Dictionary<int, FilePath>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return results.AsReadOnly();
}
} }

View File

@ -21,62 +21,188 @@ internal static class MappedLogicB
internal record TicksDirectory(DateTime AlternateDirectoryDateTime, internal record TicksDirectory(DateTime AlternateDirectoryDateTime,
string Directory, string Directory,
DateTime DirectoryDateTime, DateTime DirectoryDateTime,
string DirectoryName,
bool? IsLocationContainerDebugDirectory, bool? IsLocationContainerDebugDirectory,
float? TotalDays); float? TotalDays);
private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames) internal static ReadOnlyCollection<Record> DeleteEmptyDirectoriesAndGetCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string eDistanceContentDirectory, ReadOnlyCollections readOnlyCollections)
{
string checkFile;
string actionDirectoryName = Path.GetFileName(actionDirectory);
string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
foreach (string file in files)
{
if (facesFileNames.Contains(file))
{
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
continue;
}
File.Delete(file);
}
}
private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
{ {
List<Record> results = [];
bool check;
string message;
string[] files; string[] files;
string checkFile; bool? isDefault;
int? linksCount;
int totalSeconds;
DateTime dateTime;
TimeSpan timeSpan;
int directoryNumber;
List<Record> records;
string? checkDirectory; string? checkDirectory;
string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly); string[] yearDirectories;
foreach (string directory in directories) 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(IMapLogic.ManualCopy).Humanize(LetterCasing.Title);
string forceSingleImageHumanized = nameof(IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
for (int i = 1; i < 6; i++)
{ {
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory)); check = false;
if (!Directory.Exists(checkDirectory)) results.Clear();
Directory.Move(directory, checkDirectory); distinct.Clear();
else directoryNumber = 0;
ticksDirectories = UpdateDateVerifyAndGetTicksDirectories(distanceSettings, eDistanceContentDirectory);
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - compare.Ticks).TotalSeconds);
message = $"{i}) {ticksDirectories.Count:000} compile from and clean ticks Director(ies) - B - {totalSeconds} total second(s)";
compare.ConstructProgressBar(ticksDirectories.Count, message);
foreach (TicksDirectory ticksDirectory in ticksDirectories)
{ {
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories); if (i == 1)
foreach (string file in files) compare.Tick();
personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly);
foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories)
{ {
if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted)) personKeyFormatted = Path.GetFileName(personKeyFormattedDirectory);
isReservedDirectoryName = personKeyFormatted.StartsWith(nameof(IMapLogic.Sorting)) || personKeyFormatted.StartsWith(nameof(IMapLogic.Mapping)) || personKeyFormatted.StartsWith(nameof(IMapLogic.ManualCopy));
if (!isReservedDirectoryName && personKeyFormatted.StartsWith(nameof(IMapLogic.Individually)))
{
Individually(compareSettings, ticksDirectory, personKeyFormattedDirectory);
throw new Exception($"B) Move personKey directories up one from {nameof(IMapLogic.Sorting)} and delete {nameof(IMapLogic.Sorting)} directory!");
}
_ = readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.TryGetValue(personKeyFormatted, out newestPersonKeyFormatted);
if (readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
{
timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks);
if (timeSpan.TotalDays > 6)
throw new Exception($"{distanceSettings.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; continue;
checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted); if (ticksDirectory.IsLocationContainerDebugDirectory is null || !ticksDirectory.IsLocationContainerDebugDirectory.Value)
checkDirectory = Path.GetDirectoryName(checkFile); 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 != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, peopleSettings.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) if (checkDirectory is null)
continue; continue;
if (File.Exists(checkFile)) checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString());
continue;
if (!Directory.Exists(checkDirectory)) if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory); _ = Directory.CreateDirectory(checkDirectory);
File.Move(file, checkFile); 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(IMapLogic.Sorting)} and delete {nameof(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 (readOnlyCollections.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;
} }
_ = IPath.DeleteEmptyDirectories(personKeyDirectory); }
if (personKeyFormatted.Length != peopleSettings.PersonBirthdayFormat.Length)
continue;
if (personDisplayDirectoryName.Length == 1 || isDefault.Value || !readOnlyCollections.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(resultSettings, metadataSettings, compareSettings, 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);
}
if (check)
continue;
break;
}
return results.AsReadOnly();
} }
private static List<TicksDirectory> UpdateDateVerifyAndGetTicksDirectories(DistanceSettings distanceSettings, string eDistanceContentDirectory) private static List<TicksDirectory> UpdateDateVerifyAndGetTicksDirectories(DistanceSettings distanceSettings, string eDistanceContentDirectory)
@ -85,13 +211,13 @@ internal static class MappedLogicB
float? totalDays; float? totalDays;
long? next = null; long? next = null;
string? checkDirectory; string? checkDirectory;
string ticksDirectoryName;
DateTime directoryDateTime; DateTime directoryDateTime;
DirectoryInfo directoryInfo; DirectoryInfo directoryInfo;
TicksDirectory ticksDirectory; TicksDirectory ticksDirectory;
long? lastDirectoryTicks = null; long? lastDirectoryTicks = null;
DateTime dateTime = DateTime.Now; DateTime dateTime = DateTime.Now;
DateTime alternateDirectoryDateTime; DateTime alternateDirectoryDateTime;
string ticksDirectoryNameFirstSegment;
bool? isLocationContainerDebugDirectory; bool? isLocationContainerDebugDirectory;
long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks; long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks;
for (int i = 1; i < 5; i++) for (int i = 1; i < 5; i++)
@ -101,10 +227,10 @@ internal static class MappedLogicB
string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly); string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string ticksFullPath in ticksFullPaths) foreach (string ticksFullPath in ticksFullPaths)
{ {
ticksDirectoryName = Path.GetFileName(ticksFullPath); ticksDirectoryNameFirstSegment = Path.GetFileName(ticksFullPath).Split('.')[0];
if (ticksDirectoryName.Length < 3) if (ticksDirectoryNameFirstSegment.Length < 3)
continue; continue;
if (!long.TryParse(ticksDirectoryName, out long directoryTicks)) if (!long.TryParse(ticksDirectoryNameFirstSegment, out long directoryTicks))
throw new NotSupportedException(); throw new NotSupportedException();
if (next is null) if (next is null)
next = new DateTime(directoryTicks).Ticks; next = new DateTime(directoryTicks).Ticks;
@ -130,9 +256,9 @@ internal static class MappedLogicB
if (directoryInfo.LastWriteTime.Ticks != directoryTicks) if (directoryInfo.LastWriteTime.Ticks != directoryTicks)
Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks)); Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks));
alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1); alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1);
isLocationContainerDebugDirectory = distanceSettings.LocationContainerDebugDirectory is null ? null : ticksDirectoryName.EndsWith(distanceSettings.LocationContainerDebugDirectory); isLocationContainerDebugDirectory = distanceSettings.LocationContainerDebugDirectory is null ? null : ticksDirectoryNameFirstSegment.EndsWith(distanceSettings.LocationContainerDebugDirectory);
totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays; totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays;
ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), ticksDirectoryName, isLocationContainerDebugDirectory, totalDays); ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), isLocationContainerDebugDirectory, totalDays);
results.Add(ticksDirectory); results.Add(ticksDirectory);
if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0) if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0)
continue; continue;
@ -217,40 +343,42 @@ internal static class MappedLogicB
} }
} }
private static List<Record> GetRecords(ResultSettings resultSettings, MetadataSettings metadataSettings, ICompareSettings compareSettings, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List<string> distinct, string? personDisplayDirectoryName) private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames)
{ {
List<Record> results = [];
Record record;
string fileName;
string checkFile; string checkFile;
FilePath filePath; string actionDirectoryName = Path.GetFileName(actionDirectory);
FileHolder fileHolder; string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName);
int? wholePercentages; if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
foreach (string file in files) foreach (string file in files)
{ {
if (file.EndsWith(".lnk")) if (facesFileNames.Contains(file))
continue;
fileHolder = FileHolder.Get(file);
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
if (filePath.Id is null)
continue;
wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
if (wholePercentages is null)
continue;
fileName = Path.GetFileName(file);
if (distinct.Contains(fileName))
{ {
checkFile = $"{file}.dup"; checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
if (File.Exists(checkFile)) if (File.Exists(checkFile))
continue; continue;
File.Move(file, checkFile); File.Move(file, checkFile);
continue; continue;
} }
distinct.Add(fileName); File.Delete(file);
record = new(directoryNumber, isDefault, linksCount, filePath, personDisplayDirectoryName, personKeyFormatted);
results.Add(record);
} }
return results; }
private static int? GetLinksCount(string yearDirectory)
{
int? result;
string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-');
if (yearDirectoryNameSegments.Length != 3)
result = null;
else
{
string lastSegment = yearDirectoryNameSegments[^1];
if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0]))
result = null;
else
result = lastSegment[0] - 65;
}
return result;
} }
private static string[] RenameBirth(string[] files) private static string[] RenameBirth(string[] files)
@ -285,216 +413,117 @@ internal static class MappedLogicB
Directory.Move(personKeyDirectory, newestPersonKeyDirectory); Directory.Move(personKeyDirectory, newestPersonKeyDirectory);
} }
private static int? GetLinksCount(string yearDirectory) private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
{ {
int? result; string[] files;
string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-'); string checkFile;
if (yearDirectoryNameSegments.Length != 3) string? checkDirectory;
result = null; string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
{
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory));
if (!Directory.Exists(checkDirectory))
Directory.Move(directory, checkDirectory);
else else
{ {
string lastSegment = yearDirectoryNameSegments[^1]; files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0])) foreach (string file in files)
result = null; {
else if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted))
result = lastSegment[0] - 65; 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(ResultSettings resultSettings, MetadataSettings metadataSettings, ICompareSettings compareSettings, 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;
FileHolder fileHolder;
int? wholePercentages;
foreach (string file in files)
{
if (file.EndsWith(".lnk"))
continue;
fileHolder = FileHolder.Get(file);
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
if (filePath.Id is null)
continue;
wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
if (wholePercentages is null)
continue;
fileName = Path.GetFileName(file);
if (distinct.Contains(fileName))
{
checkFile = $"{file}.dup";
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
continue;
}
if (file.StartsWith(ticksDirectory.Directory))
{
@enum = IPath.GetEnum(filePath).ToString();
if (!ticksDirectory.Directory.EndsWith(@enum))
{
checkFile = GetCheckFile(ticksDirectory, @enum, fileName, file);
fileHolder = FileHolder.Get(checkFile);
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
if (filePath.Id is null)
continue;
}
}
distinct.Add(fileName);
record = new(DirectoryNumber: directoryNumber,
IsDefault: isDefault,
LinksCount: linksCount,
MappedFaceFilePath: filePath,
PersonDisplayDirectoryName: personDisplayDirectoryName,
PersonKeyFormatted: personKeyFormatted);
results.Add(record);
}
return results;
}
private static string GetCheckFile(TicksDirectory ticksDirectory, string @enum, string fileName, string file)
{
string result;
string checkDirectory;
string directory = file;
List<string> collection = [];
for (int i = 0; i < file.Length; i++)
{
directory = Path.GetDirectoryName(directory) ?? throw new Exception();
if (directory == ticksDirectory.Directory)
break;
collection.Add(Path.GetFileName(directory));
}
collection.Reverse();
checkDirectory = $"{ticksDirectory.Directory}.{@enum}";
foreach (string directoryName in collection)
checkDirectory = Path.Combine(checkDirectory, directoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
result = Path.Combine(checkDirectory, fileName);
if (File.Exists(result))
throw new Exception($"File <{fileName}> already exists!");
File.Move(file, result);
return result; return result;
} }
internal static ReadOnlyCollection<Record> DeleteEmptyDirectoriesAndGetCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string eDistanceContentDirectory, ReadOnlyCollections readOnlyCollections)
{
List<Record> results = [];
bool check;
string message;
string[] files;
bool? isDefault;
int? linksCount;
int totalSeconds;
DateTime dateTime;
TimeSpan timeSpan;
int directoryNumber;
string? checkDirectory;
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);
for (int i = 1; i < 6; i++)
{
check = false;
results.Clear();
distinct.Clear();
directoryNumber = 0;
ticksDirectories = UpdateDateVerifyAndGetTicksDirectories(distanceSettings, 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)";
compare.ConstructProgressBar(ticksDirectories.Count, message);
foreach (TicksDirectory ticksDirectory in ticksDirectories)
{
if (i == 1)
compare.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(compareSettings, 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!");
}
_ = readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.TryGetValue(personKeyFormatted, out newestPersonKeyFormatted);
if (readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
{
timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks);
if (timeSpan.TotalDays > 6)
throw new Exception($"{distanceSettings.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 != distanceSettings.LocationContainerDebugDirectory)
{
files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
File.Delete(file);
}
if (ticksDirectory.DirectoryName == distanceSettings.LocationContainerDebugDirectory)
{
isDefault = null;
personDisplayDirectoryName = null;
files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly);
results.AddRange(GetRecords(resultSettings, metadataSettings, compareSettings, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName));
files = Directory.GetFiles(yearDirectory, "*.lnk", SearchOption.AllDirectories);
foreach (string file in files)
File.Delete(file);
continue;
}
personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
if (personNameDirectories.Length > 1)
throw new NotSupportedException("Try deleting *.lnk files!");
foreach (string personNameDirectory in personNameDirectories)
{
directoryNumber++;
personDisplayDirectoryName = Path.GetFileName(personNameDirectory);
isDefault = IPerson.IsDefaultName(personDisplayDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]);
if (isDefault.Value && personDisplayDirectoryName.Length == 1)
{
if (personKeyFormatted.Length != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, peopleSettings.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 (readOnlyCollections.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 != peopleSettings.PersonBirthdayFormat.Length)
continue;
if (personDisplayDirectoryName.Length == 1 || isDefault.Value || !readOnlyCollections.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(resultSettings, metadataSettings, compareSettings, 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);
}
if (check)
continue;
break;
}
return results.AsReadOnly();
}
} }

1
Face/.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

View File

@ -6,14 +6,19 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>Phares.AA.Face</PackageId> <PackageId>Phares.AA.Face</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>9.0.100.0</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company> <Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<PackageReadmeFile>read-me.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows> <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@ -32,10 +37,13 @@
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'"> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" /> <PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Metadata\AA.Metadata.csproj" /> <ProjectReference Include="..\Metadata\AA.Metadata.csproj" />

View File

@ -15,17 +15,19 @@ internal static class Face
FileHolder fileHolder = FileHolder.Get(fileInfo, id: null); FileHolder fileHolder = FileHolder.Get(fileInfo, id: null);
const PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName = null; const PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName = null;
FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null); FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
ExifDirectory exifDirectory = IMetadata.GetExifDirectory(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName); Stream stream = File.OpenRead(filePath.FullName);
ExifDirectory exifDirectory = IMetadata.GetExifDirectory(filePath, stream, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
stream.Dispose();
results.Add(exifDirectory); results.Add(exifDirectory);
} }
internal static ReadOnlyCollection<ExifDirectory> GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution) internal static ReadOnlyCollection<ExifDirectory> GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string outputResolution)
{ {
List<ExifDirectory> results = []; List<ExifDirectory> results = [];
FileInfo fileInfo; FileInfo fileInfo;
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism; int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
long? skipOlderThan = distanceSettings.SkipOlderThanDays < 1 ? null : new DateTime(ticks).AddDays(-distanceSettings.SkipOlderThanDays).Ticks; long? skipOlderThan = distanceSettings.SkipOlderThanDays < 1 ? null : new DateTime(compare.Ticks).AddDays(-distanceSettings.SkipOlderThanDays).Ticks;
string resultsFullGroupDirectory = Path.GetFullPath(IResult.GetResultsFullGroupDirectory(resultSettings, string resultsFullGroupDirectory = Path.GetFullPath(IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(D_Face), nameof(D_Face),
outputResolution, outputResolution,

View File

@ -8,9 +8,9 @@ namespace View_by_Distance.Face.Models.Stateless;
public interface IFace public interface IFace
{ {
ReadOnlyCollection<ExifDirectory> TestStatic_GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution) => ReadOnlyCollection<ExifDirectory> TestStatic_GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string outputResolution) =>
GetExifDirectories(resultSettings, metadataSettings, distanceSettings, compareSettings, compare, ticks, outputResolution); GetExifDirectories(resultSettings, metadataSettings, distanceSettings, compareSettings, compare, outputResolution);
static ReadOnlyCollection<ExifDirectory> GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution) => static ReadOnlyCollection<ExifDirectory> GetExifDirectories(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, string outputResolution) =>
Face.GetExifDirectories(resultSettings, metadataSettings, distanceSettings, compareSettings, compare, ticks, outputResolution); Face.GetExifDirectories(resultSettings, metadataSettings, distanceSettings, compareSettings, compare, outputResolution);
} }

View File

@ -0,0 +1 @@
# Read Me

View File

@ -6,10 +6,13 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>Phares.AA.FaceRecognitionDotNet</PackageId> <PackageId>Phares.AA.FaceRecognitionDotNet</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>9.0.100.1</Version> <Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Authors>Mike Phares</Authors> <Authors>Mike Phares</Authors>
<Company>Phares</Company> <Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>

1
Metadata/.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

View File

@ -4,16 +4,21 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<OutputType>library</OutputType> <OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember142023>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</HoursSinceNovember142023>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>Phares.AA.Metadata</PackageId> <PackageId>Phares.AA.Metadata</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <Version>8.0.118.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</Version>
<Version>9.0.100.0</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company> <Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<PackageReadmeFile>read-me.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows> <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@ -32,10 +37,13 @@
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'"> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\AA.Shared.csproj" /> <ProjectReference Include="..\Shared\AA.Shared.csproj" />

View File

@ -11,7 +11,7 @@ public class A_Metadata
private readonly ResultSettings _ResultSettings; private readonly ResultSettings _ResultSettings;
private readonly MetadataSettings _MetadataSettings; private readonly MetadataSettings _MetadataSettings;
private readonly ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> _FileGroups; private readonly ReadOnlyDictionary<int, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> _ResultSingletonFileGroups;
public A_Metadata(ResultSettings resultSettings, MetadataSettings metadataSettings) public A_Metadata(ResultSettings resultSettings, MetadataSettings metadataSettings)
{ {
@ -23,53 +23,28 @@ public class A_Metadata
includeResizeGroup: false, includeResizeGroup: false,
includeModel: false, includeModel: false,
includePredictorModel: false); includePredictorModel: false);
_FileGroups = IPath.GetKeyValuePairs(resultSettings, aResultsFullGroupDirectory, [resultSettings.ResultSingleton]); Dictionary<int, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> results = [];
} ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePairs = IPath.GetKeyValuePairs(resultSettings, aResultsFullGroupDirectory, [resultSettings.ResultSingleton]);
foreach (KeyValuePair<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePair in keyValuePairs)
private (int, FileInfo) GetFileInfo(ResultSettings resultSettings, FilePath filePath)
{ {
FileInfo result; foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValue in keyValuePair.Value)
FileInfo fileInfo = new(filePath.FullName);
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
DateTime minimumDateTime = fileInfo.CreationTime < fileInfo.LastWriteTime ? fileInfo.CreationTime : fileInfo.LastWriteTime;
int fileInfoMinimumYear = minimumDateTime.Year < resultSettings.EpicYear ? resultSettings.EpicYear : minimumDateTime.Year;
result = new(Path.Combine(_FileGroups[fileInfoMinimumYear][_ResultSettings.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
return (fileInfoMinimumYear, result);
}
private (int, string) GetJsonFile(ResultSettings resultSettings, FilePath filePath, ExifDirectory exifDirectory)
{ {
string? result; if (keyValue.Key == resultSettings.ResultSingleton)
DateTime? dateTime; results.Add(keyValuePair.Key, keyValue.Value);
dateTime = IDate.GetDateTimeOriginal(exifDirectory); else
dateTime ??= IDate.GetMinimum(exifDirectory);
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
int exifYear = dateTime.Value.Year < resultSettings.EpicYear ? resultSettings.EpicYear : dateTime.Value.Year;
result = Path.Combine(_FileGroups[exifYear][_ResultSettings.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json");
return (exifYear, result);
}
private static (string, ExifDirectory?) Get(string jsonFile)
{
ExifDirectory? result;
string json = File.ReadAllText(jsonFile);
try
{
result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
if (result is null)
throw new Exception(); throw new Exception();
} }
catch (Exception)
{
result = null;
} }
return (json, result); _ResultSingletonFileGroups = new(results);
ReadOnlyCollection<string> directories = new([Path.Combine(aResultsFullGroupDirectory, resultSettings.ResultSingleton)]);
IPath.CreateDirectories(directories);
} }
public (FileInfo, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath) public (MinimumYearAndPathCombined, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath)
{ {
ExifDirectory? result; ExifDirectory? result;
(int fileInfoMinimumYear, FileInfo fileInfo) = GetFileInfo(resultSettings, filePath); MinimumYearAndPathCombined minimumYearAndPathCombined = GetMinimumYearAndPathCombined(resultSettings, filePath);
FileInfo fileInfo = new(minimumYearAndPathCombined.PathCombined);
if (_MetadataSettings.ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) if (_MetadataSettings.ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{ {
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName); File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
@ -103,10 +78,12 @@ public class A_Metadata
if (result is null) if (result is null)
{ {
string json; string json;
Stream stream = File.OpenRead(filePath.FullName);
const PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName = null; const PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName = null;
result = Exif.GetExifDirectory(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName); result = Exif.GetExifDirectory(filePath, stream, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
stream.Dispose();
(int exifYear, string jsonFile) = GetJsonFile(_ResultSettings, filePath, result); (int exifYear, string jsonFile) = GetJsonFile(_ResultSettings, filePath, result);
if (exifYear == fileInfoMinimumYear) if (exifYear == minimumYearAndPathCombined.MinimumYear)
json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory); json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
else else
{ {
@ -131,7 +108,76 @@ public class A_Metadata
fileInfo.Refresh(); fileInfo.Refresh();
} }
} }
return (fileInfo, result); return new(minimumYearAndPathCombined, result);
}
private MinimumYearAndPathCombined GetMinimumYearAndPathCombined(ResultSettings resultSettings, FilePath filePath)
{
MinimumYearAndPathCombined result;
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
DateTime minimumDateTime = new(filePath.CreationTicks < filePath.LastWriteTicks ? filePath.CreationTicks : filePath.LastWriteTicks);
int minimumYear = minimumDateTime.Year < resultSettings.EpicYear ? resultSettings.EpicYear : minimumDateTime.Year;
result = new(minimumYear, Path.Combine(_ResultSingletonFileGroups[minimumYear][cei.Enum][cei.Index], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
return result;
}
private (int, string) GetJsonFile(ResultSettings resultSettings, FilePath filePath, ExifDirectory exifDirectory)
{
string? result;
DateTime? dateTime;
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
dateTime ??= IDate.GetMinimum(exifDirectory);
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
int exifYear = dateTime.Value.Year < resultSettings.EpicYear ? resultSettings.EpicYear : dateTime.Value.Year;
result = Path.Combine(_ResultSingletonFileGroups[exifYear][cei.Enum][cei.Index], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json");
return new(exifYear, result);
}
private static (string, ExifDirectory?) Get(string jsonFile)
{
ExifDirectory? result;
string json = File.ReadAllText(jsonFile);
try
{
result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
if (result is null)
throw new Exception();
}
catch (Exception)
{
result = null;
}
return new(json, result);
}
public (MinimumYearAndPathCombined, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, HttpClient? httpClient, FilePath filePath)
{
ExifDirectory result;
MinimumYearAndPathCombined minimumYearAndPathCombined;
if (httpClient is null)
(minimumYearAndPathCombined, result) = GetMetadataCollection(resultSettings, metadataSettings, filePath);
else
{
Stream stream = GetStream(httpClient, filePath);
minimumYearAndPathCombined = GetMinimumYearAndPathCombined(resultSettings, filePath);
const PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName = null;
result = Exif.GetExifDirectory(filePath, stream, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
stream.Dispose();
}
return new(minimumYearAndPathCombined, result);
}
private static Stream GetStream(HttpClient httpClient, FilePath filePath)
{
Stream result;
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(filePath.FullName);
httpResponseMessage.Wait();
Task task = httpResponseMessage.Result.Content.LoadIntoBufferAsync();
task.Wait();
Task<Stream> stream = httpResponseMessage.Result.Content.ReadAsStreamAsync();
stream.Wait();
result = stream.Result;
return result;
} }
} }

View File

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

View File

@ -8,18 +8,18 @@ internal static class Dimensions
#pragma warning disable IDE0230 #pragma warning disable IDE0230
private static readonly Dictionary<byte[], Func<BinaryReader, Size?>> _ImageFormatDecoders = new() private static readonly Dictionary<byte[], Func<BinaryReader, Size?>> _ImageFormatDecoders = new()
{ {
{ new byte[] { 0xff, 0xd8 }, DecodeJfif },
{ new byte[] { 0x42, 0x4D }, DecodeBitmap }, { new byte[] { 0x42, 0x4D }, DecodeBitmap },
{ new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif },
{ new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng },
{ new byte[] { 0xff, 0xd8 }, DecodeJfif },
{ new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP },
}; };
#pragma warning restore IDE0230 #pragma warning restore IDE0230
private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) private static bool StartsWith(List<byte> thisBytes, byte[] thatBytes)
{ {
for (int i = 0; i < thatBytes.Length; i += 1) for (int i = 0; i < thisBytes.Count && i < thatBytes.Length; i += 1)
{ {
if (thisBytes[i] == thatBytes[i]) if (thisBytes[i] == thatBytes[i])
continue; continue;
@ -103,24 +103,49 @@ internal static class Dimensions
internal static Size? GetDimensions(BinaryReader binaryReader) internal static Size? GetDimensions(BinaryReader binaryReader)
{ {
int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; Size? result;
byte[] magicBytes = new byte[maxMagicBytesLength]; List<byte> magicBytes = [];
for (int i = 0; i < maxMagicBytesLength; i += 1) int[] magicBytesLengths = (from l in _ImageFormatDecoders.Keys where l.Length <= binaryReader.BaseStream.Length orderby l.Length descending select l.Length).ToArray();
if (magicBytesLengths.Length == 0)
result = null;
else
{ {
magicBytes[i] = binaryReader.ReadByte(); result = null;
if (binaryReader.BaseStream.Length == binaryReader.BaseStream.Position)
_ = binaryReader.BaseStream.Seek(0, SeekOrigin.Begin);
for (int i = 0; i < magicBytesLengths[0]; i++)
{
magicBytes.Add(binaryReader.ReadByte());
foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders) foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders)
{ {
if (StartsWith(magicBytes, kvPair.Key)) if (StartsWith(magicBytes, kvPair.Key))
return kvPair.Value(binaryReader); {
result = kvPair.Value(binaryReader);
break;
} }
} }
return null; if (result is not null)
break;
}
}
return result;
} }
internal static Size? GetDimensions(string path) internal static Size? GetDimensions(string path)
{ {
using BinaryReader binaryReader = new(File.OpenRead(path)); Size? result;
return GetDimensions(binaryReader); using FileStream fileStream = File.OpenRead(path);
using BinaryReader binaryReader = new(fileStream);
result = GetDimensions(binaryReader);
return result;
}
internal static Size? GetDimensions(Stream stream)
{
Size? result;
using BinaryReader binaryReader = new(stream);
result = GetDimensions(binaryReader);
return result;
} }
} }

View File

@ -27,24 +27,22 @@ internal abstract class Exif
private static Shared.Models.AviDirectory[] GetAviDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.AviDirectory[] GetAviDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.AviDirectory> results = []; List<Shared.Models.AviDirectory> results = [];
Shared.Models.AviDirectory aviDirectory;
IEnumerable<MetadataExtractor.Formats.Avi.AviDirectory> aviDirectories = directories.OfType<MetadataExtractor.Formats.Avi.AviDirectory>(); IEnumerable<MetadataExtractor.Formats.Avi.AviDirectory> aviDirectories = directories.OfType<MetadataExtractor.Formats.Avi.AviDirectory>();
foreach (MetadataExtractor.Formats.Avi.AviDirectory a in aviDirectories) foreach (MetadataExtractor.Formats.Avi.AviDirectory aviDirectory in aviDirectories)
{ {
if (a.Tags.Count == 0) if (aviDirectory.Tags.Count == 0)
continue; continue;
DateTime? dateTimeOriginal; DateTime? dateTimeOriginal;
string? duration = a.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration); string? duration = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration);
string? height = a.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight); string? height = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight);
string? width = a.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagWidth); string? width = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagWidth);
if (a.TryGetDateTime(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal, out DateTime checkDateTime)) if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal, out DateTime checkDateTime))
dateTimeOriginal = checkDateTime; dateTimeOriginal = checkDateTime;
else else
dateTimeOriginal = GetDateTime(a.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal));
if (dateTimeOriginal is null && duration is null && height is null && width is null) if (dateTimeOriginal is null && duration is null && height is null && width is null)
continue; continue;
aviDirectory = new(dateTimeOriginal, duration, height, width); results.Add(new(dateTimeOriginal, duration, height, width));
results.Add(aviDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -52,71 +50,72 @@ internal abstract class Exif
private static Shared.Models.ExifDirectoryBase[] GetExifBaseDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.ExifDirectoryBase[] GetExifBaseDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.ExifDirectoryBase> results = []; List<Shared.Models.ExifDirectoryBase> results = [];
Shared.Models.ExifDirectoryBase exifDirectoryBase;
IEnumerable<ExifDirectoryBase> exifBaseDirectories = directories.OfType<ExifDirectoryBase>(); IEnumerable<ExifDirectoryBase> exifBaseDirectories = directories.OfType<ExifDirectoryBase>();
foreach (ExifDirectoryBase e in exifBaseDirectories) foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{ {
if (e.Tags.Count == 0) if (exifDirectoryBase.Tags.Count == 0)
continue; continue;
DateTime? dateTime; DateTime? dateTime;
DateTime checkDateTime; DateTime checkDateTime;
DateTime? dateTimeOriginal; DateTime? dateTimeOriginal;
DateTime? dateTimeDigitized; DateTime? dateTimeDigitized;
string? aperture = e.GetDescription(ExifDirectoryBase.TagAperture); string? aperture = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagAperture);
string? applicationNotes = e.GetDescription(ExifDirectoryBase.TagApplicationNotes); string? applicationNotes = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagApplicationNotes);
string? artist = e.GetDescription(ExifDirectoryBase.TagArtist); string? artist = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagArtist);
string? bitsPerSample = e.GetDescription(ExifDirectoryBase.TagBitsPerSample); string? bitsPerSample = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBitsPerSample);
string? bodySerialNumber = e.GetDescription(ExifDirectoryBase.TagBodySerialNumber); string? bodySerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBodySerialNumber);
string? cameraOwnerName = e.GetDescription(ExifDirectoryBase.TagCameraOwnerName); string? cameraOwnerName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCameraOwnerName);
string? compressedAverageBitsPerPixel = e.GetDescription(ExifDirectoryBase.TagCompressedAverageBitsPerPixel); string? compressedAverageBitsPerPixel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompressedAverageBitsPerPixel);
string? compression = e.GetDescription(ExifDirectoryBase.TagCompression); string? compression = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompression);
string? copyright = e.GetDescription(ExifDirectoryBase.TagCopyright); string? copyright = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCopyright);
string? documentName = e.GetDescription(ExifDirectoryBase.TagDocumentName); string? documentName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagDocumentName);
string? exifVersion = e.GetDescription(ExifDirectoryBase.TagExifVersion); string? exifVersion = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExifVersion);
string? exposureTime = e.GetDescription(ExifDirectoryBase.TagExposureTime); string? exposureTime = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExposureTime);
string? fileSource = e.GetDescription(ExifDirectoryBase.TagFileSource); string? fileSource = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagFileSource);
string? imageDescription = e.GetDescription(ExifDirectoryBase.TagImageDescription); string? imageDescription = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageDescription);
string? imageHeight = e.GetDescription(ExifDirectoryBase.TagImageHeight); string? imageHeight = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageHeight);
string? imageNumber = e.GetDescription(ExifDirectoryBase.TagImageNumber); int? imageHeightValue = imageHeight is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagImageHeight);
string? imageUniqueId = e.GetDescription(ExifDirectoryBase.TagImageUniqueId); string? imageNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageNumber);
string? imageWidth = e.GetDescription(ExifDirectoryBase.TagImageWidth); string? imageUniqueId = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageUniqueId);
string? isoSpeed = e.GetDescription(ExifDirectoryBase.TagIsoSpeed); string? imageWidth = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageWidth);
string? lensMake = e.GetDescription(ExifDirectoryBase.TagLensMake); int? imageWidthValue = imageWidth is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagImageWidth);
string? lensModel = e.GetDescription(ExifDirectoryBase.TagLensModel); string? isoSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagIsoSpeed);
string? lensSerialNumber = e.GetDescription(ExifDirectoryBase.TagLensSerialNumber); string? lensMake = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensMake);
string? make = e.GetDescription(ExifDirectoryBase.TagMake); string? lensModel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensModel);
string? makerNote = e.GetDescription(ExifDirectoryBase.TagMakernote); string? lensSerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensSerialNumber);
string? model = e.GetDescription(ExifDirectoryBase.TagModel); string? make = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMake);
string? orientation = e.GetDescription(ExifDirectoryBase.TagOrientation); string? makerNote = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMakernote);
int? orientationValue = orientation is null ? null : e.GetInt32(ExifDirectoryBase.TagOrientation); string? model = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagModel);
string? rating = e.GetDescription(ExifDirectoryBase.TagRating); string? orientation = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagOrientation);
string? ratingPercent = e.GetDescription(ExifDirectoryBase.TagRatingPercent); int? orientationValue = orientation is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagOrientation);
string? securityClassification = e.GetDescription(ExifDirectoryBase.TagSecurityClassification); string? rating = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRating);
string? shutterSpeed = e.GetDescription(ExifDirectoryBase.TagShutterSpeed); string? ratingPercent = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRatingPercent);
string? software = e.GetDescription(ExifDirectoryBase.TagSoftware); string? securityClassification = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSecurityClassification);
string? timeZone = e.GetDescription(ExifDirectoryBase.TagTimeZone); string? shutterSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagShutterSpeed);
string? timeZoneDigitized = e.GetDescription(ExifDirectoryBase.TagTimeZoneDigitized); string? software = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSoftware);
string? timeZoneOriginal = e.GetDescription(ExifDirectoryBase.TagTimeZoneOriginal); string? timeZone = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZone);
string? userComment = e.GetDescription(ExifDirectoryBase.TagUserComment); string? timeZoneDigitized = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneDigitized);
string? winAuthor = e.GetDescription(ExifDirectoryBase.TagWinAuthor); string? timeZoneOriginal = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneOriginal);
string? winComment = e.GetDescription(ExifDirectoryBase.TagWinComment); string? userComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagUserComment);
string? winKeywords = e.GetDescription(ExifDirectoryBase.TagWinKeywords); string? winAuthor = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinAuthor);
string? winSubject = e.GetDescription(ExifDirectoryBase.TagWinSubject); string? winComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinComment);
string? winTitle = e.GetDescription(ExifDirectoryBase.TagWinTitle); string? winKeywords = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinKeywords);
string? xResolution = e.GetDescription(ExifDirectoryBase.TagXResolution); string? winSubject = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinSubject);
string? yResolution = e.GetDescription(ExifDirectoryBase.TagYResolution); string? winTitle = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinTitle);
if (e.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime)) string? xResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagXResolution);
string? yResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagYResolution);
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime))
dateTime = checkDateTime; dateTime = checkDateTime;
else else
dateTime = GetDateTime(e.GetString(ExifDirectoryBase.TagDateTime)); dateTime = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTime));
if (e.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime)) if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime))
dateTimeOriginal = checkDateTime; dateTimeOriginal = checkDateTime;
else else
dateTimeOriginal = GetDateTime(e.GetString(ExifDirectoryBase.TagDateTimeOriginal)); dateTimeOriginal = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeOriginal));
if (e.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime)) if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime))
dateTimeDigitized = checkDateTime; dateTimeDigitized = checkDateTime;
else else
dateTimeDigitized = GetDateTime(e.GetString(ExifDirectoryBase.TagDateTimeDigitized)); dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized));
if (userComment is not null && userComment.Length > 255) if (userComment is not null && userComment.Length > 255)
userComment = "..."; userComment = "...";
if (aperture is null if (aperture is null
@ -166,53 +165,54 @@ internal abstract class Exif
&& xResolution is not null && xResolution is not null
&& yResolution is null) && yResolution is null)
continue; continue;
exifDirectoryBase = new(aperture, results.Add(new(Aperture: aperture,
applicationNotes, ApplicationNotes: applicationNotes,
artist, Artist: artist,
bitsPerSample, BitsPerSample: bitsPerSample,
bodySerialNumber, BodySerialNumber: bodySerialNumber,
cameraOwnerName, CameraOwnerName: cameraOwnerName,
compressedAverageBitsPerPixel, CompressedAverageBitsPerPixel: compressedAverageBitsPerPixel,
compression, Compression: compression,
copyright, Copyright: copyright,
dateTime, DateTime: dateTime,
dateTimeDigitized, DateTimeDigitized: dateTimeDigitized,
dateTimeOriginal, DateTimeOriginal: dateTimeOriginal,
documentName, DocumentName: documentName,
exifVersion, ExifVersion: exifVersion,
exposureTime, ExposureTime: exposureTime,
fileSource, FileSource: fileSource,
imageDescription, ImageDescription: imageDescription,
imageHeight, ImageHeight: imageHeight,
imageNumber, ImageHeightValue: imageHeightValue,
imageUniqueId, ImageNumber: imageNumber,
imageWidth, ImageUniqueId: imageUniqueId,
isoSpeed, ImageWidth: imageWidth,
lensMake, ImageWidthValue: imageWidthValue,
lensModel, IsoSpeed: isoSpeed,
lensSerialNumber, LensMake: lensMake,
make, LensModel: lensModel,
makerNote, LensSerialNumber: lensSerialNumber,
model, Make: make,
orientation, MakerNote: makerNote,
orientationValue, Model: model,
rating, Orientation: orientation,
ratingPercent, OrientationValue: orientationValue,
securityClassification, Rating: rating,
shutterSpeed, RatingPercent: ratingPercent,
software, SecurityClassification: securityClassification,
timeZone, ShutterSpeed: shutterSpeed,
timeZoneDigitized, Software: software,
timeZoneOriginal, TimeZone: timeZone,
userComment, TimeZoneDigitized: timeZoneDigitized,
winAuthor, TimeZoneOriginal: timeZoneOriginal,
winComment, UserComment: userComment,
winKeywords, WinAuthor: winAuthor,
winSubject, WinComment: winComment,
winTitle, WinKeywords: winKeywords,
xResolution, WinSubject: winSubject,
yResolution); WinTitle: winTitle,
results.Add(exifDirectoryBase); XResolution: xResolution,
YResolution: yResolution));
} }
return results.ToArray(); return results.ToArray();
} }
@ -220,25 +220,23 @@ internal abstract class Exif
private static Shared.Models.FileMetadataDirectory[] GetFileMetadataDirectories(string file, IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.FileMetadataDirectory[] GetFileMetadataDirectories(string file, IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.FileMetadataDirectory> results = []; List<Shared.Models.FileMetadataDirectory> results = [];
Shared.Models.FileMetadataDirectory fileMetadataDirectory;
IEnumerable<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory> fileMetadataDirectories = directories.OfType<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory>(); IEnumerable<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory> fileMetadataDirectories = directories.OfType<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory>();
foreach (MetadataExtractor.Formats.FileSystem.FileMetadataDirectory f in fileMetadataDirectories) foreach (MetadataExtractor.Formats.FileSystem.FileMetadataDirectory fileMetadataDirectory in fileMetadataDirectories)
{ {
if (f.Tags.Count == 0) if (fileMetadataDirectory.Tags.Count == 0)
continue; continue;
DateTime? fileModifiedDate; DateTime? fileModifiedDate;
string? fileName = f.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName); string? fileName = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName);
string? fileSize = f.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize); string? fileSize = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize);
if (f.TryGetDateTime(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate, out DateTime checkDateTime)) if (fileMetadataDirectory.TryGetDateTime(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate, out DateTime checkDateTime))
fileModifiedDate = checkDateTime; fileModifiedDate = checkDateTime;
else else
fileModifiedDate = GetDateTime(f.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate)); fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate));
if (fileName is null || !file.EndsWith(fileName)) if (fileName is null || !file.EndsWith(fileName))
throw new NotSupportedException($"!{file}.EndsWith({fileName})"); throw new NotSupportedException($"!{file}.EndsWith({fileName})");
if (fileModifiedDate is null && fileName is null && fileSize is null) if (fileModifiedDate is null && fileName is null && fileSize is null)
continue; continue;
fileMetadataDirectory = new(fileModifiedDate, fileName, fileSize); results.Add(new(fileModifiedDate, fileName, fileSize));
results.Add(fileMetadataDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -246,18 +244,16 @@ internal abstract class Exif
private static Shared.Models.GifHeaderDirectory[] GetGifHeaderDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.GifHeaderDirectory[] GetGifHeaderDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.GifHeaderDirectory> results = []; List<Shared.Models.GifHeaderDirectory> results = [];
Shared.Models.GifHeaderDirectory gifHeaderDirectory;
IEnumerable<MetadataExtractor.Formats.Gif.GifHeaderDirectory> gifHeaderDirectories = directories.OfType<MetadataExtractor.Formats.Gif.GifHeaderDirectory>(); IEnumerable<MetadataExtractor.Formats.Gif.GifHeaderDirectory> gifHeaderDirectories = directories.OfType<MetadataExtractor.Formats.Gif.GifHeaderDirectory>();
foreach (MetadataExtractor.Formats.Gif.GifHeaderDirectory g in gifHeaderDirectories) foreach (MetadataExtractor.Formats.Gif.GifHeaderDirectory gifHeaderDirectory in gifHeaderDirectories)
{ {
if (g.Tags.Count == 0) if (gifHeaderDirectory.Tags.Count == 0)
continue; continue;
string? imageHeight = g.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight); string? imageHeight = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight);
string? imageWidth = g.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth); string? imageWidth = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth);
if (imageHeight is null && imageWidth is null) if (imageHeight is null && imageWidth is null)
continue; continue;
gifHeaderDirectory = new(imageHeight, imageWidth); results.Add(new(imageHeight, imageWidth));
results.Add(gifHeaderDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -265,31 +261,29 @@ internal abstract class Exif
private static Shared.Models.GpsDirectory[] GetGpsDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.GpsDirectory[] GetGpsDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.GpsDirectory> results = []; List<Shared.Models.GpsDirectory> results = [];
Shared.Models.GpsDirectory gpsDirectory;
IEnumerable<GpsDirectory> gpsDirectories = directories.OfType<GpsDirectory>(); IEnumerable<GpsDirectory> gpsDirectories = directories.OfType<GpsDirectory>();
foreach (GpsDirectory g in gpsDirectories) foreach (GpsDirectory gpsDirectory in gpsDirectories)
{ {
if (g.Tags.Count == 0) if (gpsDirectory.Tags.Count == 0)
continue; continue;
DateTime? timeStamp; DateTime? timeStamp;
string? altitude = g.GetDescription(GpsDirectory.TagAltitude); string? altitude = gpsDirectory.GetDescription(GpsDirectory.TagAltitude);
string? latitude = g.GetDescription(GpsDirectory.TagLatitude); string? latitude = gpsDirectory.GetDescription(GpsDirectory.TagLatitude);
string? latitudeRef = g.GetDescription(GpsDirectory.TagLatitudeRef); string? latitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLatitudeRef);
string? longitude = g.GetDescription(GpsDirectory.TagLongitude); string? longitude = gpsDirectory.GetDescription(GpsDirectory.TagLongitude);
string? longitudeRef = g.GetDescription(GpsDirectory.TagLongitudeRef); string? longitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLongitudeRef);
if (g.TryGetDateTime(GpsDirectory.TagTimeStamp, out DateTime checkDateTime)) if (gpsDirectory.TryGetDateTime(GpsDirectory.TagTimeStamp, out DateTime checkDateTime))
timeStamp = checkDateTime; timeStamp = checkDateTime;
else else
timeStamp = GetDateTime(g.GetString(GpsDirectory.TagTimeStamp)); timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp));
if (altitude is null && latitude is null && latitudeRef is null && longitude is null && longitudeRef is null && timeStamp is null) if (altitude is null && latitude is null && latitudeRef is null && longitude is null && longitudeRef is null && timeStamp is null)
continue; continue;
gpsDirectory = new(altitude, results.Add(new(altitude,
latitude, latitude,
latitudeRef, latitudeRef,
longitude, longitude,
longitudeRef, longitudeRef,
timeStamp); timeStamp));
results.Add(gpsDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -297,18 +291,16 @@ internal abstract class Exif
private static Shared.Models.JpegDirectory[] GetJpegDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.JpegDirectory[] GetJpegDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.JpegDirectory> results = []; List<Shared.Models.JpegDirectory> results = [];
Shared.Models.JpegDirectory jpegDirectory;
IEnumerable<MetadataExtractor.Formats.Jpeg.JpegDirectory> jpegDirectories = directories.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>(); IEnumerable<MetadataExtractor.Formats.Jpeg.JpegDirectory> jpegDirectories = directories.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>();
foreach (MetadataExtractor.Formats.Jpeg.JpegDirectory j in jpegDirectories) foreach (MetadataExtractor.Formats.Jpeg.JpegDirectory jpegDirectory in jpegDirectories)
{ {
if (j.Tags.Count == 0) if (jpegDirectory.Tags.Count == 0)
continue; continue;
string? imageHeight = j.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight); string? imageHeight = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight);
string? imageWidth = j.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth); string? imageWidth = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth);
if (imageHeight is null && imageWidth is null) if (imageHeight is null && imageWidth is null)
continue; continue;
jpegDirectory = new(imageHeight, imageWidth); results.Add(new(imageHeight, imageWidth));
results.Add(jpegDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -316,7 +308,6 @@ internal abstract class Exif
private static Shared.Models.MakernoteDirectory[] GetMakernoteDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.MakernoteDirectory[] GetMakernoteDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.MakernoteDirectory> results = []; List<Shared.Models.MakernoteDirectory> results = [];
Shared.Models.MakernoteDirectory makernoteDirectory;
IEnumerable<AppleMakernoteDirectory> appleMakernoteDirectories = directories.OfType<AppleMakernoteDirectory>(); IEnumerable<AppleMakernoteDirectory> appleMakernoteDirectories = directories.OfType<AppleMakernoteDirectory>();
foreach (AppleMakernoteDirectory appleMakernoteDirectory in appleMakernoteDirectories) foreach (AppleMakernoteDirectory appleMakernoteDirectory in appleMakernoteDirectories)
{ {
@ -327,8 +318,7 @@ internal abstract class Exif
string? qualityAndFileFormat = null; string? qualityAndFileFormat = null;
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue; continue;
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat); results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
results.Add(makernoteDirectory);
} }
IEnumerable<CanonMakernoteDirectory> canonMakernoteDirectories = directories.OfType<CanonMakernoteDirectory>(); IEnumerable<CanonMakernoteDirectory> canonMakernoteDirectories = directories.OfType<CanonMakernoteDirectory>();
foreach (CanonMakernoteDirectory canonMakernoteDirectory in canonMakernoteDirectories) foreach (CanonMakernoteDirectory canonMakernoteDirectory in canonMakernoteDirectories)
@ -340,8 +330,7 @@ internal abstract class Exif
string? qualityAndFileFormat = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.CameraSettings.TagQuality); string? qualityAndFileFormat = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.CameraSettings.TagQuality);
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue; continue;
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat); results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
results.Add(makernoteDirectory);
} }
IEnumerable<NikonType2MakernoteDirectory> nikonType2MakernoteDirectories = directories.OfType<NikonType2MakernoteDirectory>(); IEnumerable<NikonType2MakernoteDirectory> nikonType2MakernoteDirectories = directories.OfType<NikonType2MakernoteDirectory>();
foreach (NikonType2MakernoteDirectory nikonType2MakernoteDirectory in nikonType2MakernoteDirectories) foreach (NikonType2MakernoteDirectory nikonType2MakernoteDirectory in nikonType2MakernoteDirectories)
@ -353,8 +342,7 @@ internal abstract class Exif
string? qualityAndFileFormat = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagQualityAndFileFormat); string? qualityAndFileFormat = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagQualityAndFileFormat);
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue; continue;
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat); results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
results.Add(makernoteDirectory);
} }
IEnumerable<OlympusMakernoteDirectory> olympusMakernoteDirectories = directories.OfType<OlympusMakernoteDirectory>(); IEnumerable<OlympusMakernoteDirectory> olympusMakernoteDirectories = directories.OfType<OlympusMakernoteDirectory>();
foreach (OlympusMakernoteDirectory olympusMakernoteDirectory in olympusMakernoteDirectories) foreach (OlympusMakernoteDirectory olympusMakernoteDirectory in olympusMakernoteDirectories)
@ -366,8 +354,7 @@ internal abstract class Exif
string? qualityAndFileFormat = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagJpegQuality); string? qualityAndFileFormat = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagJpegQuality);
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue; continue;
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat); results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
results.Add(makernoteDirectory);
} }
IEnumerable<PanasonicMakernoteDirectory> panasonicMakernoteDirectories = directories.OfType<PanasonicMakernoteDirectory>(); IEnumerable<PanasonicMakernoteDirectory> panasonicMakernoteDirectories = directories.OfType<PanasonicMakernoteDirectory>();
foreach (PanasonicMakernoteDirectory panasonicMakernoteDirectory in panasonicMakernoteDirectories) foreach (PanasonicMakernoteDirectory panasonicMakernoteDirectory in panasonicMakernoteDirectories)
@ -379,8 +366,7 @@ internal abstract class Exif
string? qualityAndFileFormat = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagQualityMode); string? qualityAndFileFormat = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagQualityMode);
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue; continue;
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat); results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
results.Add(makernoteDirectory);
} }
IEnumerable<SamsungType2MakernoteDirectory> samsungType2MakernoteDirectories = directories.OfType<SamsungType2MakernoteDirectory>(); IEnumerable<SamsungType2MakernoteDirectory> samsungType2MakernoteDirectories = directories.OfType<SamsungType2MakernoteDirectory>();
foreach (SamsungType2MakernoteDirectory samsungType2MakernoteDirectory in samsungType2MakernoteDirectories) foreach (SamsungType2MakernoteDirectory samsungType2MakernoteDirectory in samsungType2MakernoteDirectories)
@ -392,8 +378,7 @@ internal abstract class Exif
string? qualityAndFileFormat = null; string? qualityAndFileFormat = null;
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue; continue;
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat); results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
results.Add(makernoteDirectory);
} }
IEnumerable<SonyType6MakernoteDirectory> sonyType6MakernoteDirectories = directories.OfType<SonyType6MakernoteDirectory>(); IEnumerable<SonyType6MakernoteDirectory> sonyType6MakernoteDirectories = directories.OfType<SonyType6MakernoteDirectory>();
foreach (SonyType6MakernoteDirectory sonyType6MakernoteDirectory in sonyType6MakernoteDirectories) foreach (SonyType6MakernoteDirectory sonyType6MakernoteDirectory in sonyType6MakernoteDirectories)
@ -405,8 +390,7 @@ internal abstract class Exif
string? qualityAndFileFormat = null; string? qualityAndFileFormat = null;
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue; continue;
makernoteDirectory = new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat); results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
results.Add(makernoteDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -414,18 +398,16 @@ internal abstract class Exif
private static Shared.Models.PhotoshopDirectory[] GetPhotoshopDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.PhotoshopDirectory[] GetPhotoshopDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.PhotoshopDirectory> results = []; List<Shared.Models.PhotoshopDirectory> results = [];
Shared.Models.PhotoshopDirectory photoshopDirectory;
IEnumerable<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory> photoshopDirectories = directories.OfType<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory>(); IEnumerable<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory> photoshopDirectories = directories.OfType<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory>();
foreach (MetadataExtractor.Formats.Photoshop.PhotoshopDirectory p in photoshopDirectories) foreach (MetadataExtractor.Formats.Photoshop.PhotoshopDirectory photoshopDirectory in photoshopDirectories)
{ {
if (p.Tags.Count == 0) if (photoshopDirectory.Tags.Count == 0)
continue; continue;
string? jpegQuality = p.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality); string? jpegQuality = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality);
string? url = p.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl); string? url = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl);
if (jpegQuality is null && url is null) if (jpegQuality is null && url is null)
continue; continue;
photoshopDirectory = new(jpegQuality, url); results.Add(new(jpegQuality, url));
results.Add(photoshopDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -433,19 +415,17 @@ internal abstract class Exif
private static Shared.Models.PngDirectory[] GetPngDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.PngDirectory[] GetPngDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.PngDirectory> results = []; List<Shared.Models.PngDirectory> results = [];
Shared.Models.PngDirectory pngDirectory;
IEnumerable<MetadataExtractor.Formats.Png.PngDirectory> pngDirectories = directories.OfType<MetadataExtractor.Formats.Png.PngDirectory>(); IEnumerable<MetadataExtractor.Formats.Png.PngDirectory> pngDirectories = directories.OfType<MetadataExtractor.Formats.Png.PngDirectory>();
foreach (MetadataExtractor.Formats.Png.PngDirectory p in pngDirectories) foreach (MetadataExtractor.Formats.Png.PngDirectory pngDirectory in pngDirectories)
{ {
if (p.Tags.Count == 0) if (pngDirectory.Tags.Count == 0)
continue; continue;
string? imageHeight = p.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight); string? imageHeight = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight);
string? imageWidth = p.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth); string? imageWidth = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth);
string? textualData = p.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagTextualData); string? textualData = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagTextualData);
if (imageHeight is null && imageWidth is null && textualData is null) if (imageHeight is null && imageWidth is null && textualData is null)
continue; continue;
pngDirectory = new(imageHeight, imageWidth, textualData); results.Add(new(imageHeight, imageWidth, textualData));
results.Add(pngDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -453,21 +433,19 @@ internal abstract class Exif
private static Shared.Models.QuickTimeMovieHeaderDirectory[] GetQuickTimeMovieHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.QuickTimeMovieHeaderDirectory[] GetQuickTimeMovieHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.QuickTimeMovieHeaderDirectory> results = []; List<Shared.Models.QuickTimeMovieHeaderDirectory> results = [];
Shared.Models.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory;
IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory> quickTimeMovieHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory>(); IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory> quickTimeMovieHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory>();
foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory q in quickTimeMovieHeaderDirectories) foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in quickTimeMovieHeaderDirectories)
{ {
if (q.Tags.Count == 0) if (quickTimeMovieHeaderDirectory.Tags.Count == 0)
continue; continue;
DateTime? created; DateTime? created;
if (q.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime)) if (quickTimeMovieHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime))
created = checkDateTime; created = checkDateTime;
else else
created = GetDateTime(q.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); created = GetDateTime(quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated));
if (created is null) if (created is null)
continue; continue;
quickTimeMovieHeaderDirectory = new(created); results.Add(new(created));
results.Add(quickTimeMovieHeaderDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -475,21 +453,19 @@ internal abstract class Exif
private static Shared.Models.QuickTimeTrackHeaderDirectory[] GetQuickTimeTrackHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.QuickTimeTrackHeaderDirectory[] GetQuickTimeTrackHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.QuickTimeTrackHeaderDirectory> results = []; List<Shared.Models.QuickTimeTrackHeaderDirectory> results = [];
Shared.Models.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory;
IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory> quickTimeTrackHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory>(); IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory> quickTimeTrackHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory>();
foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory q in quickTimeTrackHeaderDirectories) foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in quickTimeTrackHeaderDirectories)
{ {
if (q.Tags.Count == 0) if (quickTimeTrackHeaderDirectory.Tags.Count == 0)
continue; continue;
DateTime? created; DateTime? created;
if (q.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime)) if (quickTimeTrackHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime))
created = checkDateTime; created = checkDateTime;
else else
created = GetDateTime(q.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); created = GetDateTime(quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated));
if (created is null) if (created is null)
continue; continue;
quickTimeTrackHeaderDirectory = new(created); results.Add(new(created));
results.Add(quickTimeTrackHeaderDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
@ -497,23 +473,21 @@ internal abstract class Exif
private static Shared.Models.WebPDirectory[] GetWebPDirectories(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.WebPDirectory[] GetWebPDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
List<Shared.Models.WebPDirectory> results = []; List<Shared.Models.WebPDirectory> results = [];
Shared.Models.WebPDirectory webPDirectory;
IEnumerable<MetadataExtractor.Formats.WebP.WebPDirectory> webPDirectories = directories.OfType<MetadataExtractor.Formats.WebP.WebPDirectory>(); IEnumerable<MetadataExtractor.Formats.WebP.WebPDirectory> webPDirectories = directories.OfType<MetadataExtractor.Formats.WebP.WebPDirectory>();
foreach (MetadataExtractor.Formats.WebP.WebPDirectory w in webPDirectories) foreach (MetadataExtractor.Formats.WebP.WebPDirectory webPDirectory in webPDirectories)
{ {
if (w.Tags.Count == 0) if (webPDirectory.Tags.Count == 0)
continue; continue;
string? imageHeight = w.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight); string? imageHeight = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight);
string? imageWidth = w.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth); string? imageWidth = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth);
if (imageHeight is null && imageWidth is null) if (imageHeight is null && imageWidth is null)
continue; continue;
webPDirectory = new(imageHeight, imageWidth); results.Add(new(imageHeight, imageWidth));
results.Add(webPDirectory);
} }
return results.ToArray(); return results.ToArray();
} }
private static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, IReadOnlyList<MetadataExtractor.Directory> directories, System.Drawing.Size? size)
{ {
Shared.Models.ExifDirectory result; Shared.Models.ExifDirectory result;
Shared.Models.AviDirectory[] aviDirectories = GetAviDirectories(directories); Shared.Models.AviDirectory[] aviDirectories = GetAviDirectories(directories);
@ -523,41 +497,40 @@ internal abstract class Exif
Shared.Models.WebPDirectory[] webPDirectories = GetWebPDirectories(directories); Shared.Models.WebPDirectory[] webPDirectories = GetWebPDirectories(directories);
Shared.Models.ExifDirectoryBase[] exifBaseDirectories = GetExifBaseDirectories(directories); Shared.Models.ExifDirectoryBase[] exifBaseDirectories = GetExifBaseDirectories(directories);
Shared.Models.GifHeaderDirectory[] gifHeaderDirectories = GetGifHeaderDirectories(directories); Shared.Models.GifHeaderDirectory[] gifHeaderDirectories = GetGifHeaderDirectories(directories);
Shared.Models.MakernoteDirectory[] MakernoteDirectories = GetMakernoteDirectories(directories); Shared.Models.MakernoteDirectory[] makernoteDirectories = GetMakernoteDirectories(directories);
Shared.Models.PhotoshopDirectory[] photoshopDirectories = GetPhotoshopDirectories(directories); Shared.Models.PhotoshopDirectory[] photoshopDirectories = GetPhotoshopDirectories(directories);
Shared.Models.FileMetadataDirectory[] fileMetadataDirectories = GetFileMetadataDirectories(filePath.FullName, directories); Shared.Models.FileMetadataDirectory[] fileMetadataDirectories = GetFileMetadataDirectories(filePath.FullName, directories);
Shared.Models.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories); Shared.Models.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories);
Shared.Models.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories); Shared.Models.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories);
result = new(aviDirectories, result = new(AviDirectories: aviDirectories,
null, ExifBaseDirectories: exifBaseDirectories,
exifBaseDirectories, FileMetadataDirectories: fileMetadataDirectories,
fileMetadataDirectories, FilePath: filePath,
filePath, GifHeaderDirectories: gifHeaderDirectories,
gifHeaderDirectories, GpsDirectories: gpsDirectories,
gpsDirectories, Height: size?.Height ?? Shared.Models.Stateless.IMetaBase.GetHeight(exifBaseDirectories),
size?.Height, JpegDirectories: jpegDirectories,
jpegDirectories, MakernoteDirectories: makernoteDirectories,
MakernoteDirectories, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: personKeyFormattedAndKeyTicksAndDisplayDirectoryName,
personKeyFormattedAndKeyTicksAndDisplayDirectoryName, PhotoshopDirectories: photoshopDirectories,
photoshopDirectories, PngDirectories: pngDirectories,
pngDirectories, QuickTimeMovieHeaderDirectories: quickTimeMovieHeaderDirectories,
quickTimeMovieHeaderDirectories, QuickTimeTrackHeaderDirectories: quickTimeTrackHeaderDirectories,
quickTimeTrackHeaderDirectories, WebPDirectories: webPDirectories,
webPDirectories, Width: size?.Width ?? Shared.Models.Stateless.IMetaBase.GetWidth(exifBaseDirectories));
size?.Width);
return result; return result;
} }
internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath, Shared.Models.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath, Stream stream, Shared.Models.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName)
{ {
Shared.Models.ExifDirectory result; Shared.Models.ExifDirectory result;
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(stream);
System.Drawing.Size? size; System.Drawing.Size? size;
try try
{ size = Dimensions.GetDimensions(filePath.FullName); } { size = Dimensions.GetDimensions(stream); }
catch (Exception) catch (Exception)
{ size = null; } { size = null; }
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName); result = Covert(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName, directories, size);
result = Covert(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName, size, directories);
return result; return result;
} }

View File

@ -31,16 +31,40 @@ internal static class Get
return results.AsReadOnly(); return results.AsReadOnly();
} }
internal static Action<string> SetExifDirectoryCollection(IRename rename, ResultSettings resultSettings, MetadataSettings metadataSettings, IRenameSettings renameSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) internal static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<NginxFileSystem> collection)
{
Dictionary<string, List<FileHolder>> results = [];
string key;
FileHolder fileHolder;
List<FileHolder>? fileHolders;
foreach (NginxFileSystem nginxFileSystem in collection)
{
fileHolder = FileHolder.Get(nginxFileSystem);
if (fileHolder.DirectoryFullPath is null)
continue;
key = $"{Path.Combine(fileHolder.DirectoryFullPath, fileHolder.NameWithoutExtension)}";
if (!results.TryGetValue(key, out fileHolders))
{
results.Add(key, []);
if (!results.TryGetValue(key, out fileHolders))
throw new NotImplementedException();
}
fileHolders.Add(fileHolder);
}
return results.AsReadOnly();
}
internal static Action<string> SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups)
{ {
return file => return file =>
{ {
rename.Tick(); windows.Tick();
FileInfo fileInfo;
ExifDirectory exifDirectory; ExifDirectory exifDirectory;
MetadataGroup metadataGroup; MetadataGroup metadataGroup;
HttpClient? httpClient = null;
DeterministicHashCode deterministicHashCode; DeterministicHashCode deterministicHashCode;
FileHolder fileHolder = FileHolder.Get(file); FileHolder fileHolder = FileHolder.Get(file);
MinimumYearAndPathCombined minimumYearAndPathCombined;
bool fastForwardMovingPictureExpertsGroupUsed; bool fastForwardMovingPictureExpertsGroupUsed;
FilePath? fastForwardMovingPictureExpertsGroupFilePath; FilePath? fastForwardMovingPictureExpertsGroupFilePath;
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles; ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
@ -48,7 +72,7 @@ internal static class Get
string key = $"{Path.Combine(fileHolder.DirectoryFullPath ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}"; string key = $"{Path.Combine(fileHolder.DirectoryFullPath ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}";
if (distinct.Contains(key)) if (distinct.Contains(key))
throw new NotSupportedException("Turn off parallelism when sidecar files are present!"); throw new NotSupportedException("Turn off parallelism when sidecar files are present!");
if (!renameSettings.SkipIdFiles || filePath.Id is null || (!filePath.IsIntelligentIdFormat && filePath.SortOrder is not null)) if (filePath.Id is null || (!filePath.IsIntelligentIdFormat && filePath.SortOrder is not null))
{ {
if (filePath.Id is not null) if (filePath.Id is not null)
{ {
@ -57,14 +81,14 @@ internal static class Get
} }
else else
{ {
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(renameSettings, filePath); fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(resultSettings, httpClient, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(resultSettings, metadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null); fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(resultSettings, metadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath); deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? windows.GetDeterministicHashCode(httpClient, filePath) : windows.GetDeterministicHashCode(httpClient, fastForwardMovingPictureExpertsGroupFilePath);
} }
filePath = FilePath.Get(filePath, deterministicHashCode); filePath = FilePath.Get(filePath, deterministicHashCode);
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0; fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(resultSettings, metadataSettings, filePath); (minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(resultSettings, metadataSettings, filePath);
metadataGroup = new(fastForwardMovingPictureExpertsGroupUsed, filePath, fileInfo, exifDirectory, new([])); metadataGroup = new(fastForwardMovingPictureExpertsGroupUsed, filePath, minimumYearAndPathCombined, exifDirectory, new([]));
lock (metadataGroups) lock (metadataGroups)
metadataGroups.Add(metadataGroup); metadataGroups.Add(metadataGroup);
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null) if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
@ -77,4 +101,48 @@ internal static class Get
}; };
} }
internal static Action<string> SetExifDirectoryCollection(IRename rename, ResultSettings resultSettings, MetadataSettings metadataSettings, IRenameSettings renameSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups)
{
return file =>
{
rename.Tick();
ExifDirectory exifDirectory;
MetadataGroup metadataGroup;
DeterministicHashCode deterministicHashCode;
FileHolder fileHolder = FileHolder.Get(file);
bool fastForwardMovingPictureExpertsGroupUsed;
MinimumYearAndPathCombined minimumYearAndPathCombined;
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
string key = $"{Path.Combine(fileHolder.DirectoryFullPath ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}";
if (distinct.Contains(key))
throw new NotSupportedException("Turn off parallelism when sidecar files are present!");
if (renameSettings.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is null))
return;
if (filePath.Id is not null)
{
fastForwardMovingPictureExpertsGroupFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
}
else
{
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(resultSettings, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(resultSettings, metadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath);
}
filePath = FilePath.Get(filePath, deterministicHashCode);
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
(minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(resultSettings, metadataSettings, filePath);
metadataGroup = new(fastForwardMovingPictureExpertsGroupUsed, filePath, minimumYearAndPathCombined, exifDirectory, new([]));
lock (metadataGroups)
metadataGroups.Add(metadataGroup);
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
{
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
};
}
} }

View File

@ -17,25 +17,10 @@ public interface IMetadata
Meters Meters
} }
ExifDirectory TestStatic_GetExifDirectory(FilePath filePath, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) => ExifDirectory TestStatic_GetExifDirectory(FilePath filePath, Stream stream, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) =>
GetExifDirectory(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName); GetExifDirectory(filePath, stream, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
static ExifDirectory GetExifDirectory(FilePath filePath, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) => static ExifDirectory GetExifDirectory(FilePath filePath, Stream stream, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) =>
Exif.GetExifDirectory(filePath, personKeyFormattedAndKeyTicksAndDisplayDirectoryName); Exif.GetExifDirectory(filePath, stream, personKeyFormattedAndKeyTicksAndDisplayDirectoryName);
string? TestStatic_GetMaker(ExifDirectory? exifDirectory) =>
GetMaker(exifDirectory);
static string? GetMaker(ExifDirectory? exifDirectory) =>
Base.GetMaker(exifDirectory?.ExifBaseDirectories);
string? TestStatic_GetModel(ExifDirectory? exifDirectory) =>
GetModel(exifDirectory);
static string? GetModel(ExifDirectory? exifDirectory) =>
Base.GetModel(exifDirectory?.ExifBaseDirectories);
ReadOnlyCollection<string> TestStatic_GetKeywords(ExifDirectory? exifDirectory) =>
GetKeywords(exifDirectory);
static ReadOnlyCollection<string> GetKeywords(ExifDirectory? exifDirectory) =>
Base.GetKeywords(exifDirectory?.ExifBaseDirectories);
string? TestStatic_GetOutputResolution(ExifDirectory? exifDirectory) => string? TestStatic_GetOutputResolution(ExifDirectory? exifDirectory) =>
GetOutputResolution(exifDirectory); GetOutputResolution(exifDirectory);
@ -67,4 +52,14 @@ public interface IMetadata
static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<string> files) => static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<string> files) =>
Get.GetKeyValuePairs(files); Get.GetKeyValuePairs(files);
ReadOnlyDictionary<string, List<FileHolder>> TestStatic_GetKeyValuePairs(IEnumerable<NginxFileSystem> collection) =>
GetKeyValuePairs(collection);
static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<NginxFileSystem> collection) =>
Get.GetKeyValuePairs(collection);
Action<string> TestStatic_SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
SetExifDirectoryCollection(windows, resultSettings, metadataSettings, metadata, distinct, metadataGroups);
static Action<string> SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
Get.SetExifDirectoryCollection(windows, resultSettings, metadataSettings, metadata, distinct, metadataGroups);
} }

1
People/.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

View File

@ -6,14 +6,19 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>Phares.AA.People</PackageId> <PackageId>Phares.AA.People</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>9.0.100.0</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company> <Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<PackageReadmeFile>read-me.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows> <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@ -32,9 +37,12 @@
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'"> <ItemGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

1
Rename/.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
@ -8,14 +8,19 @@
<UserSecretsIdOld>fa0fa59b-afe4-4960-9afc-18fcbc7fb41b</UserSecretsIdOld> <UserSecretsIdOld>fa0fa59b-afe4-4960-9afc-18fcbc7fb41b</UserSecretsIdOld>
<UserSecretsId>843db3e1-e18f-4cba-8b00-967529a32635</UserSecretsId> <UserSecretsId>843db3e1-e18f-4cba-8b00-967529a32635</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>Phares.View.by.Distance.Rename</PackageId> <PackageId>Phares.View.by.Distance.Rename</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>9.0.100.0</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company> <Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<PackageReadmeFile>read-me.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows> <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@ -35,12 +40,19 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.7" /> <None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="9.0.0" /> </ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" /> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.0" /> <PackageReference Include="CliWrap" Version="3.8.2" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.12" />
</ItemGroup>
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Metadata\AA.Metadata.csproj" /> <ProjectReference Include="..\Metadata\AA.Metadata.csproj" />

View File

@ -6,19 +6,22 @@ namespace View_by_Distance.Rename.Models;
public record RenameSettings(string Company, public record RenameSettings(string Company,
string DefaultMaker, string DefaultMaker,
Dictionary<string, string?> DirectoryDictionary,
string? FirstPassFile,
bool ForceNewId, bool ForceNewId,
string[] IgnoreExtensions,
bool InPlace, bool InPlace,
bool InPlaceMoveDirectory, bool InPlaceMoveDirectory,
bool InPlaceWithOriginalName, bool InPlaceWithOriginalName,
bool JustMediaDate,
int MaxDegreeOfParallelism, int MaxDegreeOfParallelism,
int MaxMilliSecondsPerCall,
bool OnlySaveIdentifiersToDisk, bool OnlySaveIdentifiersToDisk,
string RelativePropertyCollectionFile, string RelativePropertyCollectionFile,
bool RequireRootDirectoryExists, bool RequireRootDirectoryExists,
string[] SidecarExtensions, string[] SidecarExtensions,
bool SkipIdFiles, bool SkipIdFiles,
string[] ValidImageFormatExtensions, int ValidationImageDeterministicHashCodeId,
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.IRenameSettings string ValidationImageFile) : Shared.Models.Properties.IRenameSettings
{ {
public override string ToString() public override string ToString()

File diff suppressed because it is too large Load Diff

1
Shared/.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

View File

@ -3,16 +3,21 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember142023>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</HoursSinceNovember142023>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<PackageId>Phares.AA.Shared</PackageId> <PackageId>Phares.AA.Shared</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <Version>8.0.118.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</Version>
<Version>9.0.100.0</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company> <Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<PackageReadmeFile>read-me.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows> <IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
@ -32,7 +37,9 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="9.0.0" /> <None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="9.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

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

View File

@ -4,7 +4,6 @@ using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models; namespace View_by_Distance.Shared.Models;
public record ExifDirectory(AviDirectory[] AviDirectories, public record ExifDirectory(AviDirectory[] AviDirectories,
object? Encoding,
ExifDirectoryBase[] ExifBaseDirectories, ExifDirectoryBase[] ExifBaseDirectories,
FileMetadataDirectory[] FileMetadataDirectories, FileMetadataDirectory[] FileMetadataDirectories,
FilePath FilePath, FilePath FilePath,
@ -28,25 +27,6 @@ public record ExifDirectory(AviDirectory[] AviDirectories,
return result; return result;
} }
public static ExifDirectory Get(object encoding, ExifDirectory e) =>
new(e.AviDirectories,
encoding,
e.ExifBaseDirectories,
e.FileMetadataDirectories,
e.FilePath,
e.GifHeaderDirectories,
e.GpsDirectories,
e.Height,
e.JpegDirectories,
e.MakernoteDirectories,
e.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
e.PhotoshopDirectories,
e.PngDirectories,
e.QuickTimeMovieHeaderDirectories,
e.QuickTimeTrackHeaderDirectories,
e.WebPDirectories,
e.Width);
} }
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]

View File

@ -21,9 +21,11 @@ public record ExifDirectoryBase(string? Aperture,
string? FileSource, string? FileSource,
string? ImageDescription, string? ImageDescription,
string? ImageHeight, string? ImageHeight,
int? ImageHeightValue,
string? ImageNumber, string? ImageNumber,
string? ImageUniqueId, string? ImageUniqueId,
string? ImageWidth, string? ImageWidth,
int? ImageWidthValue,
string? IsoSpeed, string? IsoSpeed,
string? LensMake, string? LensMake,
string? LensModel, string? LensModel,

View File

@ -21,29 +21,44 @@ public record FileHolder(DateTime? CreationTime,
return result; return result;
} }
private static FileHolder GetExisting(NginxFileSystem nginxFileSystem, int? id) =>
new(CreationTime: nginxFileSystem.LastModified,
DirectoryFullPath: Path.GetDirectoryName(nginxFileSystem.URI?.OriginalString ?? throw new Exception()),
Exists: true,
ExtensionLowered: Path.GetExtension(nginxFileSystem.Name).ToLower(),
FullName: nginxFileSystem.URI?.OriginalString ?? throw new Exception(),
Id: id,
LastWriteTime: nginxFileSystem.LastModified,
Length: nginxFileSystem.Length is null ? null : (long)nginxFileSystem.Length.Value,
Name: nginxFileSystem.Name,
NameWithoutExtension: Path.GetFileNameWithoutExtension(nginxFileSystem.Name));
public static FileHolder Get(NginxFileSystem nginxFileSystem) =>
GetExisting(nginxFileSystem, id: null);
private static FileHolder GetExisting(FileInfo fileInfo, int? id) => private static FileHolder GetExisting(FileInfo fileInfo, int? id) =>
new(fileInfo.CreationTime, new(CreationTime: fileInfo.CreationTime,
fileInfo.DirectoryName, DirectoryFullPath: fileInfo.DirectoryName,
fileInfo.Exists, Exists: fileInfo.Exists,
fileInfo.Extension.ToLower(), ExtensionLowered: fileInfo.Extension.ToLower(),
fileInfo.FullName, FullName: fileInfo.FullName,
id, Id: id,
fileInfo.LastWriteTime, LastWriteTime: fileInfo.LastWriteTime,
fileInfo.Length, Length: fileInfo.Length,
fileInfo.Name, Name: fileInfo.Name,
Path.GetFileNameWithoutExtension(fileInfo.FullName)); NameWithoutExtension: Path.GetFileNameWithoutExtension(fileInfo.FullName));
private static FileHolder GetNonExisting(FileInfo fileInfo, int? id) => private static FileHolder GetNonExisting(FileInfo fileInfo, int? id) =>
new(null, new(CreationTime: null,
fileInfo.DirectoryName, DirectoryFullPath: fileInfo.DirectoryName,
fileInfo.Exists, Exists: fileInfo.Exists,
fileInfo.Extension.ToLower(), ExtensionLowered: fileInfo.Extension.ToLower(),
fileInfo.FullName, FullName: fileInfo.FullName,
id, Id: id,
null, LastWriteTime: null,
null, Length: null,
fileInfo.Name, Name: fileInfo.Name,
Path.GetFileNameWithoutExtension(fileInfo.FullName)); NameWithoutExtension: Path.GetFileNameWithoutExtension(fileInfo.FullName));
public static FileHolder Get(FileInfo fileInfo, int? id) => public static FileHolder Get(FileInfo fileInfo, int? id) =>
fileInfo.Exists ? GetExisting(fileInfo, id) : GetNonExisting(fileInfo, id); fileInfo.Exists ? GetExisting(fileInfo, id) : GetNonExisting(fileInfo, id);
@ -52,42 +67,42 @@ public record FileHolder(DateTime? CreationTime,
{ {
FileHolder result; FileHolder result;
DateTime dateTime = new(filePath.CreationTicks); DateTime dateTime = new(filePath.CreationTicks);
result = new(dateTime, result = new(CreationTime: dateTime,
filePath.DirectoryFullPath, DirectoryFullPath: filePath.DirectoryFullPath,
true, Exists: true,
filePath.ExtensionLowered, ExtensionLowered: filePath.ExtensionLowered,
filePath.FullName, FullName: filePath.FullName,
id, Id: id,
new(filePath.LastWriteTicks), LastWriteTime: new(filePath.LastWriteTicks),
filePath.Length, Length: filePath.Length,
filePath.Name, Name: filePath.Name,
Path.GetFileNameWithoutExtension(filePath.FullName)); NameWithoutExtension: Path.GetFileNameWithoutExtension(filePath.FullName));
return result; return result;
} }
private static FileHolder GetExisting(FileHolder fileHolder) => private static FileHolder GetExisting(FileHolder fileHolder) =>
new(fileHolder.CreationTime, new(CreationTime: fileHolder.CreationTime,
fileHolder.DirectoryFullPath, DirectoryFullPath: fileHolder.DirectoryFullPath,
fileHolder.Exists, Exists: fileHolder.Exists,
fileHolder.ExtensionLowered, ExtensionLowered: fileHolder.ExtensionLowered,
fileHolder.FullName, FullName: fileHolder.FullName,
Id: null, Id: null,
fileHolder.LastWriteTime, LastWriteTime: fileHolder.LastWriteTime,
fileHolder.Length, Length: fileHolder.Length,
fileHolder.Name, Name: fileHolder.Name,
Path.GetFileNameWithoutExtension(fileHolder.FullName)); NameWithoutExtension: Path.GetFileNameWithoutExtension(fileHolder.FullName));
private static FileHolder GetNonExisting(FileHolder fileHolder) => private static FileHolder GetNonExisting(FileHolder fileHolder) =>
new(null, new(CreationTime: null,
fileHolder.DirectoryFullPath, DirectoryFullPath: fileHolder.DirectoryFullPath,
fileHolder.Exists, Exists: fileHolder.Exists,
fileHolder.ExtensionLowered, ExtensionLowered: fileHolder.ExtensionLowered,
fileHolder.FullName, FullName: fileHolder.FullName,
Id: null, Id: null,
null, LastWriteTime: null,
null, Length: null,
fileHolder.Name, Name: fileHolder.Name,
Path.GetFileNameWithoutExtension(fileHolder.FullName)); NameWithoutExtension: Path.GetFileNameWithoutExtension(fileHolder.FullName));
public static FileHolder Get(FileHolder fileHolder) => public static FileHolder Get(FileHolder fileHolder) =>
fileHolder.Exists ? GetExisting(fileHolder) : GetNonExisting(fileHolder); fileHolder.Exists ? GetExisting(fileHolder) : GetNonExisting(fileHolder);

View File

@ -0,0 +1,30 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record FirstPass(ExifDirectory ExifDirectory,
bool FastForwardMovingPictureExpertsGroupUsed,
MinimumYearAndPathCombined MinimumYearAndPathCombined,
FileHolder[] SidecarFiles)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, FirstPassSourceGenerationContext.Default.FilePath);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(FirstPass))]
public partial class FirstPassSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(List<FirstPass>))]
public partial class FirstPassCollectionSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,4 +1,5 @@
using System.Drawing; using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models; namespace View_by_Distance.Shared.Models;
@ -10,40 +11,67 @@ public record LocationContainer(DateOnly? CreationDateOnly,
int? LengthPermyriad, int? LengthPermyriad,
FilePath? LengthSource, FilePath? LengthSource,
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
RectangleF? Rectangle,
int? WholePercentages) int? WholePercentages)
{ {
public static LocationContainer Get(LocationContainer locationContainer, object? encoding, bool keepExifDirectory) public string GetWithoutEncoding()
{
string result;
WithoutEncoding withoutEncoding = new(CreationDateOnly: CreationDateOnly,
ExifDirectory: ExifDirectory,
FaceFile: FaceFile,
FilePath: FilePath,
LengthPermyriad: LengthPermyriad,
LengthSource: LengthSource,
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
WholePercentages: WholePercentages);
result = JsonSerializer.Serialize(withoutEncoding, WithoutEncodingSourceGenerationContext.Default.WithoutEncoding);
return result;
}
public static LocationContainer Get(LocationContainer locationContainer, object? encoding)
{ {
LocationContainer result; LocationContainer result;
result = new(locationContainer.CreationDateOnly, result = new(CreationDateOnly: locationContainer.CreationDateOnly,
keepExifDirectory ? locationContainer.ExifDirectory : null, ExifDirectory: locationContainer.ExifDirectory,
encoding, Encoding: encoding,
locationContainer.FaceFile, FaceFile: locationContainer.FaceFile,
locationContainer.FilePath, FilePath: locationContainer.FilePath,
locationContainer.LengthPermyriad, LengthPermyriad: locationContainer.LengthPermyriad,
locationContainer.LengthSource, LengthSource: locationContainer.LengthSource,
locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
locationContainer.Rectangle, WholePercentages: locationContainer.WholePercentages);
locationContainer.WholePercentages);
return result; return result;
} }
public static LocationContainer Get(LocationContainer source, LocationContainer locationContainer, int lengthPermyriad, bool keepExifDirectory, bool keepEncoding) public static LocationContainer Get(LocationContainer source, LocationContainer locationContainer, int lengthPermyriad, bool keepExifDirectory, bool keepEncoding)
{ {
LocationContainer result; LocationContainer result;
result = new(locationContainer.CreationDateOnly, result = new(CreationDateOnly: locationContainer.CreationDateOnly,
keepExifDirectory ? locationContainer.ExifDirectory : null, ExifDirectory: keepExifDirectory ? locationContainer.ExifDirectory : null,
keepEncoding ? locationContainer.Encoding : null, Encoding: keepEncoding ? locationContainer.Encoding : null,
locationContainer.FaceFile, FaceFile: locationContainer.FaceFile,
locationContainer.FilePath, FilePath: locationContainer.FilePath,
lengthPermyriad, LengthPermyriad: lengthPermyriad,
source.FilePath, LengthSource: source.FilePath,
locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName: locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
locationContainer.Rectangle, WholePercentages: locationContainer.WholePercentages);
locationContainer.WholePercentages);
return result; return result;
} }
} }
internal record WithoutEncoding(DateOnly? CreationDateOnly,
ExifDirectory? ExifDirectory,
FaceFile? FaceFile,
FilePath FilePath,
int? LengthPermyriad,
FilePath? LengthSource,
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
int? WholePercentages);
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(WithoutEncoding))]
internal partial class WithoutEncodingSourceGenerationContext : JsonSerializerContext
{
}

135
Shared/Models/MetaBase.cs Normal file
View File

@ -0,0 +1,135 @@
using System.Collections.ObjectModel;
using System.Globalization;
namespace View_by_Distance.Shared.Models;
internal static class MetaBase
{
internal static string? GetMaker(ExifDirectoryBase[]? exifBaseDirectories)
{
string? result = null;
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.Make is null ? string.Empty : exifDirectoryBase.Make.ToString().Trim();
if (string.IsNullOrEmpty(value))
result = null;
else
{
result = $"{value[0].ToString().ToUpper()}{value[1..].ToLower()}";
break;
}
}
}
return result;
}
internal static string? GetModel(ExifDirectoryBase[]? exifBaseDirectories)
{
string? result = null;
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.Model is null ? string.Empty : exifDirectoryBase.Model.ToString().Trim();
if (string.IsNullOrEmpty(value))
result = null;
else
{
result = value;
break;
}
}
}
return result;
}
internal static int? GetOrientation(ExifDirectoryBase[]? exifBaseDirectories)
{
int? result = null;
// public const int TagOrientation = 274;
if (exifBaseDirectories is not null)
{
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
result = exifDirectoryBase?.OrientationValue;
if (result is not null)
break;
}
}
return result;
}
internal static ReadOnlyCollection<string> GetKeywords(ExifDirectoryBase[]? exifBaseDirectories)
{
List<string> results = [];
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.WinKeywords is null ? string.Empty : exifDirectoryBase.WinKeywords.ToString().Trim();
if (string.IsNullOrEmpty(value))
continue;
results.Add(value);
}
}
return results.AsReadOnly();
}
internal static int? GetWidth(ExifDirectoryBase[]? exifBaseDirectories)
{
int? result = null;
// public const int TagImageWidth = 256;
if (exifBaseDirectories is not null)
{
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
result = exifDirectoryBase?.ImageWidthValue;
if (result is not null)
break;
}
}
return result;
}
internal static int? GetHeight(ExifDirectoryBase[]? exifBaseDirectories)
{
int? result = null;
// public const int TagImageHeight = 257;
if (exifBaseDirectories is not null)
{
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
result = exifDirectoryBase?.ImageHeightValue;
if (result is not null)
break;
}
}
return result;
}
#pragma warning disable CA1416
internal static DateTime? GetDateTime(string dateTimeFormat, string? value)
{
DateTime? result;
string alternateFormat = "ddd MMM dd HH:mm:ss yyyy";
if (value is not null && DateTime.TryParse(value, out DateTime dateTime))
result = dateTime;
else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else
result = null;
return result;
}
#pragma warning restore CA1416
}

View File

@ -4,7 +4,7 @@ using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models; namespace View_by_Distance.Shared.Models;
public record MetadataGroup(bool FastForwardMovingPictureExpertsGroupUsed, FilePath FilePath, FileInfo FileInfo, ExifDirectory ExifDirectory, ReadOnlyCollection<FileHolder> SidecarFiles) public record MetadataGroup(bool FastForwardMovingPictureExpertsGroupUsed, FilePath FilePath, MinimumYearAndPathCombined MinimumYearAndPathCombined, ExifDirectory ExifDirectory, ReadOnlyCollection<FileHolder> SidecarFiles)
{ {
public override string ToString() public override string ToString()

View File

@ -0,0 +1,22 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record MinimumYearAndPathCombined(int MinimumYear,
string PathCombined)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, MinimumYearAndPathCombinedSourceGenerationContext.Default.AviDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AviDirectory))]
public partial class MinimumYearAndPathCombinedSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,52 @@
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record NginxFileSystem([property: JsonPropertyName("name")] string Name,
DateTime? LastModified,
[property: JsonPropertyName("mtime")] string MTime,
Uri? URI,
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("size")] float? Length)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, NginxFileSystemSourceGenerationContext.Default.NginxFileSystem);
return result;
}
public static NginxFileSystem Get(string format, TimeZoneInfo timeZoneInfo, string name, string mTime, Uri uri, string type, float? size)
{
NginxFileSystem result;
DateTime dateTime;
DateTime? nullableDateTime;
if (mTime.Length != format.Length + 4 || !DateTime.TryParseExact(mTime[..format.Length], format, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
nullableDateTime = null;
else
nullableDateTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(dateTime, mTime[(format.Length + 1)..], timeZoneInfo.Id);
result = new(name, nullableDateTime, mTime, new($"{uri.OriginalString}/{name}"), type, size);
return result;
}
public static string GetFormat() =>
"ddd, dd MMM yyyy HH:mm:ss";
public static NginxFileSystem Get(string format, TimeZoneInfo timeZoneInfo, Uri uri, NginxFileSystem nginxFileSystem) =>
Get(format, timeZoneInfo, nginxFileSystem.Name, nginxFileSystem.MTime, uri, nginxFileSystem.Type, nginxFileSystem.Length);
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(NginxFileSystem))]
public partial class NginxFileSystemSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(NginxFileSystem[]))]
public partial class NginxFileSystemCollectionSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -6,9 +6,6 @@ public interface ICompareSettings
public string FacesFileNameExtension { init; get; } public string FacesFileNameExtension { init; get; }
public string FacesHiddenFileNameExtension { init; get; } public string FacesHiddenFileNameExtension { init; get; }
public string FacesPartsFileNameExtension { init; get; } public string FacesPartsFileNameExtension { init; get; }
public string[] IgnoreExtensions { init; get; }
public int MaxDegreeOfParallelism { init; get; } public int MaxDegreeOfParallelism { init; get; }
public string[] ValidImageFormatExtensions { init; get; }
public string[] ValidVideoFormatExtensions { init; get; }
} }

View File

@ -3,9 +3,6 @@ namespace View_by_Distance.Shared.Models.Properties;
public interface IRenameSettings public interface IRenameSettings
{ {
public string[] IgnoreExtensions { init; get; }
public bool SkipIdFiles { init; get; } public bool SkipIdFiles { init; get; }
public string[] ValidImageFormatExtensions { init; get; }
public string[] ValidVideoFormatExtensions { init; get; }
} }

View File

@ -0,0 +1,6 @@
namespace View_by_Distance.Shared.Models.Properties;
public interface IWindowsSettings
{
}

View File

@ -8,12 +8,15 @@ public record ResultSettings(string DateGroup,
string ModelName, string ModelName,
int NumberOfJitters, int NumberOfJitters,
int NumberOfTimesToUpsample, int NumberOfTimesToUpsample,
string[] IgnoreExtensions,
string PredictorModelName, string PredictorModelName,
int ResultAllInOneSubdirectoryLength, int ResultAllInOneSubdirectoryLength,
string ResultCollection, string ResultCollection,
string ResultContent, string ResultContent,
string ResultSingleton, string ResultSingleton,
string RootDirectory) string RootDirectory,
string[] ValidImageFormatExtensions,
string[] ValidVideoFormatExtensions)
{ {
public override string ToString() public override string ToString()

View File

@ -16,19 +16,19 @@ internal abstract class Age
years += 1; years += 1;
} }
result = new(minuendTicks - check.AddYears(-1).Ticks); result = new(minuendTicks - check.AddYears(-1).Ticks);
return (years, result); return new(years, result);
} }
internal static (int, TimeSpan) GetAge(long minuendTicks, DateTime subtrahend) internal static (int, TimeSpan) GetAge(long minuendTicks, DateTime subtrahend)
{ {
(int years, TimeSpan result) = GetAge(minuendTicks, subtrahend.Ticks); (int years, TimeSpan result) = GetAge(minuendTicks, subtrahend.Ticks);
return (years, result); return new(years, result);
} }
internal static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend) internal static (int, TimeSpan) GetAge(DateTime minuend, DateTime subtrahend)
{ {
(int years, TimeSpan result) = GetAge(minuend.Ticks, subtrahend.Ticks); (int years, TimeSpan result) = GetAge(minuend.Ticks, subtrahend.Ticks);
return (years, result); return new(years, result);
} }
internal static int? GetApproximateYears(char[] personCharacters, string personDisplayDirectoryName) internal static int? GetApproximateYears(char[] personCharacters, string personDisplayDirectoryName)

View File

@ -4,6 +4,8 @@ public interface ICompare
{ {
void Tick(); void Tick();
long Ticks { get; }
int? CurrentTick { get; }
void ConstructProgressBar(int maxTicks, string message); void ConstructProgressBar(int maxTicks, string message);
} }

View File

@ -3,24 +3,28 @@ namespace View_by_Distance.Shared.Models.Stateless;
public interface IDate public interface IDate
{ {
(bool?, string[]) TestStatic_IsWrongYear(FilePath filePath, ExifDirectory exifDirectory) => public static DateTime GetMinimum(ExifDirectory exifDirectory) =>
IsWrongYear(filePath, exifDirectory);
static (bool?, string[]) IsWrongYear(FilePath filePath, ExifDirectory exifDirectory) =>
XDate.IsWrongYear(filePath, exifDirectory);
(int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) =>
GetSeason(dayOfYear);
static (int Season, string seasonName) GetSeason(int dayOfYear) =>
XDate.GetSeason(dayOfYear);
DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
GetDateTimeOriginal(exifDirectory);
static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) =>
XDate.GetDateTimeOriginal(exifDirectory);
DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) =>
GetMinimum(exifDirectory);
static DateTime GetMinimum(ExifDirectory exifDirectory) =>
XDate.GetMinimum(exifDirectory); XDate.GetMinimum(exifDirectory);
public static (int Season, string seasonName) GetSeason(int dayOfYear) =>
XDate.GetSeason(dayOfYear);
public static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) =>
XDate.GetDateTimeOriginal(exifDirectory);
public static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
XDate.IsWrongYear(directoryInfo, filePath, exifDirectory);
internal DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) =>
GetMinimum(exifDirectory);
internal (int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) =>
GetSeason(dayOfYear);
internal DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
GetDateTimeOriginal(exifDirectory);
internal (bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
IsWrongYear(directoryInfo, filePath, exifDirectory);
} }

View File

@ -5,57 +5,69 @@ public interface IId
const int DeterministicHashCode = 9876543; const int DeterministicHashCode = 9876543;
static bool IsOffsetDeterministicHashCode(MetadataSettings metadataSettings) => public static int GetDeterministicHashCode(byte[] value) =>
metadataSettings.Offset == DeterministicHashCode;
string TestStatic_GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
Id.GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
int TestStatic_GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
GetId(resultSettings, metadataSettings, intelligentId);
static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
Id.GetId(resultSettings, metadataSettings, intelligentId);
string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
GetPaddedId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
Id.GetPaddedId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
string TestStatic_GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) =>
GetIgnoreFullPath(filePath, fileHolder);
static string GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) =>
fileHolder.DirectoryFullPath is null ?
throw new NotSupportedException() :
filePath.Id > -1 ?
fileHolder.NameWithoutExtension[^1] == '9' ?
Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}8{fileHolder.ExtensionLowered}") :
throw new NotSupportedException("High") :
fileHolder.NameWithoutExtension[^1] == '1' ?
Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}2{fileHolder.ExtensionLowered}") :
throw new NotSupportedException("Low");
bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
NameWithoutExtensionIsIntelligentIdFormat(metadataSettings, fileNameFirstSegment);
static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
fileNameFirstSegment.Length - 1 == metadataSettings.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataSettings, sortOrderOnlyLengthIndex, fileNameFirstSegment);
static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
fileNameFirstSegment.Length == metadataSettings.IntMinValueLength + sortOrderOnlyLengthIndex + 1
&& fileNameFirstSegment[^1] is '1' or '2' or '8' or '9'
&& fileNameFirstSegment.All(char.IsNumber);
bool TestStatic_NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder);
static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
Id.NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder.NameWithoutExtension.Split('.')[0]);
int TestStatic_GetDeterministicHashCode(byte[] value) =>
GetDeterministicHashCode(value);
static int GetDeterministicHashCode(byte[] value) =>
Id.GetDeterministicHashCode(value); Id.GetDeterministicHashCode(value);
public static byte GetHasIgnoreKeyword(FilePath filePath) =>
Id.GetHasIgnoreKeyword(filePath);
public static bool IsOffsetDeterministicHashCode(MetadataSettings metadataSettings) =>
metadataSettings.Offset == DeterministicHashCode;
public static byte GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
Id.GetHasDateTimeOriginal(resultSettings, filePath);
public static byte GetMissingDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
Id.GetMissingDateTimeOriginal(resultSettings, filePath);
public static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
Id.NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder.NameWithoutExtension.Split('.')[0]);
public static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
Id.GetId(resultSettings, metadataSettings, intelligentId);
public static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
fileNameFirstSegment.Length - 1 == metadataSettings.IntMinValueLength && fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
public static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
fileNameFirstSegment.Length == metadataSettings.IntMinValueLength + sortOrderOnlyLengthIndex + 1
&& fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9'
&& fileNameFirstSegment.All(char.IsNumber);
public static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
Id.GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
public static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
Id.GetPaddedId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
internal int TestStatic_GetDeterministicHashCode(byte[] value) =>
GetDeterministicHashCode(value);
internal byte TestStatic_GetHasIgnoreKeyword(FilePath filePath) =>
GetHasIgnoreKeyword(filePath);
internal byte TestStatic_GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
GetHasDateTimeOriginal(resultSettings, filePath);
internal byte TestStatic_GetMissingDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
GetMissingDateTimeOriginal(resultSettings, filePath);
internal bool TestStatic_NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder);
internal int TestStatic_GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
GetId(resultSettings, metadataSettings, intelligentId);
internal bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
NameWithoutExtensionIsIntelligentIdFormat(metadataSettings, fileNameFirstSegment);
internal bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataSettings, sortOrderOnlyLengthIndex, fileNameFirstSegment);
internal string TestStatic_GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, string extensionLowered, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
internal string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
GetPaddedId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
} }

View File

@ -5,11 +5,6 @@ namespace View_by_Distance.Shared.Models.Stateless;
public interface ILocation public interface ILocation
{ {
RectangleF? TestStatic_GetPercentagesRectangle(DistanceSettings distanceSettings, int wholePercentages) =>
GetPercentagesRectangle(distanceSettings, wholePercentages);
static RectangleF? GetPercentagesRectangle(DistanceSettings distanceSettings, int wholePercentages) =>
Location.GetPercentagesRectangle(distanceSettings, wholePercentages);
Models.Location TestStatic_GetTrimBound(double detectionConfidence, Rectangle rectangle, int width, int height, int facesCount) => Models.Location TestStatic_GetTrimBound(double detectionConfidence, Rectangle rectangle, int width, int height, int facesCount) =>
TrimBound(detectionConfidence, rectangle, width, height, facesCount); TrimBound(detectionConfidence, rectangle, width, height, facesCount);
static Models.Location TrimBound(double detectionConfidence, Rectangle rectangle, int width, int height, int facesCount) => static Models.Location TrimBound(double detectionConfidence, Rectangle rectangle, int width, int height, int facesCount) =>

View File

@ -0,0 +1,74 @@
using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless;
public interface IMetaBase
{
public static string DateTimeFormat() =>
"yyyy:MM:dd HH:mm:ss";
public static string? GetMaker(ExifDirectory? exifDirectory) =>
MetaBase.GetMaker(exifDirectory?.ExifBaseDirectories);
public static string? GetModel(ExifDirectory? exifDirectory) =>
MetaBase.GetModel(exifDirectory?.ExifBaseDirectories);
public static int? GetOrientation(ExifDirectory? exifDirectory) =>
MetaBase.GetOrientation(exifDirectory?.ExifBaseDirectories);
public static int? GetWidth(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetWidth(exifBaseDirectories);
public static int? GetHeight(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetHeight(exifBaseDirectories);
public static string? GetMaker(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetMaker(exifBaseDirectories);
public static string? GetModel(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetModel(exifBaseDirectories);
public static DateTime? GetDateTime(string dateTimeFormat, string? value) =>
MetaBase.GetDateTime(dateTimeFormat, value);
public static int? GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetOrientation(exifBaseDirectories);
public static ReadOnlyCollection<string> GetKeywords(ExifDirectory? exifDirectory) =>
MetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories);
public static ReadOnlyCollection<string> GetKeywords(ExifDirectoryBase[]? exifBaseDirectories) =>
MetaBase.GetKeywords(exifBaseDirectories);
internal string TestStatic_DateTimeFormat() =>
DateTimeFormat();
internal string? TestStatic_GetMaker(ExifDirectory? exifDirectory) =>
GetMaker(exifDirectory);
internal string? TestStatic_GetModel(ExifDirectory? exifDirectory) =>
GetModel(exifDirectory);
internal static int? TestStatic_GetWidth(ExifDirectoryBase[]? exifBaseDirectories) =>
GetWidth(exifBaseDirectories);
internal static int? TestStatic_GetHeight(ExifDirectoryBase[]? exifBaseDirectories) =>
GetHeight(exifBaseDirectories);
internal static string? TestStatic_GetMaker(ExifDirectoryBase[]? exifBaseDirectories) =>
GetMaker(exifBaseDirectories);
internal static string? TestStatic_GetModel(ExifDirectoryBase[]? exifBaseDirectories) =>
GetModel(exifBaseDirectories);
internal static DateTime? TestStatic_GetDateTime(string dateTimeFormat, string? value) =>
GetDateTime(dateTimeFormat, value);
internal static int? TestStatic_GetOrientation(ExifDirectoryBase[]? exifBaseDirectories) =>
GetOrientation(exifBaseDirectories);
internal static ReadOnlyCollection<string> TestStatic_GetKeywords(ExifDirectoryBase[]? exifBaseDirectories) =>
GetKeywords(exifBaseDirectories);
}

View File

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

View File

@ -1,13 +1,15 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Shared.Models.Stateless;
public interface IRename public interface IRename
{ {
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IRenameSettings renameSettings, FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(FilePath filePath);
void Tick(); void Tick();
long Ticks { get; }
int? CurrentTick { get; }
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(FilePath filePath);
void ConstructProgressBar(int maxTicks, string message);
} }

View File

@ -0,0 +1,16 @@
using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless;
public interface IWindows
{
void Tick();
long Ticks { get; }
int? CurrentTick { get; }
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, HttpClient? httpClient, FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(HttpClient httpClient, Uri uri);
void ConstructProgressBar(int maxTicks, string message);
}

View File

@ -1,86 +1,10 @@
using System.Text; using System.Text;
using View_by_Distance.Shared.Models.Stateless;
namespace View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Shared.Models.Stateless;
internal abstract class Id internal abstract class Id
{ {
internal static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment)
{
bool result;
if (fileNameFirstSegment.Length < 5 || fileNameFirstSegment.Length > metadataSettings.IntMinValueLength)
result = false;
else
{
bool skipOneAllAreNumbers = fileNameFirstSegment[1..].All(char.IsNumber);
result = (skipOneAllAreNumbers && fileNameFirstSegment[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameFirstSegment[0]));
}
return result;
}
internal static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId)
{
int result;
StringBuilder results = new();
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
for (int i = intelligentId.Length - (resultSettings.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
_ = results.Append(intelligentId[i]);
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
result = int.Parse(results.ToString());
if (intelligentId[^1] is '1' or '2')
result *= -1;
else if (intelligentId[^1] is not '9' and not '8')
throw new NotSupportedException();
return result;
}
#pragma warning disable IDE0060
internal static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
#pragma warning restore IDE0060
{
string result;
StringBuilder stringBuilder = new();
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
int key;
string value;
List<char> resultAllInOneSubdirectoryChars = [];
if (id > -1)
{
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : 9;
value = id.ToString().PadLeft(metadataSettings.IntMinValueLength, '0');
}
else
{
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : 1;
value = id.ToString()[1..].PadLeft(metadataSettings.IntMinValueLength, '0');
}
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
_ = stringBuilder.Append(value[i]);
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
resultAllInOneSubdirectoryChars.Add(value[i]);
result = $"{stringBuilder}{string.Join(string.Empty, resultAllInOneSubdirectoryChars)}{key}";
return result;
}
internal static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
{
string result;
if (metadataSettings.Offset < 0)
result = Guid.NewGuid().ToString();
else
{
string intelligentId = GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
int check = GetId(resultSettings, metadataSettings, intelligentId);
if (check != id)
throw new NotSupportedException();
result = index is null || metadataSettings.Offset == IId.DeterministicHashCode ? intelligentId : $"{metadataSettings.Offset + index}{intelligentId}";
}
return result;
}
internal static int GetDeterministicHashCode(byte[] value) internal static int GetDeterministicHashCode(byte[] value)
{ {
int result; int result;
@ -100,4 +24,106 @@ internal abstract class Id
return result; return result;
} }
internal static byte GetHasIgnoreKeyword(FilePath filePath) =>
(byte)(filePath.Id > -1 ? 8 : 2);
internal static byte GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
(byte)(IsIgnoreOrValidVideoFormatExtension(resultSettings, filePath) ? filePath.Id > -1 ? 6 : 4 : filePath.Id > -1 ? 9 : 1);
private static bool IsIgnoreOrValidVideoFormatExtension(ResultSettings resultSettings, FilePath filePath) =>
IsIgnoreOrValidVideoFormatExtension(resultSettings, filePath.ExtensionLowered);
private static bool IsIgnoreOrValidVideoFormatExtension(ResultSettings resultSettings, string extensionLowered) =>
resultSettings.IgnoreExtensions.Contains(extensionLowered)
|| resultSettings.ValidVideoFormatExtensions.Contains(extensionLowered);
internal static byte GetMissingDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
(byte)(IsIgnoreOrValidVideoFormatExtension(resultSettings, filePath) ? filePath.Id > -1 ? 5 : 0 : filePath.Id > -1 ? 7 : 3);
internal static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId)
{
int result;
StringBuilder results = new();
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
for (int i = intelligentId.Length - (resultSettings.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
_ = results.Append(intelligentId[i]);
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
result = int.Parse(results.ToString());
if (intelligentId[^1] is '0' or '1' or '2' or '3' or '4')
result *= -1;
else if (intelligentId[^1] is not '9' and not '8' and not '7' and not '6' and not '5')
throw new NotSupportedException();
return result;
}
internal static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment)
{
bool result;
if (fileNameFirstSegment.Length < 5 || fileNameFirstSegment.Length > metadataSettings.IntMinValueLength)
result = false;
else
{
bool skipOneAllAreNumbers = fileNameFirstSegment[1..].All(char.IsNumber);
result = (skipOneAllAreNumbers && fileNameFirstSegment[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameFirstSegment[0]));
}
return result;
}
#pragma warning disable IDE0060
internal static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
#pragma warning restore IDE0060
{
string result;
StringBuilder stringBuilder = new();
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
int key;
string value;
List<char> resultAllInOneSubdirectoryChars = [];
if (hasDateTimeOriginal is null)
{
key = 0;
value = id.ToString().PadLeft(metadataSettings.IntMinValueLength, '0');
}
else if (id > -1)
{
if (IsIgnoreOrValidVideoFormatExtension(resultSettings, extensionLowered))
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 6 : 5;
else
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal.Value ? 9 : 7;
value = id.ToString().PadLeft(metadataSettings.IntMinValueLength, '0');
}
else
{
if (IsIgnoreOrValidVideoFormatExtension(resultSettings, extensionLowered))
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 4 : 0;
else
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal.Value ? 1 : 3;
value = id.ToString()[1..].PadLeft(metadataSettings.IntMinValueLength, '0');
}
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
_ = stringBuilder.Append(value[i]);
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
resultAllInOneSubdirectoryChars.Add(value[i]);
result = $"{stringBuilder}{string.Join(string.Empty, resultAllInOneSubdirectoryChars)}{key}";
return result;
}
internal static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
{
string result;
if (metadataSettings.Offset < 0)
result = Guid.NewGuid().ToString();
else
{
string intelligentId = GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
int check = GetId(resultSettings, metadataSettings, intelligentId);
if (check != id)
throw new NotSupportedException();
result = index is null || metadataSettings.Offset == IId.DeterministicHashCode ? intelligentId : $"{metadataSettings.Offset + index}{intelligentId}";
}
return result;
}
} }

View File

@ -1,6 +1,4 @@
using System.Drawing; namespace View_by_Distance.Shared.Models.Stateless;
namespace View_by_Distance.Shared.Models.Stateless;
internal abstract class Location internal abstract class Location
{ {
@ -47,32 +45,4 @@ internal abstract class Location
return result; return result;
} }
internal static RectangleF? GetPercentagesRectangle(DistanceSettings distanceSettings, int wholePercentages)
{
RectangleF? result;
string wp = wholePercentages.ToString();
int length = (distanceSettings.LocationDigits - 1) / 4;
string[] segments =
[
wp[..1],
wp.Substring(1, length),
wp.Substring(3, length),
wp.Substring(5, length),
wp.Substring(7, length)
];
if (string.Join(string.Empty, segments) != wp)
result = null;
else
{
if (!int.TryParse(segments[1], out int xWholePercent) || !int.TryParse(segments[2], out int yWholePercent) || !int.TryParse(segments[3], out int wWholePercent) || !int.TryParse(segments[4], out int hWholePercent))
result = null;
else
{
float factor = 100;
result = new(xWholePercent / factor, yWholePercent / factor, wWholePercent / factor, hWholePercent / factor);
}
}
return result;
}
} }

View File

@ -48,7 +48,7 @@ internal abstract class PersonBirthday
if (birthday?.Value is null) if (birthday?.Value is null)
throw new NullReferenceException(nameof(birthday.Value)); throw new NullReferenceException(nameof(birthday.Value));
(years, result) = Age.GetAge(dateTimeTicks, birthday.Value); (years, result) = Age.GetAge(dateTimeTicks, birthday.Value);
return (years, result); return new(years, result);
} }
internal static (int, TimeSpan) GetAge(DateTime dateTime, Models.PersonBirthday birthday) internal static (int, TimeSpan) GetAge(DateTime dateTime, Models.PersonBirthday birthday)
@ -58,7 +58,7 @@ internal abstract class PersonBirthday
if (birthday?.Value is null) if (birthday?.Value is null)
throw new NullReferenceException(nameof(birthday.Value)); throw new NullReferenceException(nameof(birthday.Value));
(years, result) = Age.GetAge(dateTime, birthday.Value); (years, result) = Age.GetAge(dateTime, birthday.Value);
return (years, result); return new(years, result);
} }
internal static (int, double) GetAge(DateTime dateTime, DateTime dayBeforeLeapDate, Models.PersonBirthday birthday) internal static (int, double) GetAge(DateTime dateTime, DateTime dayBeforeLeapDate, Models.PersonBirthday birthday)
@ -69,7 +69,7 @@ internal abstract class PersonBirthday
result = timeSpan.TotalDays / 365; result = timeSpan.TotalDays / 365;
else else
result = timeSpan.TotalDays / 366; result = timeSpan.TotalDays / 366;
return (years, result); return new(years, result);
} }
internal static double? GetAge(Models.PersonBirthday birthday) internal static double? GetAge(Models.PersonBirthday birthday)

View File

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

View File

@ -5,35 +5,29 @@ namespace View_by_Distance.Shared.Models.Stateless;
internal abstract class XPath internal abstract class XPath
{ {
internal static string GetRelativePath(string path, int length, bool forceExtensionToLower) private static ReadOnlyDictionary<byte, ReadOnlyCollection<string>> Convert(List<CombinedEnumAndIndex> collection)
{ {
string result; Dictionary<byte, List<string>> results = [];
if (forceExtensionToLower) List<string>? c;
foreach (CombinedEnumAndIndex cei in collection)
{ {
string extension = Path.GetExtension(path); if (!results.TryGetValue(cei.Enum, out c))
string extensionLowered = Path.GetExtension(path).ToLower();
if (extension != extensionLowered)
{ {
string? directoryName = Path.GetDirectoryName(path); results.Add(cei.Enum, []);
if (string.IsNullOrEmpty(directoryName)) if (!results.TryGetValue(cei.Enum, out c))
throw new NullReferenceException(directoryName); throw new Exception();
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
if (string.IsNullOrEmpty(fileNameWithoutExtension))
throw new NullReferenceException(fileNameWithoutExtension);
path = Path.Combine(directoryName, $"{fileNameWithoutExtension}{extensionLowered}");
} }
c.Add(cei.Combined);
} }
result = path[length..].Replace(@"\", "/"); return Convert(results);
return result;
} }
internal static bool DeleteEmptyDirectories(string rootDirectory) private static ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> Convert(Dictionary<int, Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePairs)
{ {
bool result; Dictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> results = [];
List<string> results = []; foreach (KeyValuePair<int, Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePair in keyValuePairs)
DeleteEmptyDirectories(rootDirectory, results); results.Add(keyValuePair.Key, new(keyValuePair.Value));
result = results.Count > 0; return results.AsReadOnly();
return result;
} }
internal static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) internal static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories)
@ -71,45 +65,56 @@ internal abstract class XPath
} }
} }
internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches) internal static byte GetEnum(FilePath filePath) =>
GetEnum(filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal);
private static byte GetEnum(bool? ik, bool? dto)
{ {
bool result; byte result;
string text; if (ik is not null && ik.Value && dto is not null && dto.Value)
if (!compareBeforeWrite) result = 11;
result = true; else if (ik is not null && ik.Value && dto is not null && !dto.Value)
result = 15;
else if (ik is not null && ik.Value && dto is null)
result = 19;
else if (ik is not null && !ik.Value && dto is not null && dto.Value)
result = 51;
else if (ik is not null && !ik.Value && dto is not null && !dto.Value)
result = 55;
else if (ik is not null && !ik.Value && dto is null)
result = 59;
else if (ik is null && dto is not null && dto.Value)
result = 91;
else if (ik is null && dto is not null && !dto.Value)
result = 95;
else if (ik is null && dto is null)
result = 99;
else else
{ throw new Exception();
if (!File.Exists(path))
text = string.Empty;
else
text = File.ReadAllText(path);
result = text != contents;
if (!result && updateDateWhenMatches)
{
if (updateToWhenMatches is null)
File.SetLastWriteTime(path, DateTime.Now);
else
File.SetLastWriteTime(path, updateToWhenMatches.Value);
}
}
if (result)
{
if (path.Contains("()"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("[]") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{')
File.WriteAllText(path, contents);
else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[')
File.WriteAllText(path, contents);
else
File.WriteAllText(path, contents);
}
return result; return result;
} }
internal static List<string> GetDirectories(string directory)
{
List<string> results = [];
string? checkDirectory = directory;
string? pathRoot = Path.GetPathRoot(directory);
if (string.IsNullOrEmpty(pathRoot))
throw new NullReferenceException(nameof(pathRoot));
if (Directory.Exists(directory))
results.Add(directory);
for (int i = 0; i < int.MaxValue; i++)
{
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
break;
results.Add(checkDirectory);
}
results.Add(pathRoot);
results.Reverse();
return results;
}
internal static List<string> GetDirectoryNames(string directory) internal static List<string> GetDirectoryNames(string directory)
{ {
List<string> results = []; List<string> results = [];
@ -146,83 +151,15 @@ internal abstract class XPath
return results; return results;
} }
internal static List<string> GetDirectories(string directory) internal static bool DeleteEmptyDirectories(string rootDirectory)
{ {
bool result;
List<string> results = []; List<string> results = [];
string? checkDirectory = directory; DeleteEmptyDirectories(rootDirectory, results);
string? pathRoot = Path.GetPathRoot(directory); result = results.Count > 0;
if (string.IsNullOrEmpty(pathRoot))
throw new NullReferenceException(nameof(pathRoot));
if (Directory.Exists(directory))
results.Add(directory);
for (int i = 0; i < int.MaxValue; i++)
{
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
break;
results.Add(checkDirectory);
}
results.Add(pathRoot);
results.Reverse();
return results;
}
internal static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory)
{
int result = 0;
string? directory;
string? checkDirectory;
List<string> results = [];
checkDirectory = sourceDirectory;
for (int i = 0; i < int.MaxValue; i++)
{
result += 1;
directory = Path.GetFileName(checkDirectory);
if (string.IsNullOrEmpty(directory))
break;
results.Add(directory);
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (checkDirectory == rootDirectory)
break;
}
results.Reverse();
return new(result, results);
}
internal static string GetDirectory(string sourceDirectory, int level, string directoryName)
{
string result;
string? checkDirectory;
checkDirectory = Path.GetDirectoryName(sourceDirectory);
for (int i = 0; i < level; i++)
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory))
throw new Exception();
checkDirectory = Path.Combine(checkDirectory, directoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
result = checkDirectory;
return result; return result;
} }
internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks)
{
DateTime dateTime = new(ticks);
IEnumerable<string> fileSystemEntries;
string[] directories;
if (!Directory.Exists(rootDirectory))
directories = [];
else
directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.AllDirectories);
foreach (string directory in directories)
{
fileSystemEntries = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.TopDirectoryOnly);
if (fileSystemEntries.Any())
continue;
Directory.SetLastWriteTime(directory, dateTime);
}
}
internal static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) internal static void MakeHiddenIfAllItemsAreHidden(string rootDirectory)
{ {
bool check; bool check;
@ -265,46 +202,204 @@ internal abstract class XPath
} }
} }
private static (string, int) GetDirectoryNameAndIndex(int resultAllInOneSubdirectoryLength, string fileNameWithoutExtension) internal static void CreateDirectories(ReadOnlyCollection<string> directories)
{
string checkDirectory;
foreach (string directory in directories)
{
for (int i = 0; i < 101; i++)
{
checkDirectory = Path.Combine(directory, i.ToString("000"));
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
}
}
}
internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks)
{
DateTime dateTime = new(ticks);
IEnumerable<string> fileSystemEntries;
string[] directories;
if (!Directory.Exists(rootDirectory))
directories = [];
else
directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.AllDirectories);
foreach (string directory in directories)
{
fileSystemEntries = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.TopDirectoryOnly);
if (fileSystemEntries.Any())
continue;
Directory.SetLastWriteTime(directory, dateTime);
}
}
internal static string GetRelativePath(string path, int length, bool forceExtensionToLower)
{ {
int converted;
string result; string result;
if (forceExtensionToLower)
{
string extension = Path.GetExtension(path);
string extensionLowered = Path.GetExtension(path).ToLower();
if (extension != extensionLowered)
{
string? directoryName = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(directoryName))
throw new NullReferenceException(directoryName);
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
if (string.IsNullOrEmpty(fileNameWithoutExtension))
throw new NullReferenceException(fileNameWithoutExtension);
path = Path.Combine(directoryName, $"{fileNameWithoutExtension}{extensionLowered}");
}
}
result = path[length..].Replace(@"\", "/");
return result;
}
internal static string GetDirectory(string sourceDirectory, int level, string directoryName)
{
string result;
string? checkDirectory;
checkDirectory = Path.GetDirectoryName(sourceDirectory);
for (int i = 0; i < level; i++)
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory))
throw new Exception();
checkDirectory = Path.Combine(checkDirectory, directoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
result = checkDirectory;
return result;
}
internal static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory)
{
int result = 0;
string? directory;
string? checkDirectory;
List<string> results = [];
checkDirectory = sourceDirectory;
for (int i = 0; i < int.MaxValue; i++)
{
result += 1;
directory = Path.GetFileName(checkDirectory);
if (string.IsNullOrEmpty(directory))
break;
results.Add(directory);
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (checkDirectory == rootDirectory)
break;
}
results.Reverse();
return new(result, results);
}
internal static CombinedEnumAndIndex GetCombinedEnumAndIndex(ResultSettings resultSettings, FilePath filePath)
{
CombinedEnumAndIndex result;
if (filePath.Id is not null)
result = GetCombinedEnumAndIndex(resultSettings, filePath, filePath.Id.Value.ToString());
else
result = GetCombinedEnumAndIndex(resultSettings, filePath, filePath.NameWithoutExtension);
return result;
}
private static CombinedEnumAndIndex GetCombinedEnumAndIndex(ResultSettings resultSettings, FilePath filePath, string fileNameWithoutExtension)
{
CombinedEnumAndIndex result;
byte @enum;
int converted;
string combined;
byte missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(resultSettings, filePath);
if (!filePath.IsIntelligentIdFormat)
@enum = missingDateTimeOriginal;
else
{
if (filePath.HasIgnoreKeyword is null || filePath.HasDateTimeOriginal is null)
throw new NotImplementedException("Chicken and Egg!");
if (filePath.HasIgnoreKeyword.Value)
@enum = IId.GetHasIgnoreKeyword(filePath);
else if (!filePath.HasDateTimeOriginal.Value)
@enum = missingDateTimeOriginal;
else
@enum = IId.GetHasDateTimeOriginal(resultSettings, filePath);
}
string fileNameBeforeFirst = fileNameWithoutExtension.Split('.')[0]; string fileNameBeforeFirst = fileNameWithoutExtension.Split('.')[0];
string check = fileNameBeforeFirst.Length < resultAllInOneSubdirectoryLength ? new('-', resultAllInOneSubdirectoryLength) : fileNameBeforeFirst[^resultAllInOneSubdirectoryLength..]; string check = fileNameBeforeFirst.Length < resultSettings.ResultAllInOneSubdirectoryLength ?
new('-', resultSettings.ResultAllInOneSubdirectoryLength) :
fileNameBeforeFirst[^resultSettings.ResultAllInOneSubdirectoryLength..];
if (check.Any(l => !char.IsNumber(l))) if (check.Any(l => !char.IsNumber(l)))
{ {
result = new('-', resultAllInOneSubdirectoryLength); combined = $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}";
converted = int.Parse($"1{new string('0', resultAllInOneSubdirectoryLength)}"); converted = int.Parse($"1{new string('0', resultSettings.ResultAllInOneSubdirectoryLength)}");
} }
else else
{ {
result = check; combined = $"{@enum}{check}";
converted = int.Parse(check); converted = int.Parse(check);
} }
return (result, converted); result = new(combined, @enum, converted);
return result;
} }
internal static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, int id) internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches)
{ {
(string result, int converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, id.ToString()); bool result;
return (result, converted); string text;
} if (!compareBeforeWrite)
result = true;
internal static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, FileHolder fileHolder)
{
(string result, int converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, fileHolder.NameWithoutExtension);
return (result, converted);
}
internal static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, FilePath filePath)
{
string result;
int converted;
if (filePath.Id is not null)
(result, converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, filePath.Id.Value.ToString());
else else
(result, converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, filePath.NameWithoutExtension); {
return (result, converted); if (!File.Exists(path))
text = string.Empty;
else
text = File.ReadAllText(path);
result = text != contents;
if (!result && updateDateWhenMatches)
{
if (updateToWhenMatches is null)
File.SetLastWriteTime(path, DateTime.Now);
else
File.SetLastWriteTime(path, updateToWhenMatches.Value);
}
}
if (result)
{
if (path.Contains("()"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("[]") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{')
File.WriteAllText(path, contents);
else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[')
File.WriteAllText(path, contents);
else
File.WriteAllText(path, contents);
}
return result;
}
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups)
{
Dictionary<int, Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> results = [];
if (jsonGroups is not null)
{
DateTime dateTime = DateTime.Now;
ReadOnlyCollection<int> years = GetYears(resultSettings);
Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>? k;
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, jsonGroups, dateTime);
foreach (int year in years)
{
results.Add(year, []);
if (!results.TryGetValue(year, out k))
throw new NullReferenceException(nameof(k));
foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
k.Add(keyValuePair.Key, keyValuePair.Value);
}
}
return Convert(results);
} }
private static ReadOnlyCollection<int> GetYears(ResultSettings resultSettings) private static ReadOnlyCollection<int> GetYears(ResultSettings resultSettings)
@ -316,65 +411,79 @@ internal abstract class XPath
return results.AsReadOnly(); return results.AsReadOnly();
} }
private static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> Convert(Dictionary<int, Dictionary<string, string[]>> collection) private static ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups, DateTime dateTime)
{ {
Dictionary<int, ReadOnlyDictionary<string, string[]>> results = []; Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> results = [];
foreach (KeyValuePair<int, Dictionary<string, string[]>> keyValuePair in collection) int plusOne;
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return results.AsReadOnly();
}
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups)
{
Dictionary<int, Dictionary<string, string[]>> results = [];
string directory; string directory;
string checkDirectory; string checkDirectory;
Dictionary<string, string[]>? keyValuePairs; CombinedEnumAndIndex cei;
ReadOnlyCollection<int> years = GetYears(resultSettings); byte[] bytes = GetBytes();
List<CombinedEnumAndIndex> collection = [];
ReadOnlyDictionary<byte, ReadOnlyCollection<string>> keyValuePairs;
int converted = int.Parse($"1{new string('0', resultSettings.ResultAllInOneSubdirectoryLength)}"); int converted = int.Parse($"1{new string('0', resultSettings.ResultAllInOneSubdirectoryLength)}");
int plusOne = converted + 1;
List<string> collection = [];
foreach (int year in years)
{
results.Add(year, []);
if (!results.TryGetValue(year, out keyValuePairs))
throw new NullReferenceException(nameof(keyValuePairs));
if (jsonGroups is not null) if (jsonGroups is not null)
{ {
plusOne = converted + 1;
foreach (string jsonGroup in jsonGroups) foreach (string jsonGroup in jsonGroups)
{ {
if (resultsFullGroupDirectory is null) if (resultsFullGroupDirectory is null)
continue; continue;
collection.Clear(); foreach (byte @enum in bytes)
{
for (int i = 0; i < plusOne; i++) for (int i = 0; i < plusOne; i++)
{ {
if (string.IsNullOrEmpty(jsonGroup)) if (string.IsNullOrEmpty(jsonGroup))
{ {
if (i == converted) if (i == converted)
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, new('-', resultSettings.ResultAllInOneSubdirectoryLength))); checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}"));
else else
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0'))); checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, $"{@enum}{i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')}"));
} }
else else
{ {
directory = Path.Combine(resultsFullGroupDirectory, jsonGroup); directory = Path.Combine(resultsFullGroupDirectory, jsonGroup);
if (i == converted) if (i == converted)
checkDirectory = Path.GetFullPath(Path.Combine(directory, new('-', resultSettings.ResultAllInOneSubdirectoryLength))); checkDirectory = Path.GetFullPath(Path.Combine(directory, $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}"));
else else
checkDirectory = Path.GetFullPath(Path.Combine(directory, i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0'))); checkDirectory = Path.GetFullPath(Path.Combine(directory, $"{@enum}{i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')}"));
} }
if (!Directory.Exists(checkDirectory)) if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory); _ = Directory.CreateDirectory(checkDirectory);
collection.Add(checkDirectory); cei = new(Combined: checkDirectory, Enum: @enum, Index: -1);
collection.Add(cei);
} }
}
keyValuePairs = Convert(collection);
if (!string.IsNullOrEmpty(jsonGroup)) if (!string.IsNullOrEmpty(jsonGroup))
keyValuePairs.Add(jsonGroup, collection.ToArray()); results.Add(jsonGroup, keyValuePairs);
else else
keyValuePairs.Add(year.ToString(), collection.ToArray()); results.Add(dateTime.Ticks.ToString(), keyValuePairs);
} }
} }
return results.AsReadOnly();
} }
return Convert(results);
private static byte[] GetBytes() =>
[
0,
1,
2,
3,
4,
5,
6,
7,
8,
9
];
private static ReadOnlyDictionary<byte, ReadOnlyCollection<string>> Convert(Dictionary<byte, List<string>> keyValuePairs)
{
Dictionary<byte, ReadOnlyCollection<string>> results = [];
foreach (KeyValuePair<byte, List<string>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return results.AsReadOnly();
} }
} }

5
Windows/.vscode/11.http vendored Normal file
View File

@ -0,0 +1,5 @@
@host = http://192.168.0.11:8080
GET {{host}}/iCloud%20Photos%202025
###

5
Windows/.vscode/mklink.md vendored Normal file
View File

@ -0,0 +1,5 @@
# mklink
```bash 1741014465915 = 638766112659150000 = 2025-0.Winter = Mon Mar 03 2025 08:07:45 GMT-0700 (Mountain Standard Time)
mklink /J "L:\Git\AA\Windows\.vscode\.7-Question" "V:\7-Question"
````

1
Windows/.vscode/read-me.md vendored Normal file
View File

@ -0,0 +1 @@
# Read Me

60
Windows/AA.Windows.csproj Normal file
View File

@ -0,0 +1,60 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>076c87e8-c7f0-40a3-aba3-73eb7f9ea892</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Windows</PackageId>
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>
<PackageReadmeFile>read-me.md</PackageReadmeFile>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</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>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.8.2" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.14" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.14" />
</ItemGroup>
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Metadata\AA.Metadata.csproj" />
<ProjectReference Include="..\Shared\AA.Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,57 @@
using Microsoft.Extensions.Configuration;
using System.Text.Json;
using System.Text.Json.Serialization;
using View_by_Distance.Shared.Models;
namespace View_by_Distance.Windows.Models;
public record AppSettings(ResultSettings ResultSettings,
MetadataSettings MetadataSettings,
WindowsSettings WindowsSettings)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
return result;
}
private static void Verify(AppSettings appSettings)
{
if (appSettings.WindowsSettings.MaxDegreeOfParallelism > Environment.ProcessorCount)
throw new Exception("MaxDegreeOfParallelism must be =< Environment.ProcessorCount!");
}
public static AppSettings Get(IConfigurationRoot configurationRoot)
{
AppSettings result;
#pragma warning disable IL3050, IL2026
ResultSettings? resultSettings = configurationRoot.GetSection(nameof(ResultSettings)).Get<ResultSettings>();
MetadataSettings? metadataSettings = configurationRoot.GetSection(nameof(MetadataSettings)).Get<MetadataSettings>();
WindowsSettings? WindowsSettings = configurationRoot.GetSection(nameof(WindowsSettings)).Get<WindowsSettings>();
#pragma warning restore IL3050, IL2026
if (resultSettings is null || metadataSettings is null || WindowsSettings?.Company is null)
{
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
{
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
continue;
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
continue;
paths.Add(physicalFileProvider.Root);
}
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
}
result = new(resultSettings, metadataSettings, WindowsSettings);
Verify(result);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,32 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Windows.Models;
internal sealed record Identifier(string[] DirectoryNames,
bool? HasDateTimeOriginal,
int Id,
long Length,
string PaddedId,
long Ticks)
{
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

@ -0,0 +1,27 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Windows.Models;
public record WindowsSettings(string Company,
string? Host,
int MaxDegreeOfParallelism,
string? Page,
string[] SidecarExtensions,
bool VerifyOnly) : Shared.Models.Properties.IWindowsSettings
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, WindowsSettingsSourceGenerationContext.Default.WindowsSettings);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WindowsSettings))]
internal partial class WindowsSettingsSourceGenerationContext : JsonSerializerContext
{
}

53
Windows/Program.cs Normal file
View File

@ -0,0 +1,53 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using View_by_Distance.Windows.Models;
namespace View_by_Distance.Windows;
public class Program
{
public static void Secondary(ILogger<Program> logger, List<string> args)
{
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets<Program>();
IConfigurationRoot configurationRoot = configurationBuilder.Build();
AppSettings appSettings = AppSettings.Get(configurationRoot);
int silentIndex = args.IndexOf("s");
if (silentIndex > -1)
args.RemoveAt(silentIndex);
try
{
if (args is null)
throw new Exception("args is null!");
Shared.Models.Console console = new();
_ = new Windows(args, logger, appSettings, silentIndex > -1, console);
}
catch (Exception ex)
{
logger?.LogError(ex, "Error!");
}
if (silentIndex > -1)
logger?.LogInformation("Done. Bye");
else
{
logger?.LogInformation("Done. Press 'Enter' to end");
_ = Console.ReadLine();
}
}
public static void Main(string[] args)
{
#pragma warning disable IL3050
ILogger<Program>? logger = Host.CreateDefaultBuilder(args).Build().Services.GetRequiredService<ILogger<Program>>();
#pragma warning restore IL3050
if (args is not null)
Secondary(logger, args.ToList());
else
Secondary(logger, []);
}
}

413
Windows/Windows.cs Normal file
View File

@ -0,0 +1,413 @@
using CliWrap;
using Microsoft.Extensions.Logging;
using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Text.Json;
using View_by_Distance.Metadata.Models;
using View_by_Distance.Metadata.Models.Stateless;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Stateless;
using View_by_Distance.Windows.Models;
namespace View_by_Distance.Windows;
public partial class Windows : IWindows, IDisposable
{
public long Ticks { get; init; }
public int? CurrentTick => _ProgressBar?.CurrentTick;
private ProgressBar? _ProgressBar;
private readonly ProgressBarOptions _ProgressBarOptions;
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient httpClient, Uri uri) =>
GetDeterministicHashCode(httpClient, uri);
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath)
{
DeterministicHashCode result;
if (httpClient is not null)
result = GetDeterministicHashCode(httpClient, new Uri(filePath.FullName));
else
{
Stream stream = File.OpenRead(filePath.FullName);
result = GetDeterministicHashCode(stream);
stream.Dispose();
}
return result;
}
private static DeterministicHashCode GetDeterministicHashCode(HttpClient httpClient, Uri uri)
{
DeterministicHashCode result;
Stream stream = GetStream(httpClient, uri);
result = GetDeterministicHashCode(stream);
stream.Dispose();
return result;
}
private static Stream GetStream(HttpClient httpClient, Uri uri)
{
Stream result;
Task<Stream> task = httpClient.GetStreamAsync(uri);
task.Wait();
result = task.Result;
return result;
}
private static DeterministicHashCode GetDeterministicHashCode(Stream stream)
{
DeterministicHashCode result;
int? id;
int? width;
int? height;
try
{
#pragma warning disable CA1416
using Image image = Image.FromStream(stream);
width = image.Width;
height = image.Height;
using Bitmap bitmap = new(image);
Rectangle rectangle = new(0, 0, image.Width, image.Height);
BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
IntPtr intPtr = bitmapData.Scan0;
int length = bitmapData.Stride * bitmap.Height;
byte[] bytes = new byte[length];
Marshal.Copy(intPtr, bytes, 0, length);
bitmap.UnlockBits(bitmapData);
#pragma warning restore CA1416
id = IId.GetDeterministicHashCode(bytes);
}
catch (Exception)
{
id = null;
width = null;
height = null;
}
result = new(height, id, width);
return result;
}
void IWindows.Tick() =>
_ProgressBar?.Tick();
void IDisposable.Dispose()
{
_ProgressBar?.Dispose();
GC.SuppressFinalize(this);
}
void IWindows.ConstructProgressBar(int maxTicks, string message)
{
_ProgressBar?.Dispose();
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
}
ReadOnlyCollection<string> IWindows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, HttpClient? httpClient, FilePath filePath)
{
List<string> results = [];
bool isValidVideoFormatExtensions = resultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
if (isValidVideoFormatExtensions)
{
bool check;
if (httpClient is not null)
DownloadFile(httpClient, filePath);
try
{
CommandTask<CommandResult> commandTask = Cli.Wrap("L:/Git/ffmpeg-2024-10-02-git-358fdf3083-full_build/bin/ffmpeg.exe")
.WithArguments(["-i", filePath.FullName, "-vf", "select=eq(n\\,0)", "-q:v", "1", $"{filePath.Name}-%4d.jpg"])
.WithWorkingDirectory(filePath.DirectoryFullPath)
.ExecuteAsync();
commandTask.Task.Wait();
check = true;
}
catch (Exception)
{
check = false;
}
if (check)
{
results.AddRange(Directory.GetFiles(filePath.DirectoryFullPath, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly));
if (results.Count == 0)
throw new Exception();
File.SetCreationTime(results[0], new(filePath.CreationTicks));
File.SetLastWriteTime(results[0], new(filePath.LastWriteTicks));
Thread.Sleep(100);
}
}
return results.AsReadOnly();
}
private static void DownloadFile(HttpClient httpClient, FilePath filePath)
{
FileStream fileStream = new(filePath.FullName, FileMode.Truncate);
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(filePath.FullName);
httpResponseMessage.Wait();
Task task = httpResponseMessage.Result.Content.CopyToAsync(fileStream);
task.Wait();
}
public Windows(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
{
if (isSilent)
{ }
if (args is null)
throw new NullReferenceException(nameof(args));
if (console is null)
throw new NullReferenceException(nameof(console));
IWindows windows = this;
LogNetToHoursSince(logger);
Ticks = DateTime.Now.Ticks;
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
WindowsWork(logger, appSettings, windows);
}
private static void LogNetToHoursSince(ILogger<Program>? logger)
{
double secondsInAHour = 3600f;
long epoch = new DateTime(1970, 1, 1).Ticks;
long net8ReleaseDate = new DateTime(2023, 11, 14).Ticks;
long net9ReleaseDate = new DateTime(2024, 11, 12).Ticks;
double net8TotalSeconds = new TimeSpan(net8ReleaseDate - epoch).TotalSeconds;
double net9TotalSeconds = new TimeSpan(net9ReleaseDate - epoch).TotalSeconds;
logger?.LogInformation("It has been {net8TotalSeconds} seconds since net8 was released", net8TotalSeconds);
logger?.LogInformation("It has been {net9TotalSeconds} seconds since net9 was released", net9TotalSeconds);
double net8TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net8TotalSeconds) / secondsInAHour);
double net9TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net9TotalSeconds) / secondsInAHour);
logger?.LogInformation("It has been {net8TotalHours} hours since net8 was released", net8TotalHours);
logger?.LogInformation("It has been {net9TotalHours} hours since net9 was released", net9TotalHours);
}
private void WindowsWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows)
{
if (appSettings.WindowsSettings.VerifyOnly && !string.IsNullOrEmpty(appSettings.WindowsSettings.Host) && !string.IsNullOrEmpty(appSettings.WindowsSettings.Page))
Verify(logger, appSettings, windows, appSettings.WindowsSettings.Host, appSettings.WindowsSettings.Page);
else
{
string sourceDirectory = Path.GetFullPath(appSettings.ResultSettings.RootDirectory);
if (!Directory.Exists(sourceDirectory))
_ = Directory.CreateDirectory(sourceDirectory);
logger?.LogInformation("{Ticks} {RootDirectory}", windows.Ticks, sourceDirectory);
WindowsWork(logger, appSettings, windows, sourceDirectory);
}
}
private static void Verify(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, string host, string page)
{
List<string> messages = [];
HttpClient httpClient = new();
int appSettingsMaxDegreeOfParallelism = appSettings.WindowsSettings.MaxDegreeOfParallelism;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
ReadOnlyCollection<NginxFileSystem> collection = GetRecursiveCollection(httpClient, host, page) ?? throw new Exception();
windows.ConstructProgressBar(collection.Count, nameof(Verify));
_ = Parallel.For(0, collection.Count, parallelOptions, (i, state) =>
VerifyParallelFor(appSettings, windows, httpClient, collection[i], messages));
httpClient.Dispose();
foreach (string message in messages)
logger?.LogWarning("{message}", message);
}
private static ReadOnlyCollection<NginxFileSystem>? GetRecursiveCollection(HttpClient httpClient, string host, string page)
{
List<NginxFileSystem>? results;
Uri uri = new($"http://{host}/{page}");
string format = NginxFileSystem.GetFormat();
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
Task<HttpResponseMessage> taskHttpResponseMessage = httpClient.GetAsync(uri);
taskHttpResponseMessage.Wait();
if (!taskHttpResponseMessage.Result.IsSuccessStatusCode)
results = null;
else
{
Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
taskString.Wait();
NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(taskString.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
if (nginxFileSystems is null)
results = null;
else
{
results = [];
NginxFileSystem nginxFileSystem;
ReadOnlyCollection<NginxFileSystem>? directory;
for (int i = 0; i < nginxFileSystems.Length; i++)
{
nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
if (nginxFileSystem.Type == "file")
results.Add(nginxFileSystem);
else
{
directory = GetRecursiveCollection(httpClient, host, $"{page}/{nginxFileSystem.Name}");
if (directory is null)
continue;
results.AddRange(directory);
}
}
}
}
return results?.AsReadOnly();
}
private static void VerifyParallelFor(AppSettings appSettings, IWindows windows, HttpClient httpClient, NginxFileSystem nginxFileSystem, List<string> messages)
{
windows.Tick();
if (nginxFileSystem.URI is null)
return;
if (!nginxFileSystem.Name.EndsWith(".jpg"))
return;
DeterministicHashCode deterministicHashCode = windows.GetDeterministicHashCode(httpClient, nginxFileSystem.URI);
if (deterministicHashCode.Id is null)
{
messages.Add($"{nginxFileSystem.URI.OriginalString}");
return;
}
string paddedId = IId.GetPaddedId(resultSettings: appSettings.ResultSettings,
metadataSettings: appSettings.MetadataSettings,
id: deterministicHashCode.Id.Value,
extensionLowered: appSettings.ResultSettings.ValidImageFormatExtensions[0],
hasIgnoreKeyword: null,
hasDateTimeOriginal: null,
index: null);
if (!nginxFileSystem.Name.StartsWith(paddedId))
messages.Add($"!{nginxFileSystem.Name}.StartsWith({paddedId})");
}
private void WindowsWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, string sourceDirectory)
{
ReadOnlyCollection<FirstPass> results;
ReadOnlyCollection<string> files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories).AsReadOnly();
if (files.Count > 0)
_ = IPath.DeleteEmptyDirectories(sourceDirectory);
A_Metadata metadata = new(appSettings.ResultSettings, appSettings.MetadataSettings);
int appSettingsMaxDegreeOfParallelism = appSettings.WindowsSettings.MaxDegreeOfParallelism;
int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count : 123000;
windows.ConstructProgressBar(filesCount, "EnumerateFiles load");
if (appSettingsMaxDegreeOfParallelism == 1)
results = WindowsSynchronousWork(logger, appSettings, windows, files, metadata);
else
results = WindowsAsynchronousWork(appSettings, windows, files, metadata, appSettingsMaxDegreeOfParallelism);
string json = JsonSerializer.Serialize(results, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass);
File.WriteAllText(Path.Combine(sourceDirectory, $"{windows.Ticks}.json"), json);
}
private static ReadOnlyCollection<FirstPass> WindowsSynchronousWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, IEnumerable<string> files, A_Metadata metadata)
{
List<FirstPass> results = [];
int index = -1;
ReadOnlyDictionary<string, List<FileHolder>> keyValuePairs;
if (string.IsNullOrEmpty(appSettings.WindowsSettings.Host) || string.IsNullOrEmpty(appSettings.WindowsSettings.Page))
keyValuePairs = IMetadata.GetKeyValuePairs(files);
else
{
ReadOnlyCollection<NginxFileSystem> collection = GetRecursiveCollection(appSettings.WindowsSettings.Host, appSettings.WindowsSettings.Page);
keyValuePairs = IMetadata.GetKeyValuePairs(collection);
}
foreach (KeyValuePair<string, List<FileHolder>> keyValuePair in keyValuePairs)
{
if (keyValuePair.Value.Count > 2)
throw new NotSupportedException("Too many sidecar files!");
index = WindowsSynchronousWork(logger, appSettings, windows, metadata, results, index, keyValuePair);
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<NginxFileSystem> GetRecursiveCollection(string host, string page)
{
ReadOnlyCollection<NginxFileSystem> results;
HttpClient httpClient = new();
results = GetRecursiveCollection(httpClient, host, page) ?? throw new Exception();
httpClient.Dispose();
return results;
}
private static int WindowsSynchronousWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, A_Metadata metadata, List<FirstPass> results, int index, KeyValuePair<string, List<FileHolder>> keyValuePair)
{
int result = index + 1;
windows.Tick();
FilePath filePath;
FirstPass firstPass;
string directoryName;
ExifDirectory exifDirectory;
List<FileHolder> sidecarFiles;
DeterministicHashCode deterministicHashCode;
bool fastForwardMovingPictureExpertsGroupUsed;
MinimumYearAndPathCombined minimumYearAndPathCombined;
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
HttpClient? httpClient = string.IsNullOrEmpty(appSettings.WindowsSettings.Host) || string.IsNullOrEmpty(appSettings.WindowsSettings.Page) ? null : new();
foreach (FileHolder fileHolder in keyValuePair.Value)
{
if (appSettings.WindowsSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
continue;
if (appSettings.ResultSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, result);
if (filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
continue;
if (filePath.Id is not null)
{
fastForwardMovingPictureExpertsGroupFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
directoryName = Path.GetFileName(filePath.DirectoryFullPath);
if (directoryName.EndsWith(filePath.Id.Value.ToString()))
continue;
}
else
{
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.ResultSettings, httpClient, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), result);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? windows.GetDeterministicHashCode(httpClient, filePath) : windows.GetDeterministicHashCode(httpClient, fastForwardMovingPictureExpertsGroupFilePath);
}
sidecarFiles = [];
filePath = FilePath.Get(filePath, deterministicHashCode);
for (int i = 0; i < keyValuePair.Value.Count; i++)
{
if (keyValuePair.Value[i].ExtensionLowered == fileHolder.ExtensionLowered)
continue;
sidecarFiles.Add(keyValuePair.Value[i]);
}
try
{ (minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, httpClient, filePath); }
catch (Exception)
{
logger?.LogWarning("<{filePath}>", filePath.FullName);
continue;
}
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
{
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
fastForwardMovingPictureExpertsGroupUsed = true;
firstPass = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, minimumYearAndPathCombined, sidecarFiles.ToArray());
results.Add(firstPass);
}
return result;
}
private ReadOnlyCollection<FirstPass> WindowsAsynchronousWork(AppSettings appSettings, IWindows windows, ReadOnlyCollection<string> files, A_Metadata metadata, int appSettingsMaxDegreeOfParallelism)
{
List<FirstPass> results = [];
FirstPass firstPass;
List<string> distinct = [];
List<MetadataGroup> metadataGroups = [];
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(windows, appSettings.ResultSettings, appSettings.MetadataSettings, metadata, distinct, metadataGroups));
if (windows?.CurrentTick != results.Count)
throw new NotSupportedException();
foreach (MetadataGroup metadataGroup in metadataGroups)
{
if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered))
firstPass = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
else
firstPass = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
results.Add(firstPass);
}
return results.AsReadOnly();
}
}

6
global.json Normal file
View File

@ -0,0 +1,6 @@
{
"sdk": {
"rollForward": "latestMinor",
"version": "9.0.0"
}
}