1 Commits

Author SHA1 Message Date
bd03bba63e Not tested 2025-04-06 15:51:08 -07:00
36 changed files with 1300 additions and 1130 deletions

12
.vscode/tasks.json vendored
View File

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

View File

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

View File

@ -1,4 +1,5 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
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.Methods; using View_by_Distance.Shared.Models.Stateless.Methods;
@ -47,11 +48,11 @@ internal abstract class Container
continue; continue;
foreach (Item item in filteredItems) foreach (Item item in filteredItems)
{ {
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) if (item.Property?.Id is null || item.ResizedFileHolder is null)
continue; continue;
if (results.Contains(item.ExifDirectory.FilePath.Id.Value)) if (results.Contains(item.Property.Id.Value))
continue; continue;
results.Add(item.ExifDirectory.FilePath.Id.Value); results.Add(item.Property.Id.Value);
} }
} }
return results; return results;
@ -141,23 +142,20 @@ internal abstract class Container
return results; return results;
} }
private static void ParallelFor(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, int rootDirectoryLength, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, Shared.Models.FilePair filePair, List<FilePair> results) private static void ParallelFor(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs, ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers, int rootDirectoryLength, ReadOnlyDictionary<int, ExifDirectory>? __, Shared.Models.FilePair filePair, List<FilePair> results)
{ {
dlibDotNet?.Tick(); dlibDotNet?.Tick();
bool abandoned = false; bool abandoned = false;
FileHolder sourceDirectoryFileHolder; FileHolder sourceDirectoryFileHolder;
if (exifDirectoriesById is null || filePair.FilePath.Id is null || !exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out ExifDirectory? exifDirectory)) Property? property = GetProperty(filePair);
exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); bool? fileSizeChanged = property is not null ? property.FileSize != filePair.FilePath.Length : null;
ReadOnlyCollection<string> keywords = IMetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories);
bool? shouldIgnore = propertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains);
bool? fileSizeChanged = exifDirectory is not null ? exifDirectory.FilePath.Length != filePair.FilePath.Length : null;
bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePair.FilePath.ExtensionLowered); bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePair.FilePath.ExtensionLowered);
bool? shouldIgnore = property is null || property.Keywords is null ? null : propertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l));
bool? isArchive = filePair.FilePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePair.FilePath.Id.Value, out Identifier? identifier); bool? isArchive = filePair.FilePath.Id is null || splatNineIdentifiers is null ? null : splatNineIdentifiers.TryGetValue(filePair.FilePath.Id.Value, out Identifier? identifier);
if (exifDirectory is not null && filePair.FilePath.Id is not null && filePair.FilePath.HasIgnoreKeyword is not null && filePair.FilePath.HasDateTimeOriginal is not null) if (property is not null && filePair.FilePath.Id is not null && filePair.FilePath.HasIgnoreKeyword is not null && filePair.FilePath.HasDateTimeOriginal is not null)
{ {
char? change; char? change;
ReadOnlyCollection<FilePath>? filePaths = null; ReadOnlyCollection<FilePath>? filePaths = null;
DateTime? dateTime = IDate.GetDateTimeOriginal(exifDirectory);
char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePair.FilePath).ToString()[0]; char hasIgnoreKeyword = IId.GetHasIgnoreKeyword(filePair.FilePath).ToString()[0];
char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0]; char hasDateTimeOriginal = IId.GetHasDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0];
char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0]; char missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(propertyConfiguration, filePair.FilePath).ToString()[0];
@ -172,7 +170,7 @@ internal abstract class Container
throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>"); throw new NotSupportedException($"Rename File! <{filePair.FilePath.FileNameFirstSegment}>");
} }
} }
else if ((shouldIgnore is null || !shouldIgnore.Value) && dateTime is null) else if ((shouldIgnore is null || !shouldIgnore.Value) && property.DateTimeOriginal is null)
{ {
if (filePair.FilePath.FileNameFirstSegment[^1] == missingDateTimeOriginal) if (filePair.FilePath.FileNameFirstSegment[^1] == missingDateTimeOriginal)
change = null; change = null;
@ -195,7 +193,7 @@ internal abstract class Container
RenameFile(filePair, filePair.FilePath, change.Value, filePaths); RenameFile(filePair, filePair.FilePath, change.Value, filePaths);
} }
string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.FilePath.FullName, rootDirectoryLength, forceExtensionToLower: true); string relativePath = Shared.Models.Stateless.Methods.IPath.GetRelativePath(filePair.FilePath.FullName, rootDirectoryLength, forceExtensionToLower: true);
bool? lastWriteTimeChanged = exifDirectory is not null ? propertyConfiguration.PropertiesChangedForProperty || exifDirectory.FilePath.LastWriteTicks != filePair.FilePath.LastWriteTicks : null; bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != filePair.FilePath.LastWriteTicks : null;
if (filePair.Match is not null) if (filePair.Match is not null)
sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match); sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match);
else if (!filePair.IsUnique) else if (!filePair.IsUnique)
@ -215,11 +213,27 @@ internal abstract class Container
File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePair.FilePath.LastWriteTicks)); File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePair.FilePath.LastWriteTicks));
File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value);
} }
Item item = Item.Get(filePair.FilePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, exifDirectory, abandoned, fileSizeChanged, lastWriteTimeChanged); Item item = Item.Get(filePair.FilePath, sourceDirectoryFileHolder, relativePath, isArchive, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged);
lock (results) lock (results)
results.Add(new(filePair.IsUnique, filePair.Collection, filePair.FilePath, item)); results.Add(new(filePair.IsUnique, filePair.Collection, filePair.FilePath, item));
} }
private static Property? GetProperty(Shared.Models.FilePair filePair)
{
Property? result;
if (filePair.Match is null)
result = null;
else
{
string json = File.ReadAllText(filePair.Match.FullName);
if (string.IsNullOrEmpty(json))
result = null;
else
result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property);
}
return result;
}
private static void RenameFile(Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection<FilePath> filePaths) private static void RenameFile(Shared.Models.FilePair filePair, FilePath filePath, char change, ReadOnlyCollection<FilePath> filePaths)
{ {
string checkFile; string checkFile;
@ -271,7 +285,7 @@ internal abstract class Container
continue; continue;
foreach (Item item in filteredItems) foreach (Item item in filteredItems)
{ {
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) if (item.Property?.Id is null || item.ResizedFileHolder is null)
continue; continue;
if (results.Contains(item.FilePath.FileNameFirstSegment)) if (results.Contains(item.FilePath.FileNameFirstSegment))
continue; continue;
@ -300,13 +314,13 @@ internal abstract class Container
} }
foreach (Item item in filteredItems) foreach (Item item in filteredItems)
{ {
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) if (item.Property?.Id is null || item.ResizedFileHolder is null)
continue; continue;
if (distinctItems) if (distinctItems)
{ {
if (distinct.Contains(item.ExifDirectory.FilePath.Id.Value)) if (distinct.Contains(item.Property.Id.Value))
continue; continue;
distinct.Add(item.ExifDirectory.FilePath.Id.Value); distinct.Add(item.Property.Id.Value);
} }
results.Add(item); results.Add(item);
} }

View File

