using Humanizer; using ShellProgressBar; using System.Text; using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; using WindowsShortcutFactory; namespace View_by_Distance.Map.Models; public class MapLogic { protected readonly Dictionary> _SkipCollection; protected readonly List _NotMappedPersonContainers; protected readonly Dictionary _PersonKeyToPersonContainer; protected Dictionary? _PersonKeyToRanges; protected readonly Dictionary> _IdThenNormalizedRectangleToPersonContainers; public Dictionary KeyValuePairs => throw new NotImplementedException(); public Dictionary IndicesFromNew => throw new NotImplementedException(); private readonly long _Ticks; private readonly Serilog.ILogger? _Log; private readonly int _MaxDegreeOfParallelism; private readonly Configuration? _Configuration; private readonly string _EDistanceContentTicksDirectory; private readonly Shared.Models.Methods.IMapLogicSupport? _MapLogicSupport; private readonly Shared.Models.Properties.IPropertyConfiguration _PropertyConfiguration; public MapLogic(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration? configuration, long ticks, PersonContainer[] personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory, Shared.Models.Methods.IMapLogicSupport? mapLogicSupport) { _Ticks = ticks; _Configuration = configuration; _MapLogicSupport = mapLogicSupport; _Log = Serilog.Log.ForContext(); _PropertyConfiguration = propertyConfiguration; _MaxDegreeOfParallelism = maxDegreeOfParallelism; if (_Log is null) { } if (propertyConfiguration.VerifyToSeason is null || !propertyConfiguration.VerifyToSeason.Any()) throw new Exception(); if (_MaxDegreeOfParallelism == 0) { } string json; string fullPath; List>? collection; Dictionary> skipCollection = new(); List notMappedPersonContainers = new(); Dictionary personKeyToPersonContainer = new(); string? rootDirectoryParent = Path.GetDirectoryName(propertyConfiguration.RootDirectory); string eDistanceContentTicksDirectory = Path.Combine(eDistanceContentDirectory, $"({ticks})"); Dictionary> idThenNormalizedRectangleToPersonContainers = new(); for (int i = 1; i < 5; i++) _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); _ = IPath.DeleteEmptyDirectories(eDistanceContentDirectory); if (string.IsNullOrEmpty(rootDirectoryParent)) throw new NullReferenceException(nameof(rootDirectoryParent)); if (!Directory.Exists(eDistanceContentDirectory)) _ = Directory.CreateDirectory(eDistanceContentDirectory); if (!Directory.Exists(eDistanceContentTicksDirectory)) _ = Directory.CreateDirectory(eDistanceContentTicksDirectory); if (configuration is not null && mapLogicSupport is not null) { List personContainerCollection = new(personContainers); Stateless.MapLogic.Set(propertyConfiguration, configuration, ticks, personContainerCollection, a2PeopleSingletonDirectory, eDistanceContentDirectory, mapLogicSupport, personKeyToPersonContainer, notMappedPersonContainers, skipCollection, idThenNormalizedRectangleToPersonContainers); if (personContainerCollection.Count == personContainers.Length) throw new NotSupportedException(); } foreach (string propertyContentCollectionFile in propertyConfiguration.PropertyContentCollectionFiles) { fullPath = Path.GetFullPath(string.Concat(rootDirectoryParent, propertyContentCollectionFile)); if (fullPath.Contains(propertyConfiguration.RootDirectory)) continue; if (!File.Exists(fullPath)) continue; json = File.ReadAllText(fullPath); collection = JsonSerializer.Deserialize>>(json); if (collection is null) throw new NullReferenceException(nameof(collection)); } _SkipCollection = skipCollection; _PersonKeyToPersonContainer = personKeyToPersonContainer; _EDistanceContentTicksDirectory = eDistanceContentTicksDirectory; _IdThenNormalizedRectangleToPersonContainers = idThenNormalizedRectangleToPersonContainers; _NotMappedPersonContainers = notMappedPersonContainers.OrderByDescending(l => l.Key).ToList(); } public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); return result; } public (Dictionary, int) AddToMapping(Mapping[] mappingCollection) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); int result = 0; long personKey; const int zero = 0; string mappingSegmentB; PersonBirthday personBirthday; PersonContainer[]? collection; List personContainers = new(); Dictionary personKeyToCount = new(); Dictionary? normalizedRectangleToPersonContainers; foreach (Mapping mapping in mappingCollection) { personContainers.Clear(); if (!_IdThenNormalizedRectangleToPersonContainers.TryGetValue(mapping.MappingFromItem.Id, out normalizedRectangleToPersonContainers)) result += 1; else { if (!normalizedRectangleToPersonContainers.TryGetValue(mapping.MappingFromLocation.NormalizedRectangle, out collection)) result += 1; else personContainers.AddRange(collection); } foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) continue; personBirthday = personContainer.Birthdays[zero]; personKey = personBirthday.Value.Ticks; if (!personKeyToCount.ContainsKey(personKey)) personKeyToCount.Add(personKey, 0); personKeyToCount[personKey] += 1; mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(_Ticks, personBirthday, personContainer.ApproximateYears, mapping.MappingFromItem); mapping.UpdateMappingFromPerson(personContainer.ApproximateYears, personContainer.DisplayDirectoryName, personBirthday, mappingSegmentB); } } return new(personKeyToCount, result); } public void SaveContainers(int totalNotMapped, int? updated, List saveContainers) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); string checkFile; string sourceFile; WindowsShortcut windowsShortcut; string[] directories = (from l in saveContainers select l.Directory).Distinct().ToArray(); int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - _Ticks).TotalSeconds); string message; if (updated is null) message = $") {saveContainers.Count:000} save(s) - {totalNotMapped} Total not Mapped - {totalSeconds} total second(s)"; else message = $") {saveContainers.Count:000} save(s) - {totalNotMapped} Total not Mapped - {updated} Updated - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; foreach (string directory in directories) { if (string.IsNullOrEmpty(directory)) continue; if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); } using ProgressBar progressBar = new(saveContainers.Count, message, options); foreach (SaveContainer saveContainer in saveContainers) { if (string.IsNullOrEmpty(saveContainer.Directory) || string.IsNullOrEmpty(saveContainer.CheckFile) || saveContainer.FaceFileHolder is null) continue; progressBar.Tick(); if (!saveContainer.FaceFileHolder.Exists && saveContainer.ResizedFileHolder is not null && saveContainer.ResizedFileHolder.Exists) { checkFile = saveContainer.CheckFile; sourceFile = saveContainer.ResizedFileHolder.FullName; } else if (saveContainer.FaceFileHolder.Exists) { sourceFile = saveContainer.FaceFileHolder.FullName; checkFile = $"{saveContainer.CheckFile}{_Configuration.FacesFileNameExtension}"; } else continue; if (File.Exists(checkFile)) continue; File.Copy(sourceFile, checkFile); if (saveContainer.MakeAllHidden) File.SetAttributes(checkFile, FileAttributes.Hidden); if (saveContainer.HiddenFaceFileHolder is not null && saveContainer.HiddenFaceFileHolder.Exists) { sourceFile = saveContainer.HiddenFaceFileHolder.FullName; checkFile = $"{saveContainer.CheckFile}{_Configuration.FacesHiddenFileNameExtension}"; } else if (saveContainer.FacePartsFileHolder is not null && saveContainer.FacePartsFileHolder.Exists) { sourceFile = saveContainer.FacePartsFileHolder.FullName; checkFile = $"{saveContainer.CheckFile}{_Configuration.FacePartsFileNameExtension}"; } if (File.Exists(checkFile)) continue; File.Copy(sourceFile, checkFile); if (saveContainer.MakeAllHidden) File.SetAttributes(checkFile, FileAttributes.Hidden); } if (updated is null) { foreach (SaveContainer saveContainer in saveContainers) { if (string.IsNullOrEmpty(saveContainer.Directory) || string.IsNullOrEmpty(saveContainer.CheckFile) || saveContainer.ResizedFileHolder is null || !saveContainer.ResizedFileHolder.Exists) continue; checkFile = saveContainer.CheckFile; sourceFile = saveContainer.ResizedFileHolder.FullName; if (File.Exists(checkFile)) continue; File.Copy(sourceFile, checkFile); if (saveContainer.MakeAllHidden) File.SetAttributes(checkFile, FileAttributes.Hidden); } } foreach (SaveContainer saveContainer in saveContainers) { if (string.IsNullOrEmpty(saveContainer.ShortcutFile) || saveContainer.ResizedFileHolder is null || !saveContainer.ResizedFileHolder.Exists) continue; try { string description = saveContainer.FaceFileHolder is not null ? saveContainer.FaceFileHolder.Name : string.Empty; windowsShortcut = new() { Path = saveContainer.ResizedFileHolder.FullName, Description = description }; windowsShortcut.Save(saveContainer.ShortcutFile); windowsShortcut.Dispose(); if (saveContainer.MakeAllHidden) File.SetAttributes(saveContainer.ShortcutFile, FileAttributes.Hidden); } catch (Exception) { } } } private static string? GetFacesDirectory(string dFacesContentDirectory, MappingFromItem mappingFromItem) { string? result; string? directoryName = Path.GetDirectoryName(mappingFromItem.RelativePath); if (directoryName is null) result = null; else result = Path.Combine($"{dFacesContentDirectory}{directoryName}", mappingFromItem.ImageFileHolder.NameWithoutExtension); return result; } private static string? GetFacePartsDirectory(string d2FacePartsContentDirectory, MappingFromItem mappingFromItem) { string? result; string? directoryName = Path.GetDirectoryName(mappingFromItem.RelativePath); if (directoryName is null) result = null; else result = Path.Combine($"{d2FacePartsContentDirectory}{directoryName}", mappingFromItem.ImageFileHolder.NameWithoutExtension); return result; } private SaveContainer? GetMatchSaveContainer(string dFacesContentDirectory, string d2FacePartsContentDirectory, string directory, Mapping mapping) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); SaveContainer? result; string shortcutFile = string.Empty; string? facesDirectory = GetFacesDirectory(dFacesContentDirectory, mapping.MappingFromItem); if (facesDirectory is null) result = null; else { FileHolder faceFileHolder = new(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacesFileNameExtension}")); if (!faceFileHolder.Exists) result = null; else { string? facePartsDirectory = GetFacePartsDirectory(d2FacePartsContentDirectory, mapping.MappingFromItem); if (facePartsDirectory is null) result = null; else { string checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}"); FileHolder hiddenFaceFileHolder = new(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacesHiddenFileNameExtension}")); FileHolder facePartsFileHolder = new(Path.Combine(facePartsDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacePartsFileNameExtension}")); result = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, mapping.MappingFromItem.ResizedFileHolder, shortcutFile); } } } return result; } private static (string, bool, bool) Get(int? useFiltersCounter, string forceSingleImageHumanized, Mapping mapping) { string by; bool isByMapping; bool isBySorting; if (mapping.By is null) { isByMapping = false; isBySorting = false; by = $"{nameof(Shared.Models.Stateless.IMapLogic.Mapping)}Null"; } else { isByMapping = mapping.By == Shared.Models.Stateless.IMapLogic.Mapping; isBySorting = mapping.By == Shared.Models.Stateless.IMapLogic.Sorting; if (isBySorting && mapping.MappingFromPerson is null) by = $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)} Without Person"; else if (isBySorting && useFiltersCounter.HasValue) by = $"{nameof(Shared.Models.Stateless.IMapLogic.Sorting)} Modified Filters - {useFiltersCounter.Value}"; else { by = mapping.By.Value switch { Shared.Models.Stateless.IMapLogic.Mapping => nameof(Shared.Models.Stateless.IMapLogic.Mapping), Shared.Models.Stateless.IMapLogic.Sorting => nameof(Shared.Models.Stateless.IMapLogic.Sorting), Shared.Models.Stateless.IMapLogic.ForceSingleImage => forceSingleImageHumanized, _ => throw new NotImplementedException() }; } } return new(by, isByMapping, isBySorting); } private string GetDirectory(string by, MappingFromItem mappingFromItem, SortingContainer sortingContainer) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); string result; const int zero = 0; string mappingSegmentB; string personKeyFormatted; PersonBirthday personBirthday; PersonContainer personContainer; result = Path.Combine(_EDistanceContentTicksDirectory, by, sortingContainer.Sorting.Id.ToString(), sortingContainer.Sorting.NormalizedRectangle.ToString()); for (int i = _NotMappedPersonContainers.Count - 1; i > 0; i--) { personContainer = _NotMappedPersonContainers[i]; if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) continue; personBirthday = personContainer.Birthdays[zero]; mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(_Ticks, personBirthday, personContainer.ApproximateYears, mappingFromItem); personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday); result = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, mappingSegmentB); _NotMappedPersonContainers.RemoveAt(i); break; } return result; } private List GetSaveContainers(string dFacesContentDirectory, string d2FacePartsContentDirectory, Mapping[] mappingCollection, Dictionary> idToNormalizedRectangleToMapping, Dictionary personKeyToCount, int? useFiltersCounter, bool saveNullPerson, bool saveMapped) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); List results = new(); string by; long personKey; bool isByMapping; bool isBySorting; string checkFile; string directory; string shortcutFile; string? directoryName; string personDirectory; string? facesDirectory; FileHolder faceFileHolder; string personKeyFormatted; List distinct = new(); string? facePartsDirectory; SaveContainer? saveContainer; FileHolder facePartsFileHolder; FileHolder hiddenFaceFileHolder; Dictionary? normalizedRectangleToMapping; string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title); foreach (Mapping mapping in mappingCollection) { directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); if (directoryName is null) throw new NotSupportedException(); (by, isByMapping, isBySorting) = Get(useFiltersCounter, forceSingleImageHumanized, mapping); if (isByMapping && !saveMapped) continue; if (mapping.MappingFromPerson is null) { if (!saveNullPerson) continue; if (mapping.SortingContainer is null) continue; if (distinct.Contains(mapping.MappingFromItem.Id)) continue; if (distinct.Contains(mapping.SortingContainer.Sorting.Id)) continue; directory = GetDirectory(by, mapping.MappingFromItem, mapping.SortingContainer); personDirectory = Path.Combine(directory, $"Z]{DateTime.Now.Ticks}"); distinct.Add(mapping.MappingFromItem.Id); distinct.Add(mapping.SortingContainer.Sorting.Id); } else { if (string.IsNullOrEmpty(mapping.MappingFromPerson.SegmentB)) throw new NotSupportedException(); if (string.IsNullOrEmpty(mapping.MappingFromPerson.DisplayDirectoryName)) throw new NotSupportedException(); personKey = mapping.MappingFromPerson.PersonBirthday.Value.Ticks; personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mapping.MappingFromPerson.PersonBirthday); if (string.IsNullOrEmpty(mapping.SegmentC)) directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, mapping.MappingFromPerson.SegmentB); else directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, mapping.MappingFromPerson.SegmentB, mapping.SegmentC); if (isByMapping) personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); else if (mapping.By is not null) personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName, "lnk"); else personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName[..1], "lnk"); if (isByMapping && personKeyToCount.TryGetValue(personKey, out int count)) { saveContainer = new(Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName, $"{count} Face(s)")); results.Add(saveContainer); } } saveContainer = new(personDirectory); results.Add(saveContainer); facesDirectory = GetFacesDirectory(dFacesContentDirectory, mapping.MappingFromItem); if (facesDirectory is null) continue; faceFileHolder = new(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacesFileNameExtension}")); if (!faceFileHolder.Exists) continue; if (isByMapping) { checkFile = Path.Combine(personDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}"); saveContainer = new(checkFile, directory, faceFileHolder); } else { facePartsDirectory = GetFacePartsDirectory(d2FacePartsContentDirectory, mapping.MappingFromItem); if (facePartsDirectory is null) continue; // if (!isBySorting || mapping.MappingFromPerson is not null) checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}"); // else // checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}-Source{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}"); shortcutFile = Path.Combine(personDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}.lnk"); hiddenFaceFileHolder = new(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacesHiddenFileNameExtension}")); facePartsFileHolder = new(Path.Combine(facePartsDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacePartsFileNameExtension}")); saveContainer = new(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, mapping.MappingFromItem.ResizedFileHolder, shortcutFile); } results.Add(saveContainer); if (!isBySorting || mapping.SortingContainer is null) continue; if (!idToNormalizedRectangleToMapping.TryGetValue(mapping.SortingContainer.Sorting.Id, out normalizedRectangleToMapping)) continue; if (!normalizedRectangleToMapping.ContainsKey(mapping.SortingContainer.Sorting.NormalizedRectangle)) continue; if (isBySorting && mapping.MappingFromPerson is null) { saveContainer = GetMatchSaveContainer(dFacesContentDirectory, d2FacePartsContentDirectory, directory, normalizedRectangleToMapping[mapping.SortingContainer.Sorting.NormalizedRectangle]); if (saveContainer is not null) results.Add(saveContainer); } saveContainer = Stateless.MapLogic.GetDebugSaveContainer(directory, mapping.SortingContainer, normalizedRectangleToMapping[mapping.SortingContainer.Sorting.NormalizedRectangle]); results.Add(saveContainer); } return results; } public List GetSaveContainers(string dFacesContentDirectory, string d2FacePartsContentDirectory, Mapping[] mappingCollection, Dictionary> idToNormalizedRectangleToMapping, int? useFiltersCounter, bool saveNullPerson) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); List results; bool saveMapped = false; Dictionary personKeyToCount = new(); results = GetSaveContainers(dFacesContentDirectory, d2FacePartsContentDirectory, mappingCollection, idToNormalizedRectangleToMapping, personKeyToCount, useFiltersCounter, saveNullPerson, saveMapped); return results; } public void SaveMapped(string dFacesContentDirectory, string d2FacePartsContentDirectory, Mapping[] mappingCollection, Dictionary> idToNormalizedRectangleToMapping, Dictionary personKeyToCount, int totalNotMapped) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); int? updated = null; bool saveMapped = true; bool saveNullPerson = false; int? useFiltersCounter = null; string mappingDirectory = Path.Combine(_EDistanceContentTicksDirectory, nameof(Shared.Models.Stateless.IMapLogic.Mapping)); List saveContainers = GetSaveContainers(dFacesContentDirectory, d2FacePartsContentDirectory, mappingCollection, idToNormalizedRectangleToMapping, personKeyToCount, useFiltersCounter, saveNullPerson, saveMapped); SaveContainers(totalNotMapped, updated, saveContainers); if (!string.IsNullOrEmpty(_EDistanceContentTicksDirectory) && Directory.Exists(mappingDirectory)) Stateless.MapLogic.SaveMappingShortcuts(mappingDirectory); } private List<(long, long, long, long)> GetPersonKeysRangesCollection(PersonContainer[] personContainers) { if (_PersonKeyToRanges is null) throw new NullReferenceException(nameof(_PersonKeyToRanges)); List<(long, long, long, long)> results = new(); (long, long, long, long) singleton; foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null) continue; if (!_PersonKeyToRanges.TryGetValue(personContainer.Key.Value, out singleton)) continue; results.Add(singleton); } return results; } public bool Used(FaceDistance faceDistanceEncoding) { bool result = false; if (faceDistanceEncoding.NormalizedRectangle is null) throw new NotSupportedException(); List? normalizedRectangles; Dictionary? normalizedRectangleToPersonContainers; if (_SkipCollection.TryGetValue(faceDistanceEncoding.Id, out normalizedRectangles)) { if (normalizedRectangles.Contains(faceDistanceEncoding.NormalizedRectangle.Value)) result = true; } if (!result && _IdThenNormalizedRectangleToPersonContainers.TryGetValue(faceDistanceEncoding.Id, out normalizedRectangleToPersonContainers)) { if (normalizedRectangleToPersonContainers.ContainsKey(faceDistanceEncoding.NormalizedRectangle.Value)) result = true; } return result; } public List GetSortingCollection(int i, FaceDistance faceDistanceEncoding, List faceDistanceLengths) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); List results = new(); Sorting sorting; FaceDistance faceDistanceLength; List? normalizedRectangles; Dictionary? normalizedRectangleToPersonContainers; List<(long lcl, long minimum, long maximum, long ucl)> personKeysRangesCollection; for (int j = 0; j < faceDistanceLengths.Count; j++) { if (faceDistanceEncoding.NormalizedRectangle is null) throw new NotSupportedException(); if (j == i) continue; if (_SkipCollection.TryGetValue(faceDistanceEncoding.Id, out normalizedRectangles)) { if (normalizedRectangles.Contains(faceDistanceEncoding.NormalizedRectangle.Value)) continue; } if (_IdThenNormalizedRectangleToPersonContainers.TryGetValue(faceDistanceEncoding.Id, out normalizedRectangleToPersonContainers)) { if (normalizedRectangleToPersonContainers.ContainsKey(faceDistanceEncoding.NormalizedRectangle.Value)) continue; } faceDistanceLength = faceDistanceLengths[j]; if (faceDistanceLength.NormalizedRectangle is null || faceDistanceLength.Length is null) throw new NotSupportedException(); if (faceDistanceLength.Length == 0) continue; if (!_IdThenNormalizedRectangleToPersonContainers.TryGetValue(faceDistanceLength.Id, out normalizedRectangleToPersonContainers)) personKeysRangesCollection = new(); else { if (!normalizedRectangleToPersonContainers.ContainsKey(faceDistanceLength.NormalizedRectangle.Value)) personKeysRangesCollection = new(); else personKeysRangesCollection = GetPersonKeysRangesCollection(normalizedRectangleToPersonContainers[faceDistanceLength.NormalizedRectangle.Value]); } sorting = ISorting.Get(_Configuration.FaceDistancePermyriad, _Configuration.RangeDistanceTolerance, faceDistanceEncoding, faceDistanceLength, personKeysRangesCollection); if (sorting.DistancePermyriad == 0) continue; if (sorting.Id == faceDistanceEncoding.Id) { if (sorting.NormalizedRectangle == faceDistanceEncoding.NormalizedRectangle.Value) continue; continue; } results.Add(sorting); } return results; } public int UpdateFromSortingContainers(SortingContainer[] sortingContainers, bool saveNullPerson) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); if (_MapLogicSupport is not null) { string counts = _MapLogicSupport.GetCounts(); _ = Directory.CreateDirectory(Path.Combine(_EDistanceContentTicksDirectory, counts)); } int result = 0; string key; const int zero = 0; string mappingSegmentB; string personKeyFormatted; PersonBirthday personBirthday; PersonContainer[]? personContainers; Dictionary keyToCount = new(); List normalizedRectangleCollectionForA; List normalizedRectangleCollectionForB; Dictionary keyToSegmentC = new(); Dictionary> idToNormalizedRectangleCollectionForA = new(); Dictionary> idToNormalizedRectangleCollectionForB = new(); Dictionary? normalizedRectangleToPersonContainers; int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - _Ticks).TotalSeconds); string message = $") {sortingContainers.Length:000} Update From Sorting Container(s) - {totalSeconds} total second(s)"; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(sortingContainers.Length, message, options); foreach (SortingContainer sortingContainer in sortingContainers) { progressBar.Tick(); if (sortingContainer.Mapping is null) throw new NotSupportedException(); if (!idToNormalizedRectangleCollectionForA.ContainsKey(sortingContainer.Mapping.MappingFromItem.Id)) idToNormalizedRectangleCollectionForA.Add(sortingContainer.Mapping.MappingFromItem.Id, new()); normalizedRectangleCollectionForA = idToNormalizedRectangleCollectionForA[sortingContainer.Mapping.MappingFromItem.Id]; if (!idToNormalizedRectangleCollectionForB.ContainsKey(sortingContainer.Mapping.MappingFromItem.Id)) idToNormalizedRectangleCollectionForB.Add(sortingContainer.Mapping.MappingFromItem.Id, new()); normalizedRectangleCollectionForB = idToNormalizedRectangleCollectionForB[sortingContainer.Mapping.MappingFromItem.Id]; if (!_IdThenNormalizedRectangleToPersonContainers.TryGetValue(sortingContainer.Sorting.Id, out normalizedRectangleToPersonContainers) || !normalizedRectangleToPersonContainers.TryGetValue(sortingContainer.Sorting.NormalizedRectangle, out personContainers)) { if (!saveNullPerson) continue; if (normalizedRectangleCollectionForA.Contains(sortingContainer.Mapping.MappingFromLocation.NormalizedRectangle)) continue; sortingContainer.Mapping.UpdateMappingFromUnknownPerson(sortingContainer); normalizedRectangleCollectionForA.Add(sortingContainer.Mapping.MappingFromLocation.NormalizedRectangle); result += 1; } else { if (normalizedRectangleCollectionForB.Contains(sortingContainer.Mapping.MappingFromLocation.NormalizedRectangle)) continue; foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) continue; personBirthday = personContainer.Birthdays[zero]; personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday); mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(_Ticks, personBirthday, personContainer.ApproximateYears, sortingContainer.Mapping.MappingFromItem); key = string.Concat(personKeyFormatted, '\t', mappingSegmentB); if (!keyToCount.ContainsKey(key)) keyToCount.Add(key, new()); if (!keyToCount.ContainsKey(key)) keyToCount.Add(key, 0); if (!keyToSegmentC.ContainsKey(key)) keyToSegmentC.Add(key, string.Empty); keyToCount[key]++; if (keyToCount[key] > _Configuration.SortingMaximumPerKey) { keyToCount[key] = 0; keyToSegmentC[key] = sortingContainer.Sorting.DistancePermyriad.ToString(); } sortingContainer.Mapping.UpdateMappingFromPerson(personContainer.ApproximateYears, personContainer.DisplayDirectoryName, personBirthday, mappingSegmentB, keyToSegmentC[key], sortingContainer); normalizedRectangleCollectionForB.Add(sortingContainer.Mapping.MappingFromLocation.NormalizedRectangle); result += 1; break; } } } return result; } public void CopyManualFiles(string dFacesContentDirectory, Dictionary> idToNormalizedRectangleToMapping) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); int? id; Mapping mapping; string faceFile; string checkFile; string directory; FileInfo fileInfo; const int zero = 0; string faceFileName; string shortcutFile; string? directoryName; string mappingSegmentB; string personDirectory; string? facesDirectory; string personKeyFormatted; string personDisplayFileName; PersonBirthday personBirthday; string? personDisplayDirectory; int? normalizedRectangle; WindowsShortcut windowsShortcut; Dictionary? normalizedRectangleToMapping; string by = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy); Dictionary? normalizedRectangleToPeronContainerCollection; string successfull = $"_ {nameof(Shared.Models.Stateless.IMapLogic.ManualCopy).Humanize(LetterCasing.Title)} Successfull"; foreach (KeyValuePair keyValuePair in _PersonKeyToPersonContainer) { if (keyValuePair.Value.Key is null || keyValuePair.Value.Birthdays is null || !keyValuePair.Value.Birthdays.Any()) continue; personBirthday = keyValuePair.Value.Birthdays[zero]; foreach (string personDisplayDirectoryAllFile in keyValuePair.Value.DisplayDirectoryAllFiles) { if (personDisplayDirectoryAllFile.Contains("-2318605")) continue; if (!personDisplayDirectoryAllFile.EndsWith(_Configuration.FacesFileNameExtension)) continue; (id, normalizedRectangle) = IMapping.GetConverted(_Configuration.FacesFileNameExtension, personDisplayDirectoryAllFile); if (id is null || normalizedRectangle is null) continue; fileInfo = new(personDisplayDirectoryAllFile); if (!fileInfo.Exists) continue; personDisplayFileName = Path.GetFileName(personDisplayDirectoryAllFile); personDisplayDirectory = Path.GetDirectoryName(personDisplayDirectoryAllFile); personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday); mappingSegmentB = Stateless.MapLogic.GetMappingSegmentB(_Ticks, personBirthday, keyValuePair.Value.ApproximateYears, fileInfo.CreationTime, isWrongYear: null); directory = Path.Combine(_EDistanceContentTicksDirectory, by, personKeyFormatted, mappingSegmentB); personDirectory = Path.Combine(directory, keyValuePair.Value.DisplayDirectoryName, "lnk"); if (!idToNormalizedRectangleToMapping.TryGetValue(id.Value, out normalizedRectangleToMapping)) continue; if (!normalizedRectangleToMapping.ContainsKey(normalizedRectangle.Value)) continue; mapping = normalizedRectangleToMapping[normalizedRectangle.Value]; if (string.IsNullOrEmpty(personDisplayDirectory)) throw new NotSupportedException(); directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); if (string.IsNullOrEmpty(directoryName)) throw new NotSupportedException(); shortcutFile = Path.Combine(personDisplayDirectory, $"{personDisplayFileName}.lnk"); facesDirectory = GetFacesDirectory(dFacesContentDirectory, mapping.MappingFromItem); // Path.Combine($"{dFacesContentDirectory}{directoryName}", mapping.MappingFromItem.ImageFileHolder.NameWithoutExtension); if (facesDirectory is null) continue; faceFileName = $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacesFileNameExtension}"; checkFile = Path.Combine(directory, fileInfo.Name); if (!_IdThenNormalizedRectangleToPersonContainers.TryGetValue(id.Value, out normalizedRectangleToPeronContainerCollection) || !normalizedRectangleToPeronContainerCollection.ContainsKey(normalizedRectangle.Value)) { if (!Directory.Exists(personDirectory)) _ = Directory.CreateDirectory(personDirectory); if (!File.Exists(checkFile)) File.Copy(personDisplayDirectoryAllFile, checkFile); } if (personDisplayDirectoryAllFile.Contains(successfull)) continue; directoryName = Path.Combine(personDisplayDirectory, successfull); if (!Directory.Exists(directoryName)) _ = Directory.CreateDirectory(directoryName); checkFile = Path.Combine(directoryName, personDisplayFileName); if (File.Exists(checkFile)) File.Delete(personDisplayDirectoryAllFile); else File.Move(personDisplayDirectoryAllFile, checkFile); faceFile = Path.Combine(facesDirectory, faceFileName); if (!File.Exists(faceFile)) continue; if (File.Exists(shortcutFile)) continue; windowsShortcut = new() { Path = faceFile }; windowsShortcut.Save(shortcutFile); windowsShortcut.Dispose(); if (!File.Exists(shortcutFile)) continue; File.SetLastWriteTime(shortcutFile, mapping.MappingFromItem.MinimumDateTime); } } } public void SaveNotMappedTicks() { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); int updated = 0; string directory; const int zero = 0; int totalNotMapped = 0; string personKeyFormatted; SaveContainer saveContainer; PersonBirthday personBirthday; List saveContainers = new(); const string facePopulatedKey = nameof(Shared.Models.Stateless.IMapLogic.Sorting); foreach (PersonContainer personContainer in _NotMappedPersonContainers) { if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) continue; personBirthday = personContainer.Birthdays[zero]; personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday); directory = Path.Combine(_EDistanceContentTicksDirectory, $"{facePopulatedKey}NotMapped", personKeyFormatted, _Configuration.MappingDefaultName); saveContainer = new(directory); saveContainers.Add(saveContainer); } SaveContainers(totalNotMapped, updated, saveContainers); } private (string, PersonBirthday?) GetPersonBirthday(string[] directoryNames) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); PersonBirthday? personBirthday = null; string personKeyFormatted = string.Empty; foreach (string directoryName in directoryNames) { personBirthday = IPersonBirthday.GetPersonBirthday(_Configuration.PersonBirthdayFormat, directoryName); if (personBirthday is not null) { personKeyFormatted = directoryName; break; } } return new(personKeyFormatted, personBirthday); } private (string, PersonBirthday?) GetPersonBirthday(string windowsShortcutPath) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); string[] directoryNames = IPath.GetDirectoryNames(windowsShortcutPath); (string personKeyFormatted, PersonBirthday? personBirthday) = GetPersonBirthday(directoryNames); if (personBirthday is null) { string[] directories = Directory.GetDirectories(windowsShortcutPath, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { directoryNames = IPath.GetDirectoryNames(directory); (personKeyFormatted, personBirthday) = GetPersonBirthday(directoryNames); if (personBirthday is not null) break; } } return new(personKeyFormatted, personBirthday); } private List GetPersonKeyFormattedCollection(string[] jLinks, string a2PeopleSingletonDirectory, PersonContainer[] personContainers, Dictionary personKeyToCount) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); if (_MapLogicSupport is null) throw new NullReferenceException(nameof(_MapLogicSupport)); List results = new(); string[] files; string checkDirectory; string[] checkDirectories; string personKeyFormatted; PersonContainer[] matches; PersonBirthday? personBirthday; string fileNameWithoutExtension; WindowsShortcut windowsShortcut; List<(long, string)> collection = new(); foreach (string directoryName in jLinks) { checkDirectory = Path.Combine(a2PeopleSingletonDirectory, directoryName); if (!Directory.Exists(checkDirectory)) continue; checkDirectories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly); files = Directory.GetFiles(checkDirectory, "*.lnk", SearchOption.TopDirectoryOnly); foreach (string file in files) { collection.Clear(); windowsShortcut = WindowsShortcut.Load(file); fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); if (windowsShortcut.Path is null) continue; if (!Directory.Exists(windowsShortcut.Path)) { if (files.Length != checkDirectories.Length) throw new NotSupportedException(fileNameWithoutExtension); continue; } (personKeyFormatted, personBirthday) = GetPersonBirthday(windowsShortcut.Path); if (personBirthday is null) throw new NotSupportedException(fileNameWithoutExtension); if (!personKeyToCount.ContainsKey(personBirthday.Value.Ticks)) collection.Add(new(personBirthday.Value.Ticks, Path.Combine(checkDirectory, personKeyFormatted, fileNameWithoutExtension))); else collection.Add(new(personBirthday.Value.Ticks, Path.Combine(checkDirectory, personKeyFormatted, fileNameWithoutExtension, $"{personKeyToCount[personBirthday.Value.Ticks]} Face(s)"))); foreach ((long personKey, string displayDirectoryName) in collection) { matches = (from l in personContainers where l.Key == personKey && l.ApproximateYears.HasValue select l).ToArray(); if (!matches.Any()) continue; personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personKey); if (!displayDirectoryName.Contains(personKeyFormatted)) continue; if (!Directory.Exists(displayDirectoryName)) _ = Directory.CreateDirectory(displayDirectoryName); results.Add(personKeyFormatted); } } } return results; } private (int, FileHolder, int, string, string, string, string)[] GetCollectionForSaveFilteredOriginalImagesFromJLinks(string[] jLinks, string a2PeopleSingletonDirectory, PersonContainer[] personContainers, Mapping[] mappingCollection, Dictionary personKeyToCount) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); (int, FileHolder, int, string, string, string, string)[] results; string checkFile; string directory; string? directoryName; string personDirectory; string personKeyFormatted; bool usePersonKeyAndDeterministicHashCodeKey = false; List personKeyFormattedCollection = GetPersonKeyFormattedCollection(jLinks, a2PeopleSingletonDirectory, personContainers, personKeyToCount); List<(int Id, FileHolder ImageFileHolder, int ApproximateYears, string PersonKeyFormatted, string CheckFile, string Directory, string PersonDirectory)> collection = new(); foreach (Mapping mapping in mappingCollection) { directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); if (directoryName is null) throw new NotSupportedException(); if (mapping.By is null or Shared.Models.Stateless.IMapLogic.Sorting) continue; if (mapping.MappingFromPerson?.ApproximateYears is null) continue; if (string.IsNullOrEmpty(mapping.MappingFromPerson.SegmentB)) throw new NotSupportedException(); if (string.IsNullOrEmpty(mapping.MappingFromPerson.DisplayDirectoryName)) throw new NotSupportedException(); personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mapping.MappingFromPerson.PersonBirthday); if (personKeyFormatted == "1501-04-10_00") continue; if (!personKeyFormattedCollection.Contains(personKeyFormatted)) continue; if (!usePersonKeyAndDeterministicHashCodeKey) { directory = Path.Combine(_EDistanceContentTicksDirectory, "Images"); personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); checkFile = Path.Combine(directory, $"{mapping.MappingFromItem.Id}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}"); } else { directory = Path.Combine(_EDistanceContentTicksDirectory, "Images", personKeyFormatted); personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}"); } collection.Add(new(mapping.MappingFromItem.Id, mapping.MappingFromItem.ImageFileHolder, mapping.MappingFromPerson.ApproximateYears.Value, personKeyFormatted, directory, personDirectory, checkFile)); } results = (from l in collection orderby l.ApproximateYears descending, l.PersonKeyFormatted descending select l).ToArray(); return results; } public void SaveFilteredOriginalImagesFromJLinks(string[] jLinks, string a2PeopleSingletonDirectory, PersonContainer[] personContainers, Mapping[] mappingCollection, Dictionary personKeyToCount, int totalNotMapped) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); SaveContainer? saveContainer; List distinctCollection = new(); List saveContainers = new(); (int, FileHolder, int, string, string, string, string)[] collection = GetCollectionForSaveFilteredOriginalImagesFromJLinks(jLinks, a2PeopleSingletonDirectory, personContainers, mappingCollection, personKeyToCount); foreach ((int id, FileHolder imageFileHolder, int approximateYears, string personKeyFormatted, string directory, string personDirectory, string checkFile) in collection) { if (distinctCollection.Contains(id)) continue; distinctCollection.Add(id); saveContainer = new(personDirectory); saveContainers.Add(saveContainer); saveContainer = new(imageFileHolder, checkFile, directory); saveContainers.Add(saveContainer); } SaveContainers(totalNotMapped, null, saveContainers); } private (List<(string, DateTime[])>, List<(string, string, string, string)>) GetCollectionForSaveShortcutsForOutputResolutions(List filteredItems, Mapping[] mappingCollection, Dictionary personKeyToCount) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); long personKey; string fileName; string directory; string? directoryName; string personDirectory; string personKeyFormatted; List distinct = new(); List<(string, string, string, string)> collection = new(); List<(string, DateTime[])> directoriesAndDateTimes = new(); foreach (Item item in filteredItems) { if (item.ResizedFileHolder is null) continue; foreach (Face face in item.Faces) { if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.Mapping is null) continue; directoryName = Path.GetDirectoryName(face.Mapping.MappingFromItem.RelativePath); if (directoryName is null) throw new NotSupportedException(); if (item.ResizedFileHolder?.DirectoryName is null || !item.ResizedFileHolder.Exists) continue; directory = Path.Combine(item.ResizedFileHolder.DirectoryName, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", _PropertyConfiguration.ResultAllInOne); personDirectory = Path.Combine(directory, "No Faces"); fileName = Path.Combine(personDirectory, $"{item.ResizedFileHolder.Name}.lnk"); collection.Add(new(item.ResizedFileHolder.FullName, personDirectory, fileName, face.Mapping.MappingFromItem.Id.ToString())); if (face.Mapping.MappingFromItem.ContainerDateTimes.Any() && !distinct.Contains(item.ResizedFileHolder.DirectoryName)) { distinct.Add(item.ResizedFileHolder.DirectoryName); directoriesAndDateTimes.Add(new(item.ResizedFileHolder.DirectoryName, face.Mapping.MappingFromItem.ContainerDateTimes)); } } } foreach (Mapping mapping in mappingCollection) { if (mapping.MappingFromItem.ResizedFileHolder.DirectoryName is null || !mapping.MappingFromItem.ResizedFileHolder.Exists) continue; if (mapping.By is null or Shared.Models.Stateless.IMapLogic.Sorting || mapping.MappingFromPerson?.ApproximateYears is null) continue; if (string.IsNullOrEmpty(mapping.MappingFromPerson.SegmentB)) throw new NotSupportedException(); if (string.IsNullOrEmpty(mapping.MappingFromPerson.DisplayDirectoryName)) throw new NotSupportedException(); personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mapping.MappingFromPerson.PersonBirthday); } foreach (Mapping mapping in mappingCollection) { directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); if (directoryName is null) throw new NotSupportedException(); if (mapping.MappingFromItem.ResizedFileHolder.DirectoryName is null || !mapping.MappingFromItem.ResizedFileHolder.Exists) continue; if (mapping.By is null or Shared.Models.Stateless.IMapLogic.Sorting || mapping.MappingFromPerson?.ApproximateYears is null) { if (mapping.MappingFromItem.ContainerDateTimes.Any() && !distinct.Contains(mapping.MappingFromItem.ResizedFileHolder.DirectoryName)) { distinct.Add(mapping.MappingFromItem.ResizedFileHolder.DirectoryName); directoriesAndDateTimes.Add(new(mapping.MappingFromItem.ResizedFileHolder.DirectoryName, mapping.MappingFromItem.ContainerDateTimes)); } directory = Path.Combine(mapping.MappingFromItem.ResizedFileHolder.DirectoryName, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", _PropertyConfiguration.ResultAllInOne); personDirectory = Path.Combine(directory, "Unknown"); fileName = Path.Combine(personDirectory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk"); collection.Add(new(mapping.MappingFromItem.ResizedFileHolder.FullName, personDirectory, fileName, mapping.MappingFromLocation.DeterministicHashCodeKey)); } else { if (string.IsNullOrEmpty(mapping.MappingFromPerson.SegmentB)) throw new NotSupportedException(); if (string.IsNullOrEmpty(mapping.MappingFromPerson.DisplayDirectoryName)) throw new NotSupportedException(); personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, mapping.MappingFromPerson.PersonBirthday); personKey = mapping.MappingFromPerson.PersonBirthday.Value.Ticks; if (mapping.MappingFromItem.ContainerDateTimes.Any() && !distinct.Contains(mapping.MappingFromItem.ResizedFileHolder.DirectoryName)) { distinct.Add(mapping.MappingFromItem.ResizedFileHolder.DirectoryName); directoriesAndDateTimes.Add(new(mapping.MappingFromItem.ResizedFileHolder.DirectoryName, mapping.MappingFromItem.ContainerDateTimes)); } directory = Path.Combine(mapping.MappingFromItem.ResizedFileHolder.DirectoryName, $"{_PropertyConfiguration.ResultAllInOne}Shortcuts", personKeyFormatted); if (!personKeyToCount.ContainsKey(personKey)) personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName); else personDirectory = Path.Combine(directory, mapping.MappingFromPerson.DisplayDirectoryName, $"{personKeyToCount[personKey]} Face(s)"); fileName = Path.Combine(directory, $"{mapping.MappingFromItem.ResizedFileHolder.Name}.lnk"); collection.Add(new(mapping.MappingFromItem.ResizedFileHolder.FullName, personDirectory, fileName, mapping.MappingFromLocation.DeterministicHashCodeKey)); } } return new(directoriesAndDateTimes, collection); } public void SaveShortcutsForOutputResolutions(List filteredItems, Mapping[] mappingCollection, Dictionary personKeyToCount) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); WindowsShortcut windowsShortcut; List<(string, DateTime[])> directoriesAndDateTimes; List<(string, string Directory, string, string)> collection; (directoriesAndDateTimes, collection) = GetCollectionForSaveShortcutsForOutputResolutions(filteredItems, mappingCollection, personKeyToCount); string[] directories = (from l in collection select l.Directory).Distinct().ToArray(); foreach (string directory in directories) { if (string.IsNullOrEmpty(directory)) continue; if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); } foreach ((string fullName, string directory, string fileName, string description) in collection) { if (File.Exists(fileName)) continue; try { windowsShortcut = new() { Path = fullName, Description = description }; windowsShortcut.Save(fileName); windowsShortcut.Dispose(); } catch (Exception) { } } foreach ((string directory, DateTime[] dateTimes) in directoriesAndDateTimes) { if (!dateTimes.Any()) continue; Directory.SetCreationTime(directory, dateTimes[0]); Directory.SetLastWriteTime(directory, dateTimes[1]); } } private List<(string, FileHolder, string)> GetCollection(string dFacesContentDirectory, Dictionary> idToNormalizedRectangleToMapping) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); List<(string, FileHolder, string)> results = new(); Mapping mapping; string checkFile; string directory; string? directoryName; string? facesDirectory; FileHolder faceFileHolder; List? normalizedRectangles; string by = nameof(Shared.Models.Stateless.IMapLogic.CopyNotMappedFaces); Dictionary? normalizedRectangleToPersonContainers; foreach (KeyValuePair> keyValuePair in idToNormalizedRectangleToMapping) { _ = _IdThenNormalizedRectangleToPersonContainers.TryGetValue(keyValuePair.Key, out normalizedRectangleToPersonContainers); foreach (KeyValuePair normalizedRectangleAndMapping in keyValuePair.Value) { mapping = normalizedRectangleAndMapping.Value; if (normalizedRectangleToPersonContainers is not null && normalizedRectangleToPersonContainers.ContainsKey(mapping.MappingFromLocation.NormalizedRectangle)) continue; _ = _SkipCollection.TryGetValue(keyValuePair.Key, out normalizedRectangles); if (normalizedRectangles is not null && normalizedRectangles.Contains(mapping.MappingFromLocation.NormalizedRectangle)) continue; directoryName = Path.GetDirectoryName(mapping.MappingFromItem.RelativePath); facesDirectory = GetFacesDirectory(dFacesContentDirectory, mapping.MappingFromItem); // Path.Combine($"{dFacesContentDirectory}{directoryName}", mapping.MappingFromItem.ImageFileHolder.NameWithoutExtension); if (facesDirectory is null) continue; faceFileHolder = new(Path.Combine(facesDirectory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacesFileNameExtension}")); if (directoryName is null || !faceFileHolder.Exists) continue; directory = Path.Combine(_EDistanceContentTicksDirectory, by, $"{mapping.MappingFromLocation.AreaPermille:0000}A{mapping.MappingFromItem.MinimumDateTime.ToString("yyyy")[..3]}#"); checkFile = Path.Combine(directory, $"{mapping.MappingFromLocation.DeterministicHashCodeKey}{mapping.MappingFromItem.ImageFileHolder.ExtensionLowered}{_Configuration.FacesFileNameExtension}"); results.Add(new(directory, faceFileHolder, checkFile)); } } return results; } public void CopyNotMappedFaces(int[] rangeFaceAreaPermilleTolerance, string dFacesContentDirectory, Dictionary> idToNormalizedRectangleToMapping) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); List<(string Directory, FileHolder FaceFileHolder, string CheckFile)> collection = GetCollection(dFacesContentDirectory, idToNormalizedRectangleToMapping); string[] directories = (from l in collection select l.Directory).Distinct().ToArray(); foreach (string directory in directories) { if (string.IsNullOrEmpty(directory)) continue; if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); } foreach ((string directory, FileHolder faceFileHolder, string checkFile) in collection) { if (File.Exists(checkFile)) continue; File.Copy(faceFileHolder.FullName, checkFile); } } private DatabaseFileRoot GetDatabaseFileRoot() { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); string file = Path.Combine(_Configuration.PhotoPrismDirectory, "files.json"); string json = File.ReadAllText(file); DatabaseFileRoot? databaseFileRoot = JsonSerializer.Deserialize(json); if (databaseFileRoot is null) throw new NullReferenceException(nameof(databaseFileRoot)); return databaseFileRoot; } private Marker[] GetMarkers() { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); string file = Path.Combine(_Configuration.PhotoPrismDirectory, "markers.json"); string json = File.ReadAllText(file); Marker[]? markerRoot = JsonSerializer.Deserialize(json); if (markerRoot is null) throw new NullReferenceException(nameof(markerRoot)); return markerRoot; } private static Dictionary Get(DatabaseFileRoot databaseFileRoot) { Dictionary fileUidToFile = new(); for (int i = 0; i < databaseFileRoot.Files.Count; i++) fileUidToFile.Add(databaseFileRoot.Files[i].FileUid, databaseFileRoot.Files[i]); return fileUidToFile; } private static MarkerWith GetMarkerWith(int? dlib, DatabaseFile databaseFile, Marker marker, int? count, double? percent, int? normalizedRectangle, long? personKey, string personKeyFormatted) { return new(marker.MarkerUid, marker.FileUid, marker.MarkerType, marker.MarkerSrc, marker.MarkerName, marker.MarkerReview, marker.MarkerInvalid, marker.SubjUid, marker.SubjSrc, marker.FaceId, marker.FaceDist, marker.EmbeddingsJson, marker.LandmarksJson, marker.X, marker.Y, marker.W, marker.H, marker.Q, marker.Size, marker.Score, marker.Thumb, marker.MatchedAt, marker.CreatedAt, marker.UpdatedAt, databaseFile.Id, databaseFile.FileName, dlib, count, percent, normalizedRectangle, personKey, personKeyFormatted); } private static Dictionary> GetFacesByFileName(List filteredItems) { Dictionary> results = new(); string key; foreach (Item item in filteredItems) { foreach (Face face in item.Faces) { if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.Mapping is null) continue; key = Path.GetFileNameWithoutExtension(face.Mapping.MappingFromItem.RelativePath); if (!results.ContainsKey(key)) results.Add(key, new()); results[key].Add(face); } } return results; } public void FindMatch(List filteredItems) { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); int? dlib; double? percent; long? personKey; const int zero = 0; List? matches; MarkerWith markerWith; int? normalizedRectangle; string personKeyFormatted; DatabaseFile? databaseFile; PersonBirthday personBirthday; Marker[] markers = GetMarkers(); string fileNameWithoutExtension; PersonContainer[]? personContainers; System.Drawing.Rectangle dlibRectangle; System.Drawing.Rectangle prismRectangle; System.Drawing.Rectangle intersectRectangle; (Face Face, double Percent)[] sortedCollection; List<(Face Face, double Percent)> collection = new(); DatabaseFileRoot databaseFileRoot = GetDatabaseFileRoot(); Dictionary fileUidToFile = Get(databaseFileRoot); Dictionary? normalizedRectangleToPersonContainers; Dictionary> keyValuePairs = GetFacesByFileName(filteredItems); foreach (Marker marker in markers) { dlib = null; personKey = null; collection.Clear(); normalizedRectangle = null; personKeyFormatted = string.Empty; normalizedRectangleToPersonContainers = null; if (!fileUidToFile.TryGetValue(marker.FileUid, out databaseFile)) continue; fileNameWithoutExtension = Path.GetFileNameWithoutExtension(Path.Combine("C:", databaseFile.FileName)); prismRectangle = new((int)(marker.X * databaseFile.FileWidth), (int)(marker.Y * databaseFile.FileHeight), (int)(marker.W * databaseFile.FileWidth), (int)(marker.H * databaseFile.FileHeight)); if (!keyValuePairs.TryGetValue(fileNameWithoutExtension, out matches) || !int.TryParse(fileNameWithoutExtension, out int id)) percent = null; else { dlib = id; _ = _IdThenNormalizedRectangleToPersonContainers.TryGetValue(dlib.Value, out normalizedRectangleToPersonContainers); foreach (Face face in matches) { if (face.Location is null || face.OutputResolution is null) continue; dlibRectangle = new(face.Location.Left, face.Location.Top, face.Location.Right - face.Location.Left, face.Location.Bottom - face.Location.Top); intersectRectangle = System.Drawing.Rectangle.Intersect(prismRectangle, dlibRectangle); if (intersectRectangle.Width == 0 || intersectRectangle.Height == 0) continue; percent = (double)intersectRectangle.Width * intersectRectangle.Height / (dlibRectangle.Width * dlibRectangle.Height); if (percent < 0.000001) continue; collection.Add(new(face, percent.Value)); } } if (!collection.Any()) percent = null; else { sortedCollection = collection.OrderByDescending(l => l.Percent).ToArray(); percent = sortedCollection[zero].Percent; normalizedRectangle = sortedCollection[zero].Face.Mapping?.MappingFromLocation.NormalizedRectangle; if (normalizedRectangleToPersonContainers is null || normalizedRectangle is null || !normalizedRectangleToPersonContainers.TryGetValue(normalizedRectangle.Value, out personContainers)) personContainers = null; else { foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) continue; personBirthday = personContainer.Birthdays[zero]; personKey = personBirthday.Value.Ticks; personKeyFormatted = IPersonBirthday.GetFormatted(_Configuration.PersonBirthdayFormat, personBirthday); break; } } } markerWith = GetMarkerWith(dlib, databaseFile, marker, collection.Count, percent, normalizedRectangle, personKey, personKeyFormatted); string json = JsonSerializer.Serialize(markerWith, new JsonSerializerOptions() { WriteIndented = true }); if (IPath.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "With", $"{marker.MarkerUid}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true)) continue; } } public void SaveMarkers() { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); double[]? encoding; string file = Path.Combine(_Configuration.PhotoPrismDirectory, "markers.json"); string json = File.ReadAllText(file); Marker[]? markers = JsonSerializer.Deserialize(json); if (markers is null) throw new NullReferenceException(nameof(markers)); foreach (Marker marker in markers) { encoding = JsonSerializer.Deserialize(marker.EmbeddingsJson[1..^1]); File.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "EmbeddingsJson", $"{marker.MarkerUid}.json"), marker.EmbeddingsJson); if (encoding is null) continue; } } public void LoadMatches() { if (_Configuration is null) throw new NullReferenceException(nameof(_Configuration)); string json; MarkerWith? markerWith; List collection = new(); StringBuilder stringBuilder = new(); List<(int Count, MarkerWith MarkerWith)> countCollection = new(); List<(double Percent, MarkerWith MarkerWith)> percentCollection = new(); string[] files = Directory.GetFiles(Path.Combine(_Configuration.PhotoPrismDirectory, "With"), "*.json", SearchOption.TopDirectoryOnly); foreach (string file in files) { json = File.ReadAllText(file); markerWith = JsonSerializer.Deserialize(json); if (markerWith is null || markerWith.DlibId is null) continue; collection.Add(markerWith); if (markerWith.Count is null || markerWith.Count.Value == 0) continue; countCollection.Add(new(markerWith.Count.Value, markerWith)); if (markerWith.Percent is null) continue; percentCollection.Add(new(markerWith.Percent.Value, markerWith)); if (string.IsNullOrEmpty(markerWith.PersonKeyFormatted)) continue; _ = stringBuilder. Append("update `markers` set subj_src = 'manual' marker_name = '"). Append(markerWith.PersonKeyFormatted). Append("' where marker_uid = '"). Append(markerWith.MarkerUid). AppendLine("';"); } (int, MarkerWith)[] countSorted = countCollection.OrderByDescending(l => l.Count).ToArray(); (double, MarkerWith)[] percentSorted = percentCollection.OrderBy(l => l.Percent).ToArray(); if (collection.Any()) { } File.WriteAllText(Path.Combine(_Configuration.PhotoPrismDirectory, "marker_name_update.sql"), stringBuilder.ToString()); } public Dictionary> GetMissing(Dictionary> idToNormalizedRectangleToMapping) { Dictionary> results = new(); foreach (KeyValuePair> normalizedRectangleToPersonContainers in _IdThenNormalizedRectangleToPersonContainers) { if (idToNormalizedRectangleToMapping.ContainsKey(normalizedRectangleToPersonContainers.Key)) continue; results.Add(normalizedRectangleToPersonContainers.Key, normalizedRectangleToPersonContainers.Value); } return results; } public void UpdatedPersonKeyToRanges(Configuration configuration, long ticks, Mapping[] mappingCollection) { Dictionary personKeyToRanges = new(); Stateless.MapLogic.SetPersonTicks(configuration, ticks, mappingCollection, personKeyToRanges, _IdThenNormalizedRectangleToPersonContainers); _PersonKeyToRanges = personKeyToRanges; } }