From 72ab5e737ef1e62ced55e1553b924dd23c22cf36 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sun, 29 Jun 2025 16:15:56 -0700 Subject: [PATCH] Removed Obsolete A_Property Methods Changed GetDimensions to handle a stream at the end and one exit Switched to using Action? over IDlibDotNet for Tick method --- .vscode/tasks.json | 12 + .../Models/Stateless/Methods/Container.cs | 32 ++- .../Models/Stateless/Methods/IContainer.cs | 16 +- Date-Group/DateGroup.cs | 2 +- Distance/Models/_E_Distance.cs | 4 +- Drag-Drop-Search/DragDropSearch.cs | 8 +- Duplicate-Search/DuplicateSearch.cs | 14 +- Instance/DlibDotNet.cs | 135 +++++++---- Map/Models/MapLogic.cs | 12 +- Metadata/Models/Stateless/Dimensions.cs | 45 ++-- Property/Models/A_Property.cs | 227 ------------------ Shared/Models/FaceFileSystem.cs | 2 +- Shared/Models/Stateless/Methods/Dimensions.cs | 149 ++++++++++++ .../Models/Stateless/Methods/ImageHelper.cs | 132 ---------- View-by-Distance-MKLink-Console.sln | 2 + 15 files changed, 330 insertions(+), 462 deletions(-) create mode 100644 Shared/Models/Stateless/Methods/Dimensions.cs delete mode 100644 Shared/Models/Stateless/Methods/ImageHelper.cs diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0b85cfb..b882ce1 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -148,6 +148,18 @@ ], "problemMatcher": "$msCompile" }, + { + "label": "buildDuplicateSearch", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/Duplicate-Search/Duplicate-Search.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, { "label": "File-Folder-Helper AOT s X Day-Helper-2025-03-20", "type": "shell", diff --git a/Container/Models/Stateless/Methods/Container.cs b/Container/Models/Stateless/Methods/Container.cs index e3a2e6d..e4e16e6 100644 --- a/Container/Models/Stateless/Methods/Container.cs +++ b/Container/Models/Stateless/Methods/Container.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless.Methods; @@ -57,7 +58,7 @@ internal abstract class Container return results; } - internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string _) + internal static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers) { int count; Models.Container[] results; @@ -101,11 +102,11 @@ internal abstract class Container throw new Exception(); } } - (string aResultsFullGroupDirectory, _) = dlibDotNet.GetResultsFullGroupDirectories(); - string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); - if (!Directory.Exists(aPropertySingletonDirectory)) - _ = Directory.CreateDirectory(aPropertySingletonDirectory); - List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter); + (_, string bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); + string bMetadataSingletonDirectory = Path.Combine(bResultsFullGroupDirectory, propertyConfiguration.ResultSingleton); + if (!Directory.Exists(bMetadataSingletonDirectory)) + _ = Directory.CreateDirectory(bMetadataSingletonDirectory); + List filePairs = GetFilePairs(propertyConfiguration, bMetadataSingletonDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById, directorySearchFilter, dlibDotNet.Tick); foreach (FilePair filePair in filePairs) { if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryFullPath, out items)) @@ -126,24 +127,24 @@ internal abstract class Container return (filePairs.Count, results.ToArray()); } - private static List GetFilePairs(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary? exifDirectoriesById, string directorySearchFilter) + private static List GetFilePairs(IPropertyConfiguration propertyConfiguration, string bMetadataSingletonDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary? exifDirectoriesById, string directorySearchFilter, Action? tick) { List results = []; const string extension = ".json"; ReadOnlyCollection filePairs; - string jsonGroupDirectory = aPropertySingletonDirectory; + string jsonGroupDirectory = bMetadataSingletonDirectory; int maxDegreeOfParallelism = Environment.ProcessorCount; int filesCollectionDirectoryLength = filesCollectionDirectory.Length; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; filePairs = IFilePair.GetFilePairs(propertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection); _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => - ParallelFor(dlibDotNet, propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, exifDirectoriesById, filePairs[i], results)); + ParallelFor(propertyConfiguration, jsonGroupDirectory, extension, keyValuePairs, splatNineIdentifiers, filesCollectionDirectoryLength, exifDirectoriesById, filePairs[i], results, tick)); return results; } - private static void ParallelFor(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, int rootDirectoryLength, ReadOnlyDictionary? exifDirectoriesById, Shared.Models.FilePair filePair, List results) + private static void ParallelFor(IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, int rootDirectoryLength, ReadOnlyDictionary? exifDirectoriesById, Shared.Models.FilePair filePair, List results, Action? tick) { - dlibDotNet?.Tick(); + tick?.Invoke(); bool abandoned = false; FileHolder sourceDirectoryFileHolder; if (exifDirectoriesById is null || filePair.FilePath.Id is null || !exifDirectoriesById.TryGetValue(filePair.FilePath.Id.Value, out ExifDirectory? exifDirectory)) @@ -208,7 +209,14 @@ internal abstract class Container string jsonFileName = $"{fileName}{extension}"; string fullFileName = Path.Combine(directory, jsonFileName); MoveIf(jsonFileName, cei, directory, fullFileName); - sourceDirectoryFileHolder = IFileHolder.Get(fullFileName); + FileInfo fileInfo = new(fullFileName); + if (exifDirectory is not null && !fileInfo.Exists) + { + string json = JsonSerializer.Serialize(exifDirectory, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); + fileInfo.Refresh(); + } + sourceDirectoryFileHolder = IFileHolder.Get(fileInfo); } if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePair.FilePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) { diff --git a/Container/Models/Stateless/Methods/IContainer.cs b/Container/Models/Stateless/Methods/IContainer.cs index d5169ac..0bba1cf 100644 --- a/Container/Models/Stateless/Methods/IContainer.cs +++ b/Container/Models/Stateless/Methods/IContainer.cs @@ -8,8 +8,8 @@ namespace View_by_Distance.Container.Models.Stateless.Methods; public interface IContainer { - public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => - Container.GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); + public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers) => + Container.GetContainers(propertyConfiguration, splatNineIdentifiers); public static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) => Container.GetContainerDateTimes(items); @@ -17,8 +17,8 @@ public interface IContainer public static ReadOnlyCollection GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => Container.GetValidImageItems(propertyConfiguration, container); - public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, null, aPropertySingletonDirectory); + public static (int, Models.Container[]) GetContainers(IPropertyConfiguration propertyConfiguration) => + GetContainers(propertyConfiguration, splatNineIdentifiers: null); public static List GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => Container.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); @@ -38,8 +38,8 @@ public interface IContainer internal ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, Models.Container container) => GetValidImageItems(propertyConfiguration, container); - internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, aPropertySingletonDirectory); + internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration) => + GetContainers(propertyConfiguration); internal List TestStatic_GetFilteredDistinctIds(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) => GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); @@ -50,8 +50,8 @@ public interface IContainer internal ReadOnlyCollection TestStatic_GetValidImageItems(IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) => GetValidImageItems(propertyConfiguration, containers, distinctItems, filterItems); - internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers, string aPropertySingletonDirectory) => - GetContainers(propertyConfiguration, splatNineIdentifiers, aPropertySingletonDirectory); + internal (int, Models.Container[]) TestStatic_GetContainers(IPropertyConfiguration propertyConfiguration, ReadOnlyDictionary? splatNineIdentifiers) => + GetContainers(propertyConfiguration, splatNineIdentifiers); internal ReadOnlyCollection TestStatic_GetContainers(IDlibDotNet dlibDotNet, IPropertyConfiguration propertyConfiguration, string facesFileNameExtension, string facesHiddenFileNameExtension, string eDistanceContentDirectory, string filesCollectionDirectory, ReadOnlyDictionary>? keyValuePairs, ReadOnlyDictionary? splatNineIdentifiers, ReadOnlyCollection> filePathsCollection, ReadOnlyDictionary exifDirectoriesById) => GetContainers(dlibDotNet, propertyConfiguration, facesFileNameExtension, facesHiddenFileNameExtension, eDistanceContentDirectory, filesCollectionDirectory, keyValuePairs, splatNineIdentifiers, filePathsCollection, exifDirectoriesById); diff --git a/Date-Group/DateGroup.cs b/Date-Group/DateGroup.cs index 9460b51..b345ce8 100644 --- a/Date-Group/DateGroup.cs +++ b/Date-Group/DateGroup.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Phares.Shared; using System.Globalization; diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index 634c03c..2cd77fe 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -571,14 +571,14 @@ public partial class E_Distance : IDistance return results.AsReadOnly(); } - public static ReadOnlyCollection GetMatrixLocationContainers(IDlibDotNet dlibDotNet, Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary> mappedWithEncoding, List preFiltered, DistanceLimits distanceLimits, List postFiltered) + public static ReadOnlyCollection GetMatrixLocationContainers(Configuration mapConfiguration, long ticks, MapLogic mapLogic, ReadOnlyDictionary> mappedWithEncoding, List preFiltered, DistanceLimits distanceLimits, List postFiltered, Action? tick) { List results = []; ReadOnlyCollection locationContainers; ReadOnlyCollection readOnlyLocationContainers = GetReadOnlyLocationContainer(mappedWithEncoding, postFiltered); foreach (LocationContainer locationContainer in postFiltered) { - dlibDotNet.Tick(); + tick?.Invoke(); locationContainers = FaceRecognition.GetLocationContainers(mapConfiguration.FaceDistancePermyriad, readOnlyLocationContainers, locationContainer); foreach (LocationContainer item in locationContainers) { diff --git a/Drag-Drop-Search/DragDropSearch.cs b/Drag-Drop-Search/DragDropSearch.cs index abf2426..f8ec7ac 100644 --- a/Drag-Drop-Search/DragDropSearch.cs +++ b/Drag-Drop-Search/DragDropSearch.cs @@ -3,7 +3,6 @@ using Phares.Shared; using System.Diagnostics; using System.Reflection; using View_by_Distance.Drag_Drop.Models; -using View_by_Distance.Property.Models; using View_by_Distance.Resize.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; @@ -20,7 +19,7 @@ public partial class DragDropSearch : Form private readonly IsEnvironment _IsEnvironment; private readonly Dictionary _IdToItem; private readonly string _ResizeFileNameExtension; - private readonly Models.Configuration _Configuration; + private readonly Configuration _Configuration; private readonly IConfigurationRoot _ConfigurationRoot; private readonly Property.Models.Configuration _PropertyConfiguration; @@ -30,8 +29,8 @@ public partial class DragDropSearch : Form _IdToItem = []; AppSettings appSettings; string workingDirectory; + Configuration configuration; IsEnvironment isEnvironment; - Models.Configuration configuration; IConfigurationRoot configurationRoot; Property.Models.Configuration propertyConfiguration; Assembly assembly = Assembly.GetExecutingAssembly(); @@ -93,8 +92,7 @@ public partial class DragDropSearch : Form 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); + (_, containers) = View_by_Distance.Container.Models.Stateless.Methods.IContainer.GetContainers(_Configuration.PropertyConfiguration); List collection = Program.GetItemCollection(_Configuration, containers); foreach (Item item in collection) { diff --git a/Duplicate-Search/DuplicateSearch.cs b/Duplicate-Search/DuplicateSearch.cs index a897783..210fbd8 100644 --- a/Duplicate-Search/DuplicateSearch.cs +++ b/Duplicate-Search/DuplicateSearch.cs @@ -9,6 +9,7 @@ using View_by_Distance.Duplicate.Search.Models; using View_by_Distance.Property.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; +using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Duplicate.Search; @@ -71,8 +72,7 @@ public class DuplicateSearch 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); + (f, containers) = Container.Models.Stateless.Methods.IContainer.GetContainers(configuration); } return containers; } @@ -101,12 +101,15 @@ public class DuplicateSearch private static Dictionary> GetIdToCollection(string argZero, Configuration configuration, bool argZeroIsConfigurationRootDirectory, Container.Models.Container[] containers, string destinationRoot, List preloadIds) { Dictionary> results = []; + string? model; string directory; const int zero = 0; + DateTime? dateTime; FileHolder resizedFileHolder; DateTime[] containerDateTimes; MappingFromItem? mappingFromItem; List? collection; + ReadOnlyCollection keywords; ReadOnlyCollection validImageItems; const string duplicates = "-Duplicate(s)"; if (containers.Length != 0) @@ -145,12 +148,15 @@ public class DuplicateSearch mappingFromItem = collection[zero]; if (mappingFromItem is not null) { + dateTime = IDate.GetDateTimeOriginal(item.ExifDirectory); + keywords = IMetaBase.GetKeywords(item.ExifDirectory?.ExifBaseDirectories); + model = Metadata.Models.Stateless.Methods.IMetadata.GetModel(item.ExifDirectory); 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); + collection[0] = new(mappingFromItem.ContainerDateTimes, dateTime, mappingFromItem.Id, mappingFromItem.IsArchive, mappingFromItem.FilePath, mappingFromItem.IsWrongYear, keywords, mappingFromItem.MinimumDateTime, 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); + mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); collection.Add(mappingFromItem); } } diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 26d9e84..f889b5c 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -67,6 +67,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable _Exceptions = []; _Console = console; _AppSettings = appSettings; + IDlibDotNet dlibDotNet = this; _IsEnvironment = isEnvironment; long ticks = DateTime.Now.Ticks; _JLinkResolvedDirectories = []; @@ -128,8 +129,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); message = $") Building People Collection - {totalSeconds} total second(s)"; - using ProgressBar progressBar = new(1, message, _ProgressBarOptions); - progressBar.Tick(); + dlibDotNet.ConstructProgressBar(1, message); + dlibDotNet.Tick(); string peopleRootDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(propertyConfiguration, nameof(A2_People)); string? rootResultsDirectory = Path.GetDirectoryName(Path.GetDirectoryName(peopleRootDirectory)) ?? throw new Exception(); _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(Path.Combine(peopleRootDirectory, propertyConfiguration.ResultSingleton)); @@ -196,7 +197,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string[] changesFrom = [nameof(A_Property)]; List> subFileTuples = []; 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.ExifDirectory is null || item.ExifDirectory.FilePath.Id is null) + throw new Exception(); + if (!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) { @@ -327,12 +330,13 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { int result = 0; int exceptionsCount = 0; + IDlibDotNet dlibDotNet = this; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; DateTime[] containerDateTimes = Container.Models.Stateless.Methods.IContainer.GetContainerDateTimes(filteredItems); string focusRelativePath = Path.GetFullPath(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, _Configuration.FocusDirectory)); bool? isFocusRelativePath = string.IsNullOrEmpty(_Configuration.FocusDirectory) ? null : container.SourceDirectory.StartsWith(focusRelativePath); bool anyPropertiesChangedForX = _Configuration.PropertyConfiguration.PropertiesChangedForProperty || _Configuration.PropertiesChangedForDistance || _Configuration.PropertiesChangedForFaces || _Configuration.PropertiesChangedForIndex || _Configuration.PropertiesChangedForMetadata || _Configuration.PropertiesChangedForResize; - using ProgressBar progressBar = new(filteredItems.Count, message, _ProgressBarOptions); + dlibDotNet.ConstructProgressBar(filteredItems.Count, message); _ = Parallel.For(0, filteredItems.Count, parallelOptions, (i, state) => { try @@ -351,7 +355,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable containerDateTimes, isFocusRelativePath); if (!anyPropertiesChangedForX && (i == 0 || sourceDirectoryChanges.Count > 0)) - progressBar.Tick(); + dlibDotNet.Tick(); } catch (Exception) { @@ -450,8 +454,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); message = $"{totalSeconds} total second(s) - {outputResolution} - ### [###] / {readOnlyContainers.Count:000} - {total} / {count} total - <> - total not mapped {totalNotMapped:000000}"; - using ProgressBar progressBar = new(1, message, _ProgressBarOptions); - progressBar.Tick(); + dlibDotNet.ConstructProgressBar(1, message); + dlibDotNet.Tick(); } } @@ -607,7 +611,11 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { ExifDirectory? result; if (filePair.Match is null) - result = null; + { + try + { result = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePair.FilePath); } + catch (Exception) { result = null; } + } else { string json = File.ReadAllText(filePair.Match.FullName); @@ -1003,9 +1011,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable return results.AsReadOnly(); } - private void ParallelFor(IDlibDotNet dlibDotNet, FilePair filePair, Dictionary results) + private void ParallelFor(FilePair filePair, Dictionary results, Action? tick) { - dlibDotNet?.Tick(); + tick?.Invoke(); if (filePair.FilePath.Id is null || results.ContainsKey(filePair.FilePath.Id.Value)) return; ExifDirectory? exifDirectory = GetExifDirectory(filePair); @@ -1087,7 +1095,6 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable const string directorySearchFilter = "*"; bool configurationOutputResolutionsHas = false; ReadOnlyDictionary> personKeyToIds; - (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); string[] checkDirectories = [ Path.Combine(_Configuration.PropertyConfiguration.RootDirectory, "Ancestry"), @@ -1096,6 +1103,7 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable ]; bool runToDoCollectionFirst = GetRunToDoCollectionFirst(_Configuration, ticks, checkDirectories); (aResultsFullGroupDirectory, bResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(); + (int season, string seasonName) = Shared.Models.Stateless.Methods.IProperty.GetSeason(dateTime.DayOfYear); Shared.Models.Stateless.Methods.IPath.ChangeDateForEmptyDirectories(_Configuration.PropertyConfiguration.RootDirectory, ticks); a2PeopleContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A2_People), "([])"); eDistanceContentDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(E_Distance), _Configuration.PropertyConfiguration.ResultContent); @@ -1145,30 +1153,25 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable { if (outputResolution.Any(char.IsNumber)) continue; - Dictionary exifDirectoriesById = []; + bool filesCollectionCountIsOne = record?.FilesCollectionCountIsOne ?? false; (cResultsFullGroupDirectory, _, _, _) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - string? filesCollectionRootDirectory = Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); + ReadOnlyDictionary exifDirectoriesById = record?.ExifDirectoriesById ?? new(new Dictionary()); + string? filesCollectionRootDirectory = record?.FilesCollectionRootDirectory ?? Path.Combine(cResultsFullGroupDirectory, _Configuration.PropertyConfiguration.ResultContent); ReadOnlyCollection>? filePathsCollection = IDirectory.GetFilePathCollections(_Configuration.PropertyConfiguration, directorySearchFilter, fileSearchFilter, filesCollectionRootDirectory, useIgnoreExtensions: true, useCeilingAverage: true); record = new(FilesCollectionRootDirectory: filesCollectionRootDirectory, - FilesCollectionCountIsOne: false, + FilesCollectionCountIsOne: filesCollectionCountIsOne, FilePathsCollection: filePathsCollection, - ExifDirectoriesById: new(exifDirectoriesById), - IdToFilePaths: null, - SplatNineIdentifiers: null); + ExifDirectoriesById: exifDirectoriesById, + IdToFilePaths: record?.IdToFilePaths, + SplatNineIdentifiers: record?.SplatNineIdentifiers); break; } } if (string.IsNullOrEmpty(record?.FilesCollectionRootDirectory) || record.FilePathsCollection.Count == 0) throw new NullReferenceException(nameof(record.FilePathsCollection)); - foreach (string checkDirectory in checkDirectories) - { - seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}"); - if (!Directory.Exists(seasonDirectory)) - _ = Directory.CreateDirectory(seasonDirectory); - } int count = record.FilePathsCollection.Select(l => l.Count).Sum(); message = $") Building Container(s) - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - _ProgressBar = new(count, message, _ProgressBarOptions); + dlibDotNet.ConstructProgressBar(count, message); ReadOnlyCollection readOnlyContainers = Container.Models.Stateless.Methods.IContainer.GetContainers(dlibDotNet, _Configuration.PropertyConfiguration, @@ -1180,20 +1183,18 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable record.SplatNineIdentifiers, record.FilePathsCollection, record.ExifDirectoriesById); - _ProgressBar.Dispose(); + Verify(argZero, readOnlyContainers); + foreach (string checkDirectory in checkDirectories) + { + seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}"); + if (!Directory.Exists(seasonDirectory)) + _ = Directory.CreateDirectory(seasonDirectory); + } mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); DeleteContinueFiles(personContainers); if (!runToDoCollectionFirst) MapFaceFileLogic(ticks, personContainers, mapLogic, a2PeopleContentDirectory, eDistanceContentDirectory); - FullDoWork(argZero, - propertyRoot, - ticks, - fPhotoPrismSingletonDirectory, - count, - metadata, - record, - readOnlyContainers, - mapLogic); + FullDoWork(argZero, propertyRoot, ticks, fPhotoPrismSingletonDirectory, count, metadata, record, readOnlyContainers, mapLogic); ReadOnlyCollection distinctValidImageItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); if (_Configuration.LookForAbandoned) { @@ -1201,10 +1202,9 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable string d2ResultsFullGroupDirectory; foreach (string outputResolution in _Configuration.OutputResolutions) { - _ProgressBar = new(5, nameof(mapLogic.LookForAbandoned), _ProgressBarOptions); + dlibDotNet.ConstructProgressBar(5, nameof(mapLogic.LookForAbandoned)); (cResultsFullGroupDirectory, _, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory) = dlibDotNet.GetResultsFullGroupDirectories(outputResolution); - mapLogic.LookForAbandoned(dlibDotNet, _Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory); - _ProgressBar.Dispose(); + mapLogic.LookForAbandoned(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers, cResultsFullGroupDirectory, dResultsFullGroupDirectory, d2ResultsFullGroupDirectory, dlibDotNet.Tick); } } _Distance.Clear(); @@ -1253,12 +1253,49 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable } } + private void Verify(string argZero, ReadOnlyCollection readOnlyContainers) + { + int count = 0; + List items = []; + Container.Models.Container container; + ReadOnlyCollection filteredItems; + foreach (string outputResolution in _Configuration.OutputResolutions) + { + for (int i = 0; i < readOnlyContainers.Count; i++) + { + container = readOnlyContainers[i]; + if (container.Items.Count == 0) + continue; + if (!_ArgZeroIsConfigurationRootDirectory && !container.SourceDirectory.StartsWith(argZero)) + continue; + filteredItems = Container.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, container); + if (filteredItems.Count == 0) + continue; + foreach (Item item in filteredItems) + { + count++; + if (item.ExifDirectory is null || item.ExifDirectory.FilePath.Id is null) + { + items.Add(item); + continue; + } + if (!item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) + { + items.Add(item); + continue; + } + } + } + } + if (items.Count > 0) + throw new Exception($"{items.Count} item(s) of {count} item(s) are not setup!"); + } + private Record GetFilesCollectionThenCopyOrMove(IDlibDotNet dlibDotNet, long ticks, string fileSearchFilter, string directorySearchFilter, string bResultsFullGroupDirectory, string outputResolution) { Record result; int count; string message; - ProgressBar progressBar; const string extension = ".json"; ReadOnlyCollection filePairs; int maxDegreeOfParallelism = Environment.ProcessorCount; @@ -1276,24 +1313,23 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable count = filePathsCollection.Select(l => l.Count).Sum(); filePairs = IFilePair.GetFilePairs(_Configuration.PropertyConfiguration, directorySearchFilter, extension, jsonGroupDirectory, filePathsCollection); message = $") Preloading ExifDirectory Dictionary - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - progressBar = new(count, message, _ProgressBarOptions); - _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(dlibDotNet, filePairs[i], exifDirectoriesById)); - progressBar.Dispose(); + dlibDotNet.ConstructProgressBar(count, message); + _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(filePairs[i], exifDirectoriesById, dlibDotNet.Tick)); + if (exifDirectoriesById.Count == 0) + throw new Exception("No exif directories were found!"); count = filePathsCollection.Select(l => l.Count).Sum(); bool filesCollectionCountIsOne = GetFilesCollectionCountIsOne(filePathsCollection); message = $") Selecting for ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - progressBar = new(count, message, _ProgressBarOptions); - (string[] distinctDirectories, List<(FilePath, string)> toDoCollection) = IDirectory.GetToDoCollection(_Configuration.PropertyConfiguration, filePathsCollection, fileGroups, exifDirectoriesById, () => progressBar.Tick()); - progressBar.Dispose(); + dlibDotNet.ConstructProgressBar(count, message); + (string[] distinctDirectories, List<(FilePath, string)> toDoCollection) = IDirectory.GetToDoCollection(_Configuration.PropertyConfiguration, filePathsCollection, fileGroups, exifDirectoriesById, dlibDotNet.Tick); foreach (string distinctDirectory in distinctDirectories) { if (!Directory.Exists(distinctDirectory)) _ = Directory.CreateDirectory(distinctDirectory); } message = $") Copying to ## pattern directory - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - progressBar = new(count, message, _ProgressBarOptions); - _ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, () => progressBar.Tick()); - progressBar.Dispose(); + dlibDotNet.ConstructProgressBar(count, message); + _ = IDirectory.CopyOrMove(toDoCollection, move: false, moveBack: false, dlibDotNet.Tick); ReadOnlyDictionary> idToFilePaths = FilePath.GetKeyValuePairs(filePathsCollection); ReadOnlyDictionary splatNineIdentifiers = GetSplatNineIdentifiersAndHideSplatNine(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, idToFilePaths); result = new(ExifDirectoriesById: new(exifDirectoriesById), @@ -1362,9 +1398,8 @@ public partial class DlibDotNet : IDlibDotNet, IDisposable else { string message = $") Building Matrix - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)"; - _ProgressBar = new(postFiltered.Count, message, _ProgressBarOptions); - ReadOnlyCollection matrix = E_Distance.GetMatrixLocationContainers(dlibDotNet, _MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered); - _ProgressBar.Dispose(); + dlibDotNet.ConstructProgressBar(postFiltered.Count, message); + ReadOnlyCollection matrix = E_Distance.GetMatrixLocationContainers(_MapConfiguration, ticks, mapLogic, mappedWithEncoding, preFiltered, distanceLimits, postFiltered, dlibDotNet.Tick); ReadOnlyDictionary onlyOne = GetOnlyOne(distanceLimits, matrix); if (onlyOne.Count == 0) results = []; diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 08ecd54..1f4bf09 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -921,16 +921,16 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return result; } - public void LookForAbandoned(IDlibDotNet dlibDotNet, Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory) + public void LookForAbandoned(Property.Models.Configuration propertyConfiguration, string bResultsFullGroupDirectory, ReadOnlyCollection readOnlyContainers, string cResultsFullGroupDirectory, string dResultsFullGroupDirectory, string d2ResultsFullGroupDirectory, Action? tick) { string[] directories; string? directoryName; List distinctFilteredIds = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctIds(propertyConfiguration, readOnlyContainers); LookForAbandoned(propertyConfiguration, distinctFilteredIds); - dlibDotNet.Tick(); + tick?.Invoke(); List distinctFilteredFileNameFirstSegments = Container.Models.Stateless.Methods.IContainer.GetFilteredDistinctFileNameFirstSegments(propertyConfiguration, readOnlyContainers); Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, bResultsFullGroupDirectory, distinctFilteredFileNameFirstSegments); - dlibDotNet.Tick(); + tick?.Invoke(); directories = Directory.GetDirectories(cResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { @@ -939,7 +939,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic continue; Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } - dlibDotNet.Tick(); + tick?.Invoke(); directories = Directory.GetDirectories(dResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { @@ -948,7 +948,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic continue; Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } - dlibDotNet.Tick(); + tick?.Invoke(); directories = Directory.GetDirectories(d2ResultsFullGroupDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { @@ -957,7 +957,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic continue; Stateless.LookForAbandonedLogic.LookForAbandoned(propertyConfiguration, distinctFilteredFileNameFirstSegments, directory, directoryName); } - dlibDotNet.Tick(); + tick?.Invoke(); } private void LookForAbandoned(Property.Models.Configuration propertyConfiguration, List distinctFilteredIds) diff --git a/Metadata/Models/Stateless/Dimensions.cs b/Metadata/Models/Stateless/Dimensions.cs index fd53715..e156efb 100644 --- a/Metadata/Models/Stateless/Dimensions.cs +++ b/Metadata/Models/Stateless/Dimensions.cs @@ -8,18 +8,18 @@ internal static class Dimensions #pragma warning disable IDE0230 private static readonly Dictionary> _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 }, - { new byte[] { 0xff, 0xd8 }, DecodeJfif }, - { new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP }, }; #pragma warning restore IDE0230 - private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) + private static bool StartsWith(List thisBytes, byte[] thatBytes) { - for (int i = 0; i < thatBytes.Length; i += 1) + for (int i = 0; i < thisBytes.Count && i < thatBytes.Length; i += 1) { if (thisBytes[i] == thatBytes[i]) continue; @@ -103,24 +103,41 @@ internal static class Dimensions internal static Size? GetDimensions(BinaryReader binaryReader) { - int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; - byte[] magicBytes = new byte[maxMagicBytesLength]; - for (int i = 0; i < maxMagicBytesLength; i += 1) + Size? result; + List 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 { - magicBytes[i] = binaryReader.ReadByte(); - foreach (KeyValuePair> kvPair in _ImageFormatDecoders) + result = null; + if (binaryReader.BaseStream.Length == binaryReader.BaseStream.Position) + _ = binaryReader.BaseStream.Seek(0, SeekOrigin.Begin); + for (int i = 0; i < magicBytesLengths[0]; i++) { - if (StartsWith(magicBytes, kvPair.Key)) - return kvPair.Value(binaryReader); + magicBytes.Add(binaryReader.ReadByte()); + foreach (KeyValuePair> kvPair in _ImageFormatDecoders) + { + if (StartsWith(magicBytes, kvPair.Key)) + { + result = kvPair.Value(binaryReader); + break; + } + } + if (result is not null) + break; } } - return null; + return result; } internal static Size? GetDimensions(string path) { - using BinaryReader binaryReader = new(File.OpenRead(path)); - return GetDimensions(binaryReader); + Size? result; + using FileStream fileStream = File.OpenRead(path); + using BinaryReader binaryReader = new(fileStream); + result = GetDimensions(binaryReader); + return result; } } \ No newline at end of file diff --git a/Property/Models/A_Property.cs b/Property/Models/A_Property.cs index dd3826e..eb4094a 100644 --- a/Property/Models/A_Property.cs +++ b/Property/Models/A_Property.cs @@ -1,4 +1,3 @@ -using ShellProgressBar; using System.Collections.ObjectModel; using System.Text; using System.Text.Json; @@ -66,40 +65,6 @@ public class A_Property converted: false)); } - [Obsolete("Use ExifDirectory")] - public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata metadata, int t, Container.Models.Container[] containers) - { - int total = 0; - string message; - int totalSeconds; - bool anyNullOrNoIsUniqueFileName; - List exceptions = []; - Container.Models.Container container; - int containersLength = containers.Length; - const string outputResolution = "Original"; - List> sourceDirectoryChanges = []; - string propertyRoot = IResult.GetResultsGroupDirectory(_PropertyConfiguration, nameof(A_Property)); - for (int i = 0; i < containers.Length; i++) - { - container = containers[i]; - if (container.Items.Count == 0) - continue; - sourceDirectoryChanges.Clear(); - if (container.Items.Count == 0) - continue; - anyNullOrNoIsUniqueFileName = container.Items.Any(l => !l.IsUniqueFileName); - SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName); - totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); - message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; - SavePropertyParallelWork(_MaxDegreeOfParallelism, metadata, exceptions, sourceDirectoryChanges, container, container.Items, message); - if (exceptions.Count == container.Items.Count) - throw new Exception(string.Concat("All in [", container.SourceDirectory, "]failed!")); - if (exceptions.Count != 0) - _ExceptionsDirectories.Add(container.SourceDirectory); - total += container.Items.Count; - } - } - private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName) { _AngleBracketCollection.Clear(); @@ -112,180 +77,6 @@ public class A_Property SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); } - [Obsolete("Use ExifDirectory")] - private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata metadata, List exceptions, List> sourceDirectoryChanges, Container.Models.Container container, ReadOnlyCollection items, string message) - { - List> 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> collection; - SavePropertyParallelForWork(metadata, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]); - if (i == 0 || sourceDirectoryChanges.Count != 0) - progressBar.Tick(); - lock (sourceDirectoryFileTuples) - collection = (from l in sourceDirectoryFileTuples where l.Item2 > dateTime select l).ToList(); - lock (sourceDirectoryChanges) - sourceDirectoryChanges.AddRange(collection); - } - catch (Exception ex) - { - lock (exceptions) - exceptions.Add(ex); - } - }); - } - - [Obsolete("Use ExifDirectory")] - private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata metadata, string sourceDirectory, List> sourceDirectoryFileTuples, List> sourceDirectoryChanges, Item item) - { - Shared.Models.Property property; - List parseExceptions = []; - bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered); - string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.FilePath.NameWithoutExtension}{item.FilePath.ExtensionLowered}"); - if (item.IsValidImageFormatExtension && item.FilePath.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.FilePath.FullName != filteredSourceDirectoryFileExtensionLowered) - File.Move(item.FilePath.FullName, filteredSourceDirectoryFileExtensionLowered); - if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.ExifDirectory is null) - { - property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension); - lock (sourceDirectoryChanges) - sourceDirectoryChanges.Add(new Tuple(nameof(A_Property), DateTime.Now)); - lock (item) - item.Update(null); - } - } - - [Obsolete("Use ExifDirectory")] - private Shared.Models.Property GetImageProperty(Shared.Models.Methods.IMetadata metadata, Item item, List> sourceDirectoryFileTuples, List 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 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.ExifDirectory is not null) - result = null; - 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(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(nameof(A_Property), DateTime.Now)); - else - { - File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); - fileInfo.Refresh(); - sourceDirectoryFileTuples.Add(new Tuple(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(nameof(A_Property), fileInfo.CreationTime)); - } - } - return result; - } - private static void MoveIf(string fileName, CombinedEnumAndIndex cei, string directory, FileInfo fileInfo) { string[] segments = directory.Split(cei.Combined); @@ -305,22 +96,4 @@ public class A_Property } } - [Obsolete("Use ExifDirectory")] - public Shared.Models.Property GetProperty(Shared.Models.Methods.IMetadata metadata, Item item, List> sourceDirectoryFileTuples, List 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; - } - } \ No newline at end of file diff --git a/Shared/Models/FaceFileSystem.cs b/Shared/Models/FaceFileSystem.cs index 0f529bc..776ab2e 100644 --- a/Shared/Models/FaceFileSystem.cs +++ b/Shared/Models/FaceFileSystem.cs @@ -76,7 +76,7 @@ public class FaceFileSystem : FileSystem, Properties.IFaceFileSystem } else { - System.Drawing.Size size = Stateless.Methods.ImageHelper.GetDimensions(fileInfo.FullName, faceRight, faceBottom); + System.Drawing.Size size = Stateless.Methods.Dimensions.GetDimensions(fileInfo.FullName, faceRight, faceBottom); imageWidth = size.Width; imageHeight = size.Height; } diff --git a/Shared/Models/Stateless/Methods/Dimensions.cs b/Shared/Models/Stateless/Methods/Dimensions.cs new file mode 100644 index 0000000..05d3535 --- /dev/null +++ b/Shared/Models/Stateless/Methods/Dimensions.cs @@ -0,0 +1,149 @@ +using System.Drawing; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal static class Dimensions +{ + +#pragma warning disable IDE0230 + private static readonly Dictionary> _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 thisBytes, byte[] thatBytes) + { + for (int i = 0; i < thisBytes.Count && 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 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> 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; + } + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ImageHelper.cs b/Shared/Models/Stateless/Methods/ImageHelper.cs deleted file mode 100644 index e4493f2..0000000 --- a/Shared/Models/Stateless/Methods/ImageHelper.cs +++ /dev/null @@ -1,132 +0,0 @@ -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."); - } - - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognized format. - internal static Size GetDimensions(BinaryReader binaryReader, int? faceRight, int? faceBottom) - { - Size? result = null; -#pragma warning disable IDE0230 - Dictionary> _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> 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; - } - - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognized format. - 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; - } - -} \ No newline at end of file diff --git a/View-by-Distance-MKLink-Console.sln b/View-by-Distance-MKLink-Console.sln index 913b79f..369e024 100644 --- a/View-by-Distance-MKLink-Console.sln +++ b/View-by-Distance-MKLink-Console.sln @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Search", "Drag-Dr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Set-Property-Item", "Drag-Drop-Set-Property-Item\Drag-Drop-Set-Property-Item.csproj", "{BFF75D8C-48E6-4B84-B480-3E5A4F9B2DD8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicate-Search", "Duplicate-Search\Duplicate-Search.csproj", "{48E87D9B-B802-467A-BDC7-E86F7FD01D5C}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Face", "Face\Face.csproj", "{A12E19E5-59C0-40D4-B807-DF1334D4906D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FaceParts", "FaceParts\FaceParts.csproj", "{919525B1-60BA-40C6-BA66-6F7F4C526E01}"