@ -117,7 +117,7 @@ public class CopyDistinct
string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey; string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey;
if (key != _PropertyConfiguration.ResultContent) if (key != _PropertyConfiguration.ResultContent)
throw new NotImplementedException("Changed but didn't update!"); throw new NotImplementedException("Changed but didn't update!");
ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, filesCollection, useIgnoreExtensions: true); ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, filesCollection);
(distinctDirectories, toDoCollection) = IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, _AppSettings.IfCanUseId, filePathsCollection, _FileGroups, () => progressBar.Tick()); (distinctDirectories, toDoCollection) = IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, _AppSettings.IfCanUseId, filePathsCollection, _FileGroups, () => progressBar.Tick());
progressBar.Dispose(); progressBar.Dispose();
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -25,12 +25,10 @@ namespace View_by_Distance.Instance;
public partial class DlibDotNet : IDlibDotNet, IDisposable public partial class DlibDotNet : IDlibDotNet, IDisposable
{ {
public record Record(ReadOnlyDictionary<int, ExifDirectory> ExifDirectoriesById, public record Record(string FilesCollectionRootDirectory,
string FilesCollectionRootDirectory,
bool FilesCollectionCountIsOne, bool FilesCollectionCountIsOne,
ReadOnlyCollection<ReadOnlyCollection<FilePath>> FilePathsCollection, ReadOnlyCollection<ReadOnlyCollection<FilePath>> FilePathsCollection,
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? IdToFilePaths, ReadOnlyDictionary<int, ExifDirectory> ExifDirectoriesById);
ReadOnlyDictionary<int, Identifier>? SplatNineIdentifiers);
private readonly D_Face _Faces; private readonly D_Face _Faces;
private ProgressBar? _ProgressBar; private ProgressBar? _ProgressBar;
@ -64,11 +62,11 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
{ {
string message; string message;
_Logger = logger; _Logger = logger;
_Exceptions = [];
_Console = console; _Console = console;
_AppSettings = appSettings; _AppSettings = appSettings;
_IsEnvironment = isEnvironment; _IsEnvironment = isEnvironment;
long ticks = DateTime.Now.Ticks; long ticks = DateTime.Now.Ticks;
_Exceptions = [];
_JLinkResolvedDirectories = []; _JLinkResolvedDirectories = [];
if (ticks.ToString().Last() == '0') if (ticks.ToString().Last() == '0')
ticks += 1; ticks += 1;
@ -173,7 +171,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
_Logger?.LogInformation("First run completed. Run again if wanted"); _Logger?.LogInformation("First run completed. Run again if wanted");
} }
private int FullParallelForWork(B_Metadata metadata, private int FullParallelForWork(A_Property propertyLogic,
B_Metadata metadata,
MapLogic mapLogic, MapLogic mapLogic,
string outputResolution, string outputResolution,
bool outputResolutionHasNumber, bool outputResolutionHasNumber,
@ -181,47 +180,60 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string d2ResultsFullGroupDirectory, string d2ResultsFullGroupDirectory,
List<Tuple<string, DateTime>> sourceDirectoryChanges, List<Tuple<string, DateTime>> sourceDirectoryChanges,
Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection, Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection,
Record record, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById,
Container.Models.Container container, Container.Models.Container container,
Item item, Item item,
DateTime[] containerDateTimes, DateTime[] containerDateTimes,
bool? isFocusRelativePath) bool? isFocusRelativePath)
{ {
int result = 0; int result = 0;
bool? shouldIgnore;
List<Shared.Models.Face> faces; List<Shared.Models.Face> faces;
long ticks = DateTime.Now.Ticks; long ticks = DateTime.Now.Ticks;
DateTime dateTime = DateTime.Now; DateTime dateTime = DateTime.Now;
Shared.Models.Property? property;
List<string> parseExceptions = []; List<string> parseExceptions = [];
string[] changesFrom = [nameof(A_Property)]; string[] changesFrom = [nameof(A_Property)];
List<Tuple<string, DateTime>> subFileTuples = []; List<Tuple<string, DateTime>> subFileTuples = [];
FileHolder resizedFileHolder = _Resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber); FileHolder resizedFileHolder = _Resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber);
if (item.ExifDirectory is null || item.ExifDirectory.FilePath.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) if (item.Property is null || item.Property.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any())
throw new Exception();
if (_Configuration.PropertyConfiguration.ForcePropertyLastWriteTimeToCreationTime && item.SourceDirectoryFileHolder.LastWriteTime.Value != item.SourceDirectoryFileHolder.CreationTime.Value)
{ {
File.SetLastWriteTime(item.SourceDirectoryFileHolder.FullName, item.SourceDirectoryFileHolder.CreationTime.Value); LogItemPropertyIsNull(item);
subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), item.SourceDirectoryFileHolder.CreationTime.Value)); int? propertyHashCode = item.Property?.GetHashCode();
} property = propertyLogic.GetProperty(metadata, item, subFileTuples, parseExceptions);
else if (item.SourceDirectoryFileHolder.LastWriteTime is not null) item.Update(property);
subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), item.SourceDirectoryFileHolder.LastWriteTime.Value)); if (propertyHashCode is null)
else {
subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), new FileInfo(item.SourceDirectoryFileHolder.FullName).LastWriteTime)); lock (sourceDirectoryChanges)
if (resizedFileHolder.Exists && item.ExifDirectory.Width is not null && item.ExifDirectory.Width.Value > 4 && _Configuration.SaveBlurHashForOutputResolutions.Contains(outputResolution)) sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
{ }
string? file = _BlurHasher.GetFile(item.FilePath); else if (propertyHashCode.Value != property.GetHashCode())
if (file is not null && !File.Exists(file)) {
_ = _BlurHasher.EncodeAndSave(item.FilePath, resizedFileHolder); lock (sourceDirectoryChanges)
} sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
if (item.FilePath.Id is null || !record.ExifDirectoriesById.TryGetValue(item.FilePath.Id.Value, out ExifDirectory? exifDirectory)) }
{
shouldIgnore = null;
exifDirectory = null;
} }
else else
{ {
ReadOnlyCollection<string> keywords = IMetaBase.GetKeywords(exifDirectory.ExifBaseDirectories); property = item.Property;
shouldIgnore = _Configuration.PropertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains); if (_Configuration.PropertyConfiguration.ForcePropertyLastWriteTimeToCreationTime && item.SourceDirectoryFileHolder.LastWriteTime.Value != item.SourceDirectoryFileHolder.CreationTime.Value)
{
File.SetLastWriteTime(item.SourceDirectoryFileHolder.FullName, item.SourceDirectoryFileHolder.CreationTime.Value);
subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), item.SourceDirectoryFileHolder.CreationTime.Value));
}
else if (item.SourceDirectoryFileHolder.LastWriteTime is not null)
subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), item.SourceDirectoryFileHolder.LastWriteTime.Value));
else
subFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), new FileInfo(item.SourceDirectoryFileHolder.FullName).LastWriteTime));
if (resizedFileHolder.Exists && item.Property.Width is not null && item.Property.Width.Value > 4 && _Configuration.SaveBlurHashForOutputResolutions.Contains(outputResolution))
{
string? file = _BlurHasher.GetFile(item.FilePath);
if (file is not null && !File.Exists(file))
_ = _BlurHasher.EncodeAndSave(item.FilePath, resizedFileHolder);
}
}
bool? shouldIgnore = property is null || property.Keywords is null ? null : _Configuration.PropertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l));
if (shouldIgnore is not null)
{
if (shouldIgnore.Value) if (shouldIgnore.Value)
{ {
FileInfo fileInfo = new(resizedFileHolder.FullName); FileInfo fileInfo = new(resizedFileHolder.FullName);
@ -236,9 +248,12 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
throw new NotSupportedException($"Rename File! <{item.FilePath.FileNameFirstSegment}>"); throw new NotSupportedException($"Rename File! <{item.FilePath.FileNameFirstSegment}>");
} }
} }
if (property is null || item.Property is null)
throw new NullReferenceException(nameof(property));
item.SetResizedFileHolder(_Resize.FileNameExtension, resizedFileHolder); item.SetResizedFileHolder(_Resize.FileNameExtension, resizedFileHolder);
MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder);
exifDirectory ??= metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); if (item.FilePath.Id is null || !exifDirectoriesById.TryGetValue(item.FilePath.Id.Value, out ExifDirectory? exifDirectory))
exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem);
if (_AppSettings.Places.Count > 0) if (_AppSettings.Places.Count > 0)
{ {
float latitude; float latitude;
@ -261,13 +276,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
distance += 1; distance += 1;
} }
} }
Dictionary<string, int[]> outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem); Dictionary<string, int[]> outputResolutionToResize = _Resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.Property, mappingFromItem);
if (_Configuration.SaveResizedSubfiles) if (_Configuration.SaveResizedSubfiles)
{ {
if (shouldIgnore is not null && item.FilePath.HasIgnoreKeyword is not null && item.FilePath.HasIgnoreKeyword.Value != shouldIgnore.Value) if (shouldIgnore is not null && item.FilePath.HasIgnoreKeyword is not null && item.FilePath.HasIgnoreKeyword.Value != shouldIgnore.Value)
faces = []; faces = [];
else else
_Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.ExifDirectory, mappingFromItem, outputResolutionToResize); _Resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize);
} }
if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution)) if (!_Configuration.LoadOrCreateThenSaveImageFacesResultsForOutputResolutions.Contains(outputResolution))
faces = []; faces = [];
@ -279,7 +294,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection)) if (!fileNameToCollection.TryGetValue(mappingFromItem.Id, out mappingFromPhotoPrismCollection))
mappingFromPhotoPrismCollection = null; mappingFromPhotoPrismCollection = null;
bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution); bool move = _Configuration.DistanceMoveUnableToMatch || _Configuration.DistanceRenameToMatch && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution);
faces = _Faces.GetFaces(outputResolution, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.ExifDirectory, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection); faces = _Faces.GetFaces(outputResolution, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, property, mappingFromItem, outputResolutionToResize, mappingFromPhotoPrismCollection);
result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces); result = GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(mapLogic, item, isFocusRelativePath, mappingFromItem, mappingFromPhotoPrismCollection, faces);
List<(Shared.Models.Face, FileHolder?, string, bool Saved)> faceCollection = _Faces.SaveFaces(item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces); List<(Shared.Models.Face, FileHolder?, string, bool Saved)> faceCollection = _Faces.SaveFaces(item.FilePath, subFileTuples, parseExceptions, mappingFromItem, exifDirectory, faces);
if (move && faceCollection.All(l => !l.Saved)) if (move && faceCollection.All(l => !l.Saved))
@ -312,6 +327,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
} }
private (int, bool) FullParallelWork(int maxDegreeOfParallelism, private (int, bool) FullParallelWork(int maxDegreeOfParallelism,
A_Property propertyLogic,
B_Metadata metadata, B_Metadata metadata,
MapLogic mapLogic, MapLogic mapLogic,
string outputResolution, string outputResolution,
@ -320,7 +336,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string d2ResultsFullGroupDirectory, string d2ResultsFullGroupDirectory,
List<Tuple<string, DateTime>> sourceDirectoryChanges, List<Tuple<string, DateTime>> sourceDirectoryChanges,
Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection, Dictionary<int, List<MappingFromPhotoPrism>> fileNameToCollection,
Record record, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById,
Container.Models.Container container, Container.Models.Container container,
ReadOnlyCollection<Item> filteredItems, ReadOnlyCollection<Item> filteredItems,
string message) string message)
@ -337,7 +353,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
{ {
try try
{ {
result += FullParallelForWork(metadata, result += FullParallelForWork(propertyLogic,
metadata,
mapLogic, mapLogic,
outputResolution, outputResolution,
outputResolutionHasNumber, outputResolutionHasNumber,
@ -345,7 +362,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
d2ResultsFullGroupDirectory, d2ResultsFullGroupDirectory,
sourceDirectoryChanges, sourceDirectoryChanges,
fileNameToCollection, fileNameToCollection,
record, exifDirectoriesById,
container, container,
filteredItems[i], filteredItems[i],
containerDateTimes, containerDateTimes,
@ -366,11 +383,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
private void FullDoWork(string argZero, private void FullDoWork(string argZero,
string propertyRoot, string propertyRoot,
long ticks, long ticks,
string aResultsFullGroupDirectory,
string fPhotoPrismSingletonDirectory, string fPhotoPrismSingletonDirectory,
int count, int count,
B_Metadata metadata, B_Metadata metadata,
Record record, ReadOnlyDictionary<int, ExifDirectory> exifDirectoriesById,
ReadOnlyCollection<Container.Models.Container> readOnlyContainers, ReadOnlyCollection<Container.Models.Container> readOnlyContainers,
A_Property propertyLogic,
MapLogic mapLogic) MapLogic mapLogic)
{ {
int total; int total;
@ -415,9 +434,11 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName); anyNullOrNoIsUniqueFileName = filteredItems.Any(l => !l.IsUniqueFileName);
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{totalSeconds} total second(s) - {outputResolution} - {i + 1:000} / {readOnlyContainers.Count:000} - {total} / {count} total - <{container.SourceDirectory}> [{filteredItems.Count:000}] - total not mapped {totalNotMapped:000000}"; message = $"{totalSeconds} total second(s) - {outputResolution} - {i + 1:000} / {readOnlyContainers.Count:000} - {total} / {count} total - <{container.SourceDirectory}> [{filteredItems.Count:000}] - total not mapped {totalNotMapped:000000}";
propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, container.SourceDirectory, anyNullOrNoIsUniqueFileName);
if (outputResolutionHasNumber) if (outputResolutionHasNumber)
_Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory); _Resize.SetAngleBracketCollection(cResultsFullGroupDirectory, container.SourceDirectory);
(notMapped, exceptions) = FullParallelWork(maxDegreeOfParallelism, (notMapped, exceptions) = FullParallelWork(maxDegreeOfParallelism,
propertyLogic,
metadata, metadata,
mapLogic, mapLogic,
outputResolution, outputResolution,
@ -426,7 +447,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
d2ResultsFullGroupDirectory, d2ResultsFullGroupDirectory,
sourceDirectoryChanges, sourceDirectoryChanges,
fileNameToCollection, fileNameToCollection,
record, exifDirectoriesById,
container, container,
filteredItems, filteredItems,
message); message);
@ -688,11 +709,11 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
foreach (Item item in distinctFilteredItems) foreach (Item item in distinctFilteredItems)
{ {
found = false; found = false;
if (item.ExifDirectory?.FilePath.Id is null) if (item.Property?.Id is null)
continue; continue;
foreach (Mapping mapping in distinctFilteredMappingCollection) foreach (Mapping mapping in distinctFilteredMappingCollection)
{ {
if (mapping.MappingFromItem.Id != item.ExifDirectory.FilePath.Id.Value) if (mapping.MappingFromItem.Id != item.Property.Id.Value)
continue; continue;
found = true; found = true;
break; break;
@ -707,15 +728,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
string fileName; string fileName;
string directory; string directory;
bool? isWrongYear; bool? isWrongYear;
DateTime? dateTime; List<DateTime> dateTimes;
string? exifDirectoryModel;
List<string> distinct = []; List<string> distinct = [];
WindowsShortcut windowsShortcut; WindowsShortcut windowsShortcut;
ReadOnlyCollection<DateTime> dateTimes;
List<(string, string, string)> collection = []; List<(string, string, string)> collection = [];
foreach (Item item in distinctValidImageItems) foreach (Item item in distinctValidImageItems)
{ {
if (item.ExifDirectory?.FilePath.Id is null) if (item.Property?.Id is null)
continue; continue;
if (item.IsNotUniqueAndNeedsReview is null || !item.IsNotUniqueAndNeedsReview.Value) if (item.IsNotUniqueAndNeedsReview is null || !item.IsNotUniqueAndNeedsReview.Value)
continue; continue;
@ -728,19 +747,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
} }
foreach (Item item in distinctValidImageItems) foreach (Item item in distinctValidImageItems)
{ {
if (item.ExifDirectory?.FilePath.Id is null) if (item.Property?.Id is null || item.Property.DateTimeOriginal is null)
continue; continue;
dateTime = IDate.GetDateTimeOriginal(item.ExifDirectory); dateTimes = item.Property.GetDateTimes();
if (dateTime is null) (isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, item.Property.DateTimeOriginal, dateTimes);
continue;
dateTimes = IDate.GetDateTimes(item.ExifDirectory);
(isWrongYear, _) = Shared.Models.Stateless.Methods.IProperty.IsWrongYear(item.FilePath, dateTime, dateTimes.ToList());
if (isWrongYear is null || !isWrongYear.Value) if (isWrongYear is null || !isWrongYear.Value)
continue; continue;
// Remove-Item -LiteralPath "\\?\D:\Tmp\a\EX-Z70 " // Remove-Item -LiteralPath "\\?\D:\Tmp\a\EX-Z70 "
exifDirectoryModel = IMetaBase.GetModel(item.ExifDirectory?.ExifBaseDirectories); model = string.IsNullOrEmpty(item.Property.Model) ? "Unknown" : CameraRegex().Replace(item.Property.Model.Trim(), "_");
model = string.IsNullOrEmpty(exifDirectoryModel) ? "Unknown" : CameraRegex().Replace(exifDirectoryModel.Trim(), "_"); directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Item)})", item.Property.DateTimeOriginal.Value.Year.ToString(), model);
directory = Path.Combine($"{eDistanceContentDirectory[..^1]}{nameof(Item)})", dateTime.Value.Year.ToString(), model);
fileName = item.IsNotUniqueAndNeedsReview is not null && item.IsNotUniqueAndNeedsReview.Value ? Path.Combine(directory, $"{item.FilePath.Name} {item.FilePath.Length}.lnk") : Path.Combine(directory, $"{item.FilePath.Name}.lnk"); fileName = item.IsNotUniqueAndNeedsReview is not null && item.IsNotUniqueAndNeedsReview.Value ? Path.Combine(directory, $"{item.FilePath.Name} {item.FilePath.Length}.lnk") : Path.Combine(directory, $"{item.FilePath.Name}.lnk");
collection.Add((item.FilePath.FullName, directory, fileName)); collection.Add((item.FilePath.FullName, directory, fileName));
if (distinct.Contains(directory)) if (distinct.Contains(directory))
@ -909,19 +924,15 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
return results; return results;
} }
private bool? GetIsFocusModel(ExifDirectory? exifDirectory) private bool? GetIsFocusModel(Shared.Models.Property? property)
{ {
bool? result; bool? result;
if (string.IsNullOrEmpty(_Configuration.FocusModel)) if (string.IsNullOrEmpty(_Configuration.FocusModel))
result = null; result = null;
else if (property is null || string.IsNullOrEmpty(property.Model))
result = null;
else else
{ result = property.Model.Contains(_Configuration.FocusModel);
string? model = IMetaBase.GetModel(exifDirectory?.ExifBaseDirectories);
if (exifDirectory is null || string.IsNullOrEmpty(model))
result = null;
else
result = model.Contains(_Configuration.FocusModel);
}
return result; return result;
} }
@ -1088,6 +1099,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
bool configurationOutputResolutionsHas = false; bool configurationOutputResolutionsHas = false;
ReadOnlyDictionary<long, List<int>> personKeyToIds; ReadOnlyDictionary<long, List<int>> personKeyToIds;
(int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear);
ReadOnlyDictionary<int, Identifier>? splatNineIdentifiers = null;
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>>? keyValuePairs = null;
string[] checkDirectories = string[] checkDirectories =
[ [
Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Ancestry"), Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Ancestry"),
@ -1114,6 +1127,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
if (!runToDoCollectionFirst) if (!runToDoCollectionFirst)
break; break;
record = GetFilesCollectionThenCopyOrMove(dlibDotNet, ticks, fileSearchFilter, directorySearchFilter, bResultsFullGroupDirectory, outputResolution); record = GetFilesCollectionThenCopyOrMove(dlibDotNet, ticks, fileSearchFilter, directorySearchFilter, bResultsFullGroupDirectory, outputResolution);
keyValuePairs = FilePath.GetKeyValuePairs(record.FilePathsCollection);
splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, keyValuePairs);
break; break;
} }
fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent); fPhotoPrismContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(F_PhotoPrism), _Configuration.PropertyConfiguration.ResultContent);
@ -1152,9 +1167,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
record = new(FilesCollectionRootDirectory: filesCollectionRootDirectory, record = new(FilesCollectionRootDirectory: filesCollectionRootDirectory,
FilesCollectionCountIsOne: false, FilesCollectionCountIsOne: false,
FilePathsCollection: filePathsCollection, FilePathsCollection: filePathsCollection,
ExifDirectoriesById: new(exifDirectoriesById), ExifDirectoriesById: new(exifDirectoriesById));
IdToFilePaths: null,
SplatNineIdentifiers: null);
break; break;
} }
} }
@ -1176,8 +1189,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
_Faces.HiddenFileNameExtension, _Faces.HiddenFileNameExtension,
eDistanceContentDirectory, eDistanceContentDirectory,
record.FilesCollectionRootDirectory, record.FilesCollectionRootDirectory,
record.IdToFilePaths, keyValuePairs,
record.SplatNineIdentifiers, splatNineIdentifiers,
record.FilePathsCollection, record.FilePathsCollection,
record.ExifDirectoriesById); record.ExifDirectoriesById);
_ProgressBar.Dispose(); _ProgressBar.Dispose();
@ -1188,11 +1201,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
FullDoWork(argZero, FullDoWork(argZero,
propertyRoot, propertyRoot,
ticks, ticks,
aResultsFullGroupDirectory,
fPhotoPrismSingletonDirectory, fPhotoPrismSingletonDirectory,
count, count,
metadata, metadata,
record, record.ExifDirectoriesById,
readOnlyContainers, readOnlyContainers,
propertyLogic,
mapLogic); mapLogic);
ReadOnlyCollection<Item> distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); ReadOnlyCollection<Item> distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true);
if (_Configuration.LookForAbandoned) if (_Configuration.LookForAbandoned)
@ -1234,8 +1249,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
&& _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution) && _Configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions.Contains(outputResolution)
&& _Exceptions.Count == 0) && _Exceptions.Count == 0)
MapLogic(ticks, readOnlyContainers, fPhotoPrismContentDirectory, mapLogic, outputResolution, new(personKeyToIds), distinctValidImageFaces, distinctValidImageMappingCollection); MapLogic(ticks, readOnlyContainers, fPhotoPrismContentDirectory, mapLogic, outputResolution, new(personKeyToIds), distinctValidImageFaces, distinctValidImageMappingCollection);
if (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && record?.SplatNineIdentifiers is not null && distinctValidImageMappingCollection.Count > 0) if (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && splatNineIdentifiers is not null && distinctValidImageMappingCollection.Count > 0)
_Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.ImmichOwnerId, _Configuration.ImmichRoot, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, record.SplatNineIdentifiers, distinctValidImageMappingCollection); _Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.ImmichOwnerId, _Configuration.ImmichRoot, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, splatNineIdentifiers, distinctValidImageMappingCollection);
if (_IsEnvironment.Development) if (_IsEnvironment.Development)
continue; continue;
if (!_IsEnvironment.Development) if (!_IsEnvironment.Development)
@ -1294,14 +1309,10 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
progressBar = new(count, message, _ProgressBarOptions); progressBar = new(count, message, _ProgressBarOptions);
_ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, () => progressBar.Tick()); _ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, () => progressBar.Tick());
progressBar.Dispose(); progressBar.Dispose();
ReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection); result = new(FilesCollectionRootDirectory: filesCollectionRootDirectory,
ReadOnlyDictionary<int, Identifier> splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, idToFilePaths);
result = new(ExifDirectoriesById: new(exifDirectoriesById),
FilesCollectionRootDirectory: filesCollectionRootDirectory,
FilesCollectionCountIsOne: filesCollectionCountIsOne, FilesCollectionCountIsOne: filesCollectionCountIsOne,
FilePathsCollection: filePathsCollection, FilePathsCollection: filePathsCollection,
IdToFilePaths: idToFilePaths, ExifDirectoriesById: new(exifDirectoriesById));
SplatNineIdentifiers: splatNineIdentifiers);
return result; return result;
} }
@ -1403,14 +1414,14 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath); isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath);
foreach (Item item in filteredItems) foreach (Item item in filteredItems)
{ {
if (item.ExifDirectory?.FilePath.Id is null || item.ResizedFileHolder is null) if (item.Property?.Id is null || item.ResizedFileHolder is null)
continue; continue;
mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, item.ResizedFileHolder); mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, item.ResizedFileHolder);
if (distinctItems) if (distinctItems)
{ {
if (distinct.Contains(item.ExifDirectory.FilePath.Id.Value)) if (distinct.Contains(item.Property.Id.Value))
continue; continue;
distinct.Add(item.ExifDirectory.FilePath.Id.Value); distinct.Add(item.Property.Id.Value);
} }
count++; count++;
anyValidFaces = false; anyValidFaces = false;
@ -1442,7 +1453,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
bool? eyeReview = null; bool? eyeReview = null;
int confidencePercent = 0; int confidencePercent = 0;
int faceAreaPermyriad = 0; int faceAreaPermyriad = 0;
bool? isFocusModel = GetIsFocusModel(item.ExifDirectory); bool? isFocusModel = GetIsFocusModel(item.Property);
long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray();
int wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(Shared.Models.Stateless.ILocation.Digits); int wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(Shared.Models.Stateless.ILocation.Digits);
string deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, Shared.Models.Stateless.ILocation.Digits); string deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, Shared.Models.Stateless.ILocation.Digits);
@ -1598,12 +1609,12 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
MappingFromLocation? mappingFromLocation; MappingFromLocation? mappingFromLocation;
MappingFromFilterPre mappingFromFilterPre; MappingFromFilterPre mappingFromFilterPre;
MappingFromFilterPost mappingFromFilterPost; MappingFromFilterPost mappingFromFilterPost;
bool? isFocusModel = GetIsFocusModel(item.ExifDirectory); bool? isFocusModel = GetIsFocusModel(item.Property);
ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.ExifDirectory?.FilePath.Id); ReadOnlyDictionary<int, ReadOnlyCollection<PersonContainer>>? wholePercentagesToPersonContainers = mapLogic.GetWholePercentagesToPersonContainers(item.Property?.Id);
long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray(); long[] jLinkResolvedPersonKeys = _JLinkResolvedDirectories.Select(l => l.PersonKey).ToArray();
foreach (Shared.Models.Face face in faces) foreach (Shared.Models.Face face in faces)
{ {
if (item.ExifDirectory?.FilePath.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null) if (item.Property?.Id is null || face.FaceEncoding is null || face.Location is null || face.OutputResolution is null)
{ {
canReMap = null; canReMap = null;
isFocusPerson = null; isFocusPerson = null;
@ -1626,7 +1637,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable
wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); wholePercentRectangle = Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution); deterministicHashCodeKey = IMapping.GetDeterministicHashCodeKey(item.FilePath, face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution);
mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle); mappingFromLocation = new(faceAreaPermyriad, confidencePercent, deterministicHashCodeKey, eyeα, eyeReview, wholePercentRectangle);
inSkipCollection = mapLogic.InSkipCollection(item.ExifDirectory.FilePath.Id.Value, mappingFromLocation); inSkipCollection = mapLogic.InSkipCollection(item.Property.Id.Value, mappingFromLocation);
mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath); mappingFromFilterPre = new(inSkipCollection, isFocusModel, isFocusRelativePath);
canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); canReMap = Map.Models.Stateless.Methods.IMapLogic.CanReMap(jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);
isFocusPerson = mapLogic.IsFocusPerson(_Configuration.SkipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation); isFocusPerson = mapLogic.IsFocusPerson(_Configuration.SkipPersonWithMoreThen, jLinkResolvedPersonKeys, wholePercentagesToPersonContainers, mappingFromLocation);

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions; private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions;
private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups; private readonly ReadOnlyDictionary<byte, ReadOnlyCollection<string>>[] _ResultSingletonFileGroups;
public B_Metadata(IDlibDotNet? dlibDotNet, IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, long ticks, string bResultsFullGroupDirectory) public B_Metadata(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata, long ticks, string bResultsFullGroupDirectory)
{ {
_PropertyConfiguration = propertyConfiguration; _PropertyConfiguration = propertyConfiguration;
_PropertiesChangedForMetadata = propertiesChangedForMetadata; _PropertiesChangedForMetadata = propertiesChangedForMetadata;
@ -68,7 +68,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
ReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles = FilePath.GetFilesKeyValuePairs(filePathsCollection); ReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles = FilePath.GetFilesKeyValuePairs(filePathsCollection);
ReadOnlyCollection<FilePair> filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupSingletonDirectory, filePathsCollection, fileNamesToFiles); ReadOnlyCollection<FilePair> filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupSingletonDirectory, filePathsCollection, fileNamesToFiles);
string message = $") Preloading ExifDirectory Dictionary - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; string message = $") Preloading ExifDirectory Dictionary - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
dlibDotNet?.ConstructProgressBar(filePairs.Count, message); dlibDotNet.ConstructProgressBar(filePairs.Count, message);
_ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(dlibDotNet, filePairs[i], results)); _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(dlibDotNet, filePairs[i], results));
jsonGroupDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultCollection); jsonGroupDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultCollection);
foreach (ExifDirectory exifDirectory in results) foreach (ExifDirectory exifDirectory in results)
@ -86,7 +86,7 @@ public class B_Metadata : IMetadata<MetadataExtractor.Directory>
Collection = filePathsSingletonCollection[0]; Collection = filePathsSingletonCollection[0];
} }
private void ParallelFor(IDlibDotNet? dlibDotNet, FilePair filePair, List<ExifDirectory> results) private void ParallelFor(IDlibDotNet dlibDotNet, FilePair filePair, List<ExifDirectory> results)
{ {
dlibDotNet?.Tick(); dlibDotNet?.Tick();
if (filePair.FilePath.Id is null) if (filePair.FilePath.Id is null)

View File

@ -8,16 +8,16 @@ 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(List<byte> thisBytes, byte[] thatBytes) private static bool StartsWith(byte[] thisBytes, byte[] thatBytes)
{ {
for (int i = 0; i < thatBytes.Length; i += 1) for (int i = 0; i < thatBytes.Length; i += 1)
{ {
@ -103,41 +103,24 @@ internal static class Dimensions
internal static Size? GetDimensions(BinaryReader binaryReader) internal static Size? GetDimensions(BinaryReader binaryReader)
{ {
Size? result; int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length;
List<byte> magicBytes = []; byte[] magicBytes = new byte[maxMagicBytesLength];
int[] magicBytesLengths = (from l in _ImageFormatDecoders.Keys where l.Length <= binaryReader.BaseStream.Length orderby l.Length descending select l.Length).ToArray(); for (int i = 0; i < maxMagicBytesLength; i += 1)
if (magicBytesLengths.Length == 0)
result = null;
else
{ {
result = null; magicBytes[i] = binaryReader.ReadByte();
if (binaryReader.BaseStream.Length == binaryReader.BaseStream.Position) foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders)
_ = binaryReader.BaseStream.Seek(0, SeekOrigin.Begin);
for (int i = 0; i < magicBytesLengths[0]; i++)
{ {
magicBytes.Add(binaryReader.ReadByte()); if (StartsWith(magicBytes, kvPair.Key))
foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders) return kvPair.Value(binaryReader);
{
if (StartsWith(magicBytes, kvPair.Key))
{
result = kvPair.Value(binaryReader);
break;
}
}
if (result is not null)
break;
} }
} }
return result; return null;
} }
internal static Size? GetDimensions(string path) internal static Size? GetDimensions(string path)
{ {
Size? result; using BinaryReader binaryReader = new(File.OpenRead(path));
using FileStream fileStream = File.OpenRead(path); return GetDimensions(binaryReader);
using BinaryReader binaryReader = new(fileStream);
result = GetDimensions(binaryReader);
return result;
} }
} }

View File

@ -1,3 +1,4 @@
using ShellProgressBar;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
@ -65,6 +66,39 @@ public class A_Property
converted: false)); converted: false));
} }
public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, int t, Container.Models.Container[] containers)
{
int total = 0;
string message;
int totalSeconds;
bool anyNullOrNoIsUniqueFileName;
List<Exception> exceptions = [];
Container.Models.Container container;
int containersLength = containers.Length;
const string outputResolution = "Original";
List<Tuple<string, DateTime>> sourceDirectoryChanges = [];
string propertyRoot = IResult.GetResultsGroupDirectory(_PropertyConfiguration, nameof(A_Property));
for (int i = 0; i < containers.Length; i++)
{
container = containers[i];
if (container.Items.Count == 0)
continue;
sourceDirectoryChanges.Clear();
if (container.Items.Count == 0)
continue;
anyNullOrNoIsUniqueFileName = container.Items.Any(l => !l.IsUniqueFileName);
SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName);
totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}";
SavePropertyParallelWork(_MaxDegreeOfParallelism, metadata, exceptions, sourceDirectoryChanges, container, container.Items, message);
if (exceptions.Count == container.Items.Count)
throw new Exception(string.Concat("All in [", container.SourceDirectory, "]failed!"));
if (exceptions.Count != 0)
_ExceptionsDirectories.Add(container.SourceDirectory);
total += container.Items.Count;
}
}
private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName) private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName)
{ {
_AngleBracketCollection.Clear(); _AngleBracketCollection.Clear();
@ -77,6 +111,177 @@ public class A_Property
SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName);
} }
private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, List<Exception> exceptions, List<Tuple<string, DateTime>> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection<Item> items, string message)
{
List<Tuple<string, DateTime>> sourceDirectoryFileTuples = [];
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
using ProgressBar progressBar = new(items.Count, message, options);
_ = Parallel.For(0, items.Count, parallelOptions, (i, state) =>
{
try
{
long ticks = DateTime.Now.Ticks;
DateTime dateTime = DateTime.Now;
List<Tuple<string, DateTime>> collection;
SavePropertyParallelForWork(metadata, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]);
if (i == 0 || sourceDirectoryChanges.Count != 0)
progressBar.Tick();
lock (sourceDirectoryFileTuples)
collection = (from l in sourceDirectoryFileTuples where l.Item2 > dateTime select l).ToList();
lock (sourceDirectoryChanges)
sourceDirectoryChanges.AddRange(collection);
}
catch (Exception ex)
{
lock (exceptions)
exceptions.Add(ex);
}
});
}
private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, string sourceDirectory, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<Tuple<string, DateTime>> sourceDirectoryChanges, Item item)
{
Shared.Models.Property property;
List<string> parseExceptions = [];
bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered);
string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}");
if (item.IsValidImageFormatExtension && item.FilePath.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.FilePath.FullName != filteredSourceDirectoryFileExtensionLowered)
File.Move(item.FilePath.FullName, filteredSourceDirectoryFileExtensionLowered);
if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.Property is null)
{
property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension);
lock (sourceDirectoryChanges)
sourceDirectoryChanges.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
lock (item)
item.Update(property);
}
}
private Shared.Models.Property GetImageProperty(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, Item item, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<string> parseExceptions, bool isIgnoreExtension)
{
Shared.Models.Property? result;
int? id = null;
FileInfo fileInfo;
string? json = null;
bool hasWrongYearProperty = false;
string[] changesFrom = [];
string angleBracket = _AngleBracketCollection[0];
bool populateId = _Configuration.PopulatePropertyId;
if (!item.IsUniqueFileName)
fileInfo = new(Path.Combine(angleBracket.Replace("<>", _PropertyConfiguration.ResultSingleton), $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json"));
else
{
string fileName = $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}.json";
CombinedEnumAndIndex cei = Shared.Models.Stateless.Methods.IPath.GetCombinedEnumAndIndex(_PropertyConfiguration, item.FilePath);
string directory = _ResultSingletonFileGroups[0][cei.Enum][cei.Index];
fileInfo = new(Path.Combine(directory, fileName));
MoveIf(fileName, cei, directory, fileInfo);
}
List<DateTime> dateTimes = (from l in sourceDirectoryFileTuples where l is not null && changesFrom.Contains(l.Item1) select l.Item2).ToList();
if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
fileInfo.Refresh();
}
if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
}
if (_Configuration.PropertiesChangedForProperty)
result = null;
else if (!fileInfo.Exists)
result = null;
else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old"))
throw new ArgumentException("must be a *.json file");
else if (dateTimes.Count != 0 && dateTimes.Max() > fileInfo.LastWriteTime)
result = null;
else
{
json = File.ReadAllText(fileInfo.FullName);
try
{
if (item.Property is not null)
result = item.Property;
else
result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property);
if (result is not null && json.Contains("WrongYear"))
{
id = result.Id;
hasWrongYearProperty = true;
result = null;
}
if (!isIgnoreExtension && item.IsValidImageFormatExtension && ((populateId && result?.Id is null) || result?.Width is null || result.Height is null))
{
id = result?.Id;
result = null;
}
if (!isIgnoreExtension && item.IsValidImageFormatExtension && populateId && result is not null && result.LastWriteTime.Ticks != item.FilePath.LastWriteTicks)
{
id = null;
result = null;
}
if (!isIgnoreExtension && item.IsValidImageFormatExtension && result?.Width is not null && result.Height is not null && result.Width.Value == result.Height.Value)
{
id = result.Id;
result = null;
if (result?.Width is not null && result.Height is not null && result.Width.Value != result.Height.Value)
throw new Exception("Was square!");
}
if (!isIgnoreExtension && item.IsValidImageFormatExtension && result is not null && result.FileSize != item.FilePath.Length)
{
id = result.Id;
result = null;
}
if (result is not null)
{
sourceDirectoryFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), fileInfo.LastWriteTime));
if (fileInfo.CreationTime != result.LastWriteTime)
{
File.SetCreationTime(fileInfo.FullName, result.LastWriteTime);
File.SetLastWriteTime(fileInfo.FullName, fileInfo.LastWriteTime);
}
}
}
catch (Exception)
{
result = null;
parseExceptions.Add(nameof(A_Property));
}
}
if (result is null)
{
id ??= item.FilePath.Id;
(_, _, result) = Stateless.Property.GetProperty(populateId, metadata, item.FilePath, result, isIgnoreExtension, item.IsValidImageFormatExtension, id, _ASCIIEncoding);
json = JsonSerializer.Serialize(result, PropertyGenerationContext.Default.Property);
if (populateId && Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: true, compareBeforeWrite: true))
{
File.SetCreationTime(fileInfo.FullName, result.LastWriteTime);
if (!_Configuration.ForcePropertyLastWriteTimeToCreationTime)
sourceDirectoryFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), DateTime.Now));
else
{
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
sourceDirectoryFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), fileInfo.CreationTime));
}
}
}
else if (hasWrongYearProperty)
{
json = JsonSerializer.Serialize(result, PropertyGenerationContext.Default.Property);
if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: true, compareBeforeWrite: true))
{
File.SetCreationTime(fileInfo.FullName, result.LastWriteTime);
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh();
sourceDirectoryFileTuples.Add(new Tuple<string, DateTime>(nameof(A_Property), fileInfo.CreationTime));
}
}
return result;
}
private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo) private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo)
{ {
string[] segments = directory.Split(cei.Combined); string[] segments = directory.Split(cei.Combined);
@ -96,4 +301,21 @@ public class A_Property
} }
} }
public Shared.Models.Property GetProperty(Shared.Models.Methods.IMetadata<MetadataExtractor.Directory> metadata, Item item, List<Tuple<string, DateTime>> sourceDirectoryFileTuples, List<string> parseExceptions)
{
Shared.Models.Property result;
bool angleBracketCollectionAny = _AngleBracketCollection.Count != 0;
if (!angleBracketCollectionAny)
{
if (item.FilePath.DirectoryFullPath is null)
throw new NullReferenceException(nameof(item.FilePath.DirectoryFullPath));
SetAngleBracketCollection(item.FilePath.DirectoryFullPath, !item.IsUniqueFileName);
}
bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered);
result = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension);
if (!angleBracketCollectionAny)
_AngleBracketCollection.Clear();
return result;
}
} }

