using System.Drawing; using System.Text; using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.PhotoPrism.Models; public class F_PhotoPrism { private static JsonProperty[] GetJsonProperty(string fileName) { JsonProperty[] results; if (!File.Exists(fileName)) results = Array.Empty(); else { string json = File.ReadAllText(fileName); JsonElement[]? jsonElements = JsonSerializer.Deserialize(json); if (jsonElements is null || !jsonElements.Any()) results = Array.Empty(); else { JsonElement jsonElement = jsonElements.Last(); if (jsonElement.ValueKind != JsonValueKind.Object) results = Array.Empty(); else results = jsonElement.EnumerateObject().ToArray(); } } return results; } private static Marker[]? GetMarkers(string fPhotoPrismSingletonDirectory) { Marker[]? results; string file = Path.Combine(fPhotoPrismSingletonDirectory, "markers.json"); JsonProperty[] jsonProperties = GetJsonProperty(file); if (!jsonProperties.Any()) results = null; else results = JsonSerializer.Deserialize(jsonProperties.Last().Value); return results; } private static Dictionary> GetFileUIdToMarkers(string fPhotoPrismSingletonDirectory) { Dictionary> results = new(); string fileUid; Marker[]? markers = GetMarkers(fPhotoPrismSingletonDirectory); if (markers is null) throw new NullReferenceException(nameof(markers)); foreach (Marker marker in markers) { fileUid = HexStringToString(marker.FileUid); if (!results.ContainsKey(fileUid)) results.Add(fileUid, new()); results[fileUid].Add(Marker.Map(marker)); } return results; } private static Dictionary>? GetCollectionFile(string fileName) { Dictionary>? results; if (!File.Exists(fileName)) results = null; else { string json = File.ReadAllText(fileName); results = JsonSerializer.Deserialize>>(json); } return results; } private static List? GetDatabaseFiles(string fPhotoPrismSingletonDirectory) { List? results; string file = Path.Combine(fPhotoPrismSingletonDirectory, "files.json"); JsonProperty[] jsonProperties = GetJsonProperty(file); if (!jsonProperties.Any()) results = null; else { results = new(); DatabaseFile[]? databaseFiles = JsonSerializer.Deserialize(jsonProperties.Last().Value); if (databaseFiles is null) results = null; else { foreach (DatabaseFile databaseFile in databaseFiles) results.Add(DatabaseFile.Map(databaseFile)); } } return results; } public static Dictionary> GetFileNameToCollection(string fPhotoPrismSingletonDirectory) { Dictionary>? results; string fileName = Path.Combine(fPhotoPrismSingletonDirectory, "collection.json"); results = GetCollectionFile(fileName); if (results is null) { int id; results = new(); string fileNameWithoutExtension; List? makers; MappingFromPhotoPrism mappingFromPhotoPrism; List? mappingFromPhotoPrismCollection; List? databaseFiles = GetDatabaseFiles(fPhotoPrismSingletonDirectory); if (databaseFiles is not null) { Dictionary> fileUIdToMarkers = GetFileUIdToMarkers(fPhotoPrismSingletonDirectory); foreach (Shared.Models.DatabaseFile databaseFile in databaseFiles) { if (databaseFile.FileName is null || databaseFile.FileUid is null) continue; fileNameWithoutExtension = Path.GetFileNameWithoutExtension(databaseFile.FileName); if (!int.TryParse(fileNameWithoutExtension, out id)) continue; if (!results.TryGetValue(id, out mappingFromPhotoPrismCollection)) { results.Add(id, new()); if (!results.TryGetValue(id, out mappingFromPhotoPrismCollection)) throw new Exception(); } if (!fileUIdToMarkers.TryGetValue(databaseFile.FileUid, out makers)) mappingFromPhotoPrism = new(databaseFile, new()); else mappingFromPhotoPrism = new(databaseFile, makers); mappingFromPhotoPrismCollection.Add(mappingFromPhotoPrism); } } string json = JsonSerializer.Serialize(results); _ = IPath.WriteAllText(fileName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); } return results; } private static void PopulateSubjects(string mappingDefaultName, string personBirthdayFormat, List subjects, StringBuilder stringBuilder, PersonContainer[] personContainers, (MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, float Percent)[] sortedCollection) { long? personKey; const int zero = 0; string personKeyFormatted; PersonBirthday personBirthday; foreach ((MappingFromPhotoPrism mappingFromPhotoPrism, Shared.Models.Marker marker, float percent) in sortedCollection) { foreach (PersonContainer personContainer in personContainers) { if (personContainer.Key is null || personContainer.Birthdays is null || !personContainer.Birthdays.Any()) continue; if (IPerson.IsDefaultName(mappingDefaultName, personContainer.DisplayDirectoryName)) continue; personBirthday = personContainer.Birthdays[zero]; personKey = personBirthday.Value.Ticks; personKeyFormatted = IPersonBirthday.GetFormatted(personBirthdayFormat, personBirthday); subjects.Add($"update `subjects` set subj_alias = '{personKeyFormatted}' where subj_name = '{personContainer.DisplayDirectoryName}';"); _ = stringBuilder. Append("update `markers` set subj_src = 'manual', marker_name = '"). Append(personContainer.DisplayDirectoryName). Append("' where marker_uid = '"). Append(marker.MarkerUid). AppendLine("';"); } } } public static void WriteMatches(string fPhotoPrismContentDirectory, string mappingDefaultName, string personBirthdayFormat, float[] rectangleIntersectMinimums, long ticks, List distinctFilteredFaces, Shared.Models.Methods.IMapLogic mapLogic) { string file; string text; float dlibArea; float? percent; string directory; int width, height; int? wholePercentages; RectangleF? prismRectangle; List subjects = new(); DateTime dateTime = new(ticks); int dlibLocationWholePercentages; PersonContainer[]? personContainers; StringBuilder stringBuilder = new(); RectangleF? dlibPercentagesRectangle; float rectangleIntersectMinimum = rectangleIntersectMinimums.Min(); Dictionary? wholePercentagesToPersonContainers; (MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, float Percent)[] sortedCollection; List<(MappingFromPhotoPrism MappingFromPhotoPrism, Shared.Models.Marker Marker, float Percent)> collection = new(); foreach (Face face in distinctFilteredFaces) { collection.Clear(); wholePercentages = face.Mapping?.MappingFromLocation?.WholePercentages; if (wholePercentages is null) continue; if (face.FaceEncoding is null || face.Location is null || face.OutputResolution is null || face.Mapping is null) continue; if (face.Mapping.MappingFromPhotoPrismCollection is null) continue; (_, wholePercentagesToPersonContainers) = mapLogic.GetWholePercentagesToPersonContainers(face.Mapping.MappingFromItem.Id); if (wholePercentagesToPersonContainers is null || !wholePercentagesToPersonContainers.TryGetValue(wholePercentages.Value, out personContainers)) continue; (width, height) = IOutputResolution.Get(face.OutputResolution); dlibLocationWholePercentages = ILocation.GetWholePercentages(height, face.Location, Shared.Models.Stateless.ILocation.Digits, width); dlibPercentagesRectangle = ILocation.GetPercentagesRectangle(Shared.Models.Stateless.ILocation.Digits, dlibLocationWholePercentages); if (dlibPercentagesRectangle is null) continue; dlibArea = dlibPercentagesRectangle.Value.Width * dlibPercentagesRectangle.Value.Height; foreach (MappingFromPhotoPrism mappingFromPhotoPrism in face.Mapping.MappingFromPhotoPrismCollection) { foreach (Shared.Models.Marker marker in mappingFromPhotoPrism.Markers) { prismRectangle = ILocation.GetPercentagesRectangle(mappingFromPhotoPrism.DatabaseFile, marker, face.OutputResolution); if (prismRectangle is null) continue; percent = ILocation.GetIntersectPercent(dlibPercentagesRectangle.Value, dlibArea, prismRectangle.Value); if (percent is null || percent < rectangleIntersectMinimum) continue; collection.Add(new(mappingFromPhotoPrism, marker, percent.Value)); } } if (!collection.Any()) continue; sortedCollection = collection.OrderByDescending(l => l.Percent).ToArray(); PopulateSubjects(mappingDefaultName, personBirthdayFormat, subjects, stringBuilder, personContainers, sortedCollection); } if (subjects.Any()) { directory = Path.Combine(fPhotoPrismContentDirectory, dateTime.ToString("yyyy-MM-dd")); if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); file = Path.Combine(directory, $"{ticks}-{rectangleIntersectMinimum}-subject_alias_update.sql"); text = string.Join(Environment.NewLine, subjects.Distinct()); _ = IPath.WriteAllText(file, text, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); file = Path.Combine(directory, $"{ticks}-{rectangleIntersectMinimum}-marker_name_update.sql"); text = stringBuilder.ToString(); _ = IPath.WriteAllText(file, text, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); } } internal static string HexStringToString(string text) { string result; if (text.Length < 2 || text[0] != '0' || text[1] != 'x') result = text; else { string after = text[2..]; byte[] bytes = Enumerable.Range(0, after.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(after.Substring(x, 2), 16)) .ToArray(); result = Encoding.UTF8.GetString(bytes); } return result; } }