View File

@ -21,9 +21,9 @@ internal partial class Property
List<long> ticksCollection = []; List<long> ticksCollection = [];
foreach (Item item in container.Items) foreach (Item item in container.Items)
{ {
if (item.ExifDirectory is null) if (item.Property is null)
continue; continue;
minimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(item.ExifDirectory); minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
if (minimumDateTime is null) if (minimumDateTime is null)
continue; continue;
ticksCollection.Add(minimumDateTime.Value.Ticks); ticksCollection.Add(minimumDateTime.Value.Ticks);
@ -195,17 +195,17 @@ internal partial class Property
{ {
ticks = null; ticks = null;
item = container.Items[j]; item = container.Items[j];
if (item.ExifDirectory is null) if (item.Property is null)
continue; continue;
minimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(item.ExifDirectory); minimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(item.Property);
if (minimumDateTime is null) if (minimumDateTime is null)
continue; continue;
for (int k = j + 1; k < container.Items.Count; k++) for (int k = j + 1; k < container.Items.Count; k++)
{ {
nextItem = container.Items[k]; nextItem = container.Items[k];
if (nextItem.ExifDirectory is null) if (nextItem.Property is null)
continue; continue;
nextMinimumDateTime = Shared.Models.Stateless.Methods.IDate.GetMinimum(nextItem.ExifDirectory); nextMinimumDateTime = Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(nextItem.Property);
if (nextMinimumDateTime is null) if (nextMinimumDateTime is null)
continue; continue;
ticks = nextMinimumDateTime.Value.Ticks; ticks = nextMinimumDateTime.Value.Ticks;

View File

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

View File

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

View File

@ -76,7 +76,7 @@ public class FaceFileSystem : FileSystem, Properties.IFaceFileSystem
} }
else else
{ {
System.Drawing.Size size = Stateless.Methods.Dimensions.GetDimensions(fileInfo.FullName, faceRight, faceBottom); System.Drawing.Size size = Stateless.Methods.ImageHelper.GetDimensions(fileInfo.FullName, faceRight, faceBottom);
imageWidth = size.Width; imageWidth = size.Width;
imageHeight = size.Height; imageHeight = size.Height;
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,149 +0,0 @@
using System.Drawing;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal static class Dimensions
{
#pragma warning disable IDE0230
private static readonly Dictionary<byte[], Func<BinaryReader, Size?>> _ImageFormatDecoders = new()
{
{ new byte[] { 0xff, 0xd8 }, DecodeJfif },
{ new byte[] { 0x42, 0x4D }, DecodeBitmap },
{ new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif },
{ new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng },
};
#pragma warning restore IDE0230
private static bool StartsWith(List<byte> thisBytes, byte[] thatBytes)
{
for (int i = 0; i < thatBytes.Length; i += 1)
{
if (thisBytes[i] == thatBytes[i])
continue;
return false;
}
return true;
}
private static short ReadLittleEndianInt16(BinaryReader binaryReader)
{
byte[] bytes = new byte[sizeof(short)];
for (int i = 0; i < sizeof(short); i += 1)
bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte();
return BitConverter.ToInt16(bytes, 0);
}
private static int ReadLittleEndianInt32(BinaryReader binaryReader)
{
byte[] bytes = new byte[sizeof(int)];
for (int i = 0; i < sizeof(int); i += 1)
bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte();
return BitConverter.ToInt32(bytes, 0);
}
private static Size? DecodeBitmap(BinaryReader binaryReader)
{
_ = binaryReader.ReadBytes(16);
int width = binaryReader.ReadInt32();
int height = binaryReader.ReadInt32();
return new Size(width, height);
}
private static Size? DecodeGif(BinaryReader binaryReader)
{
int width = binaryReader.ReadInt16();
int height = binaryReader.ReadInt16();
return new Size(width, height);
}
private static Size? DecodePng(BinaryReader binaryReader)
{
_ = binaryReader.ReadBytes(8);
int width = ReadLittleEndianInt32(binaryReader);
int height = ReadLittleEndianInt32(binaryReader);
return new Size(width, height);
}
private static Size? DecodeJfif(BinaryReader binaryReader)
{
while (binaryReader.ReadByte() == 0xff)
{
byte marker = binaryReader.ReadByte();
short chunkLength = ReadLittleEndianInt16(binaryReader);
if (marker == 0xc0)
{
_ = binaryReader.ReadByte();
int height = ReadLittleEndianInt16(binaryReader);
int width = ReadLittleEndianInt16(binaryReader);
return new Size(width, height);
}
if (chunkLength >= 0)
_ = binaryReader.ReadBytes(chunkLength - 2);
else
{
ushort uChunkLength = (ushort)chunkLength;
_ = binaryReader.ReadBytes(uChunkLength - 2);
}
}
return null;
}
private static Size? DecodeWebP(BinaryReader binaryReader)
{
_ = binaryReader.ReadUInt32(); // Size
_ = binaryReader.ReadBytes(15); // WEBP, VP8 + more
_ = binaryReader.ReadBytes(3); // SYNC
int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width
int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height
return new Size(width, height);
}
internal static Size GetDimensions(BinaryReader binaryReader, int? faceRight, int? faceBottom)
{
Size? result;
List<byte> magicBytes = [];
int[] magicBytesLengths = (from l in _ImageFormatDecoders.Keys where l.Length <= binaryReader.BaseStream.Length orderby l.Length descending select l.Length).ToArray();
if (magicBytesLengths.Length == 0)
result = null;
else
{
result = null;
if (binaryReader.BaseStream.Length == binaryReader.BaseStream.Position)
_ = binaryReader.BaseStream.Seek(0, SeekOrigin.Begin);
for (int i = 0; i < magicBytesLengths[0]; i++)
{
magicBytes.Add(binaryReader.ReadByte());
foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders)
{
if (StartsWith(magicBytes, kvPair.Key))
{
result = kvPair.Value(binaryReader);
break;
}
}
if (result is not null)
break;
}
}
if (result is null)
{
if (faceRight is null || faceBottom is null)
throw new Exception("face is null!");
result = new(faceRight.Value, faceBottom.Value);
}
return result.Value;
}
internal static Size GetDimensions(string path, int? faceRight, int? faceBottom)
{
Size result;
using FileStream fileStream = File.OpenRead(path);
using BinaryReader binaryReader = new(fileStream);
result = GetDimensions(binaryReader, faceRight, faceBottom);
return result;
}
}

View File

@ -1,5 +1,3 @@
using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IDate public interface IDate
@ -14,9 +12,6 @@ public interface IDate
public static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) => public static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) =>
XDate.GetDateTimeOriginal(exifDirectory); XDate.GetDateTimeOriginal(exifDirectory);
public static ReadOnlyCollection<DateTime> GetDateTimes(ExifDirectory exifDirectory) =>
XDate.GetDateTimes(exifDirectory);
public static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) => public static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
XDate.IsWrongYear(directoryInfo, filePath, exifDirectory); XDate.IsWrongYear(directoryInfo, filePath, exifDirectory);
@ -29,9 +24,6 @@ public interface IDate
internal DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) => internal DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
GetDateTimeOriginal(exifDirectory); GetDateTimeOriginal(exifDirectory);
internal ReadOnlyCollection<DateTime> TestStatic_GetDateTimes(ExifDirectory exifDirectory) =>
XDate.GetDateTimes(exifDirectory);
internal (bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) => internal (bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
IsWrongYear(directoryInfo, filePath, exifDirectory); IsWrongYear(directoryInfo, filePath, exifDirectory);

View File

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

View File

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

View File

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

View File

@ -0,0 +1,132 @@
using System.Drawing;
namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract class ImageHelper
{
private static bool StartsWith(byte[] thisBytes, byte[] thatBytes)
{
for (int i = 0; i < thatBytes.Length; i += 1)
{
if (thisBytes[i] != thatBytes[i])
return false;
}
return true;
}
private static short ReadLittleEndianInt16(BinaryReader binaryReader)
{
byte[] bytes = new byte[sizeof(short)];
for (int i = 0; i < sizeof(short); i += 1)
bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte();
return BitConverter.ToInt16(bytes, 0);
}
private static int ReadLittleEndianInt32(BinaryReader binaryReader)
{
byte[] bytes = new byte[sizeof(int)];
for (int i = 0; i < sizeof(int); i += 1)
bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte();
return BitConverter.ToInt32(bytes, 0);
}
private static Size DecodeBitmap(BinaryReader binaryReader)
{
_ = binaryReader.ReadBytes(16);
int width = binaryReader.ReadInt32();
int height = binaryReader.ReadInt32();
return new Size(width, height);
}
private static Size DecodeGif(BinaryReader binaryReader)
{
int width = binaryReader.ReadInt16();
int height = binaryReader.ReadInt16();
return new Size(width, height);
}
private static Size DecodePng(BinaryReader binaryReader)
{
_ = binaryReader.ReadBytes(8);
int width = ReadLittleEndianInt32(binaryReader);
int height = ReadLittleEndianInt32(binaryReader);
return new Size(width, height);
}
private static Size DecodeJfif(BinaryReader binaryReader)
{
while (binaryReader.ReadByte() == 0xff)
{
byte marker = binaryReader.ReadByte();
short chunkLength = ReadLittleEndianInt16(binaryReader);
if (marker == 0xc0)
{
_ = binaryReader.ReadByte();
int height = ReadLittleEndianInt16(binaryReader);
int width = ReadLittleEndianInt16(binaryReader);
return new Size(width, height);
}
_ = binaryReader.ReadBytes(chunkLength - 2);
}
throw new ArgumentException("Could not recognize image format.");
}
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
internal static Size GetDimensions(BinaryReader binaryReader, int? faceRight, int? faceBottom)
{
Size? result = null;
#pragma warning disable IDE0230
Dictionary<byte[], Func<BinaryReader, Size>> _ImageFormatDecoders = new()
{
{ new byte[] { 0x42, 0x4D }, DecodeBitmap },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif },
{ new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng },
{ new byte[] { 0xff, 0xd8 }, DecodeJfif },
};
#pragma warning restore IDE0230
int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length;
byte[] magicBytes = new byte[maxMagicBytesLength];
for (int i = 0; i < maxMagicBytesLength; i += 1)
{
magicBytes[i] = binaryReader.ReadByte();
foreach (KeyValuePair<byte[], Func<BinaryReader, Size>> kvPair in _ImageFormatDecoders)
{
if (!StartsWith(magicBytes, kvPair.Key))
continue;
result = kvPair.Value(binaryReader);
break;
}
if (result is not null)
break;
}
if (result is null)
{
if (faceRight is null || faceBottom is null)
throw new Exception("face is null!");
result = new(faceRight.Value, faceBottom.Value);
}
return result.Value;
}
/// <summary>
/// Gets the dimensions of an image.
/// </summary>
/// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
internal static Size GetDimensions(string path, int? faceRight, int? faceBottom)
{
Size result;
using BinaryReader binaryReader = new(File.OpenRead(path));
result = GetDimensions(binaryReader, faceRight, faceBottom);
return result;
}
}

View File

@ -47,22 +47,6 @@ internal static class MetaBase
return result; 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) internal static ReadOnlyCollection<string> GetKeywords(ExifDirectoryBase[]? exifBaseDirectories)
{ {
List<string> results = []; List<string> results = [];

View File

@ -7,7 +7,15 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract class XDate internal abstract class XDate
{ {
internal static ReadOnlyCollection<DateTime> GetDateTimes(ExifDirectory exifDirectory) internal static DateTime GetMinimum(ExifDirectory exifDirectory)
{
DateTime result;
ReadOnlyCollection<DateTime> results = GetDateTimes(exifDirectory);
result = results.Count == 0 ? DateTime.MinValue : results.Min();
return result;
}
private static ReadOnlyCollection<DateTime> GetDateTimes(ExifDirectory exifDirectory)
{ {
List<DateTime> results = []; List<DateTime> results = [];
foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
@ -114,14 +122,6 @@ internal abstract class XDate
return result; return result;
} }
internal static DateTime GetMinimum(ExifDirectory exifDirectory)
{
DateTime result;
ReadOnlyCollection<DateTime> results = GetDateTimes(exifDirectory);
result = results.Count == 0 ? DateTime.MinValue : results.Min();
return result;
}
internal static (int Season, string seasonName) GetSeason(int dayOfYear) internal static (int Season, string seasonName) GetSeason(int dayOfYear)
{ {
(int Season, string seasonName) result = dayOfYear switch (int Season, string seasonName) result = dayOfYear switch

View File

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

View File

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

View File

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

View File

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

View File

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