diff --git a/.vscode/settings.json b/.vscode/settings.json index aad7716..82493c8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,6 +22,8 @@ "Hmmssfff", "jfif", "JOSN", + "Makernote", + "Makernotes", "mmod", "Nicéphore", "Niépce", @@ -31,6 +33,7 @@ "permyriad", "Phares", "Phgtv", + "photoshop", "RDHC", "Rects", "resnet", diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index 206914a..3ab6b1c 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -8,7 +8,7 @@ using View_by_Distance.Shared.Models.Methods; namespace View_by_Distance.Distance.Models; -public partial class E_Distance : IDistance +public partial class E_Distance : IDistance { internal record Record(FilePath FilePath, FaceRecognitionDotNet.FaceEncoding FaceRecognitionDotNetFaceEncoding); @@ -201,7 +201,7 @@ public partial class E_Distance : IDistance } } - public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, FilePath filePath, MappingFromItem mappingFromItem, List faces, ReadOnlyCollection> locationContainers) + public void LookForMatchFacesAndPossiblyRename(string facesFileNameExtension, FilePath filePath, MappingFromItem mappingFromItem, List faces, ReadOnlyCollection locationContainers) { string? json; string[] matches; @@ -212,20 +212,20 @@ public partial class E_Distance : IDistance Face[] filteredFaces = (from l in faces where l.FaceEncoding is not null && l.Location is not null && l.OutputResolution is not null select l).ToArray(); if (filteredFaces.Length != faces.Count) checkFaces.Clear(); - foreach (LocationContainer locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { if (_Renamed.Contains(locationContainer.FilePath.FullName)) continue; if (locationContainer.FromDistanceContent && _DuplicateMappedFaceFiles.Contains(locationContainer.FilePath.Name)) continue; checkFaces.Clear(); - if (locationContainer.Directories.Count == 0) + if (locationContainer.ExifDirectory is null) { if (locationContainer.FromDistanceContent) - throw new NullReferenceException(nameof(locationContainer.Directories)); + throw new NullReferenceException(nameof(locationContainer.ExifDirectory)); continue; } - json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.Directories); + json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory); if (json is null) { if (_DistanceMoveUnableToMatch) @@ -520,16 +520,16 @@ public partial class E_Distance : IDistance return new(results); } - ReadOnlyCollection IDistance.GetRelationContainers(int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection> locationContainers) + ReadOnlyCollection IDistance.GetRelationContainers(int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection locationContainers) { ReadOnlyCollection result; string? json; List records = []; Shared.Models.FaceEncoding? modelsFaceEncoding; FaceRecognitionDotNet.FaceEncoding faceRecognitionDotNetFaceEncoding; - foreach (LocationContainer locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { - json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.Directories); + json = Metadata.Models.Stateless.Methods.IMetadata.GetFaceEncoding(locationContainer.ExifDirectory); if (json is null) continue; modelsFaceEncoding = JsonSerializer.Deserialize(json); diff --git a/Drag-Drop-Set-Property-Item/DragDropSetPropertyItem.cs b/Drag-Drop-Set-Property-Item/DragDropSetPropertyItem.cs index 30ca0c5..7ae4359 100644 --- a/Drag-Drop-Set-Property-Item/DragDropSetPropertyItem.cs +++ b/Drag-Drop-Set-Property-Item/DragDropSetPropertyItem.cs @@ -7,7 +7,6 @@ using System.Text; using System.Text.Json; using View_by_Distance.Drag.Drop.Set.Item.Models; using View_by_Distance.Shared.Models; -using View_by_Distance.Shared.Models.Stateless; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Drag.Drop.Set.Item; @@ -178,7 +177,7 @@ public partial class DragDropSetPropertyItem : Form { short type = 1; ASCIIEncoding asciiEncoding = new(); - int xpKeywords = (int)IExif.Tags.XPKeywords; + int xpKeywords = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinKeywords; ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, [], null) ?? throw new Exception(); List records = GetRecords(asciiEncoding, xpKeywords, files); if (records.Count == 0) diff --git a/Face/Models/_D_Face.cs b/Face/Models/_D_Face.cs index 94a9d8b..aa9e80d 100644 --- a/Face/Models/_D_Face.cs +++ b/Face/Models/_D_Face.cs @@ -162,10 +162,10 @@ public class D_Face string faceEncodingJson; PropertyItem? propertyItem; string outputResolutionJson; - int artist = (int)IExif.Tags.Artist; - int fileSource = (int)IExif.Tags.FileSource; - int userComment = (int)IExif.Tags.UserComment; using Bitmap source = new(resizedFileHolder.FullName); + int artist = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist; + int fileSource = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagFileSource; + int userComment = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagUserComment; foreach ((Shared.Models.Face face, FileInfo? fileInfo, string fileName, bool save) in collection) { if (!save) @@ -260,9 +260,9 @@ public class D_Face #pragma warning restore CA1416 - private static List> GetLocationContainers(string outputResolution, ReadOnlyCollection> locationContainers, Dictionary outputResolutionToResize, List faces) + private static List GetLocationContainers(string outputResolution, ReadOnlyCollection locationContainers, Dictionary outputResolutionToResize, List faces) { - List> results = []; + List results = []; string? json; Location? location; Rectangle? rectangle; @@ -275,9 +275,9 @@ public class D_Face continue; skip.Add(Shared.Models.Stateless.Methods.ILocation.GetWholePercentages(face.Location, Shared.Models.Stateless.ILocation.Digits, face.OutputResolution)); } - foreach (LocationContainer locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { - if (locationContainer.Directories is null) + if (locationContainer.ExifDirectory is null) continue; if (skip.Contains(locationContainer.WholePercentages)) continue; @@ -285,7 +285,7 @@ public class D_Face { if (face.Location is not null && face.OutputResolution is not null) continue; - json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(locationContainer.Directories); + json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(locationContainer.ExifDirectory); if (json is null) continue; outputResolutionCheck = JsonSerializer.Deserialize(json); @@ -299,7 +299,7 @@ public class D_Face continue; if (!results.Any(l => l.WholePercentages == locationContainer.WholePercentages)) results.Add(new(locationContainer.CreationDateOnly, - locationContainer.Directories, + locationContainer.ExifDirectory, locationContainer.DirectoryNumber, locationContainer.DisplayDirectoryName, locationContainer.FilePath, @@ -316,7 +316,7 @@ public class D_Face return results; } - public List GetFaces(string outputResolution, string dResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, ReadOnlyCollection>? locationContainers, List? mappingFromPhotoPrismCollection) + public List GetFaces(string outputResolution, string dResultsFullGroupDirectory, FilePath filePath, List> subFileTuples, List parseExceptions, Shared.Models.Property property, MappingFromItem mappingFromItem, Dictionary outputResolutionToResize, ReadOnlyCollection? locationContainers, List? mappingFromPhotoPrismCollection) { List? results; if (string.IsNullOrEmpty(dResultsFullGroupDirectory)) @@ -359,7 +359,7 @@ public class D_Face parseExceptions.Add(nameof(D_Face)); } } - List> collection; + List collection; if (results is null || locationContainers is null) collection = []; else diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 8dccc2a..6a73758 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -430,10 +430,10 @@ public partial class DlibDotNet string[] checkDirectories = [ Path.Combine(rootDirectory, "Ancestry"), - Path.Combine(rootDirectory, "Facebook"), - Path.Combine(rootDirectory, "LinkedIn"), - rootDirectory, - ]; + Path.Combine(rootDirectory, "Facebook"), + Path.Combine(rootDirectory, "LinkedIn"), + rootDirectory, + ]; foreach (string checkDirectory in checkDirectories) { if (checkDirectory == rootDirectory) @@ -788,7 +788,7 @@ public partial class DlibDotNet _Logger?.LogInformation(string.Concat("Moved <", item.ImageFileHolder.FullName, '>')); } - private int GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(MapLogic mapLogic, Item item, bool? isFocusRelativePath, ReadOnlyCollection> locationContainers, MappingFromItem mappingFromItem, List? mappingFromPhotoPrismCollection, List faces) + private int GetNotMappedCountAndUpdateMappingFromPersonThenSetMapping(MapLogic mapLogic, Item item, bool? isFocusRelativePath, ReadOnlyCollection locationContainers, MappingFromItem mappingFromItem, List? mappingFromPhotoPrismCollection, List faces) { int result; double? α; @@ -951,7 +951,7 @@ public partial class DlibDotNet string[] changesFrom = [nameof(A_Property)]; List> subFileTuples = []; FileHolder resizedFileHolder = _Resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber); - ReadOnlyCollection> locationContainers = mapLogic.GetLocationContainers(item); + ReadOnlyCollection locationContainers = mapLogic.GetLocationContainers(item); if (item.Property is null || item.Property.Id is null || !item.SourceDirectoryFileHolder.Exists || item.SourceDirectoryFileHolder.CreationTime is null || item.SourceDirectoryFileHolder.LastWriteTime is null || item.Any()) { LogItemPropertyIsNull(item); @@ -993,14 +993,14 @@ public partial class DlibDotNet throw new NullReferenceException(nameof(property)); item.SetResizedFileHolder(_Resize.FileNameExtension, resizedFileHolder); MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(containerDateTimes, item, resizedFileHolder); + ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); Map.Models.Stateless.Methods.IMapLogic.SetCreationTimeMaybeMoveToDecade(_Configuration.PropertyConfiguration, _Configuration.MoveToDecade && _Configuration.LocationContainerDistanceTolerance is null, mappingFromItem, locationContainers); - ReadOnlyDictionary metadataExtractorDirectories = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); if (_AppSettings.Places.Count > 0) { float latitude; float longitude; double? distance; - MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(metadataExtractorDirectories); + MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory); foreach (Place place in _AppSettings.Places) { if (geoLocation is null) diff --git a/Map/Models/MapLogic.cs b/Map/Models/MapLogic.cs index 29ceadf..7fc33f2 100644 --- a/Map/Models/MapLogic.cs +++ b/Map/Models/MapLogic.cs @@ -194,11 +194,11 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic private void LookForAbandoned(Property.Models.Configuration propertyConfiguration, List distinctFilteredIds) { List renameCollection = []; - foreach (KeyValuePair>> keyValuePair in _IdToLocationContainers) + foreach (KeyValuePair> keyValuePair in _IdToLocationContainers) { if (distinctFilteredIds.Contains(keyValuePair.Key)) continue; - foreach (LocationContainer locationContainer in keyValuePair.Value) + foreach (LocationContainer locationContainer in keyValuePair.Value) { if (locationContainer.FilePath.FullName.Contains('!')) continue; @@ -217,10 +217,10 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic private readonly ReadOnlyDictionary> _SkipCollection; private readonly ReadOnlyDictionary> _SkipNotSkipCollection; private readonly Shared.Models.Properties.IPropertyConfiguration _PropertyConfiguration; - private readonly ReadOnlyDictionary>> _IdToLocationContainers; + private readonly ReadOnlyDictionary> _IdToLocationContainers; private readonly ReadOnlyDictionary>> _IdThenWholePercentagesToPersonContainers; - public MapLogic(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration? configuration, Shared.Models.Methods.IDistance distance, ReadOnlyCollection personContainers, long ticks, string? a2PeopleContentDirectory, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) + public MapLogic(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration? configuration, Shared.Models.Methods.IDistance distance, ReadOnlyCollection personContainers, long ticks, string? a2PeopleContentDirectory, string a2PeopleSingletonDirectory, string eDistanceContentDirectory) { _Ticks = ticks; _Configuration = configuration; @@ -229,7 +229,7 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic List notMappedPersonContainers = []; Dictionary> skipCollection = []; Dictionary> skipNotSkipCollection = []; - List> locationContainers = []; + List locationContainers = []; string? rootDirectoryParent = Path.GetDirectoryName(propertyConfiguration.RootDirectory); string eDistanceContentTicksDirectory = Path.Combine(eDistanceContentDirectory, ticks.ToString()); ReadOnlyDictionary>> idThenWholePercentagesToPersonContainers; @@ -332,14 +332,14 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic return result; } - public ReadOnlyCollection> GetLocationContainers(Item item) + public ReadOnlyCollection GetLocationContainers(Item item) { - LocationContainer[] results; + LocationContainer[] results; if (item.Property?.Id is null) results = []; else { - List>? locationContainers; + List? locationContainers; if (_IdToLocationContainers.TryGetValue(item.Property.Id.Value, out locationContainers)) results = locationContainers.ToArray(); else @@ -678,17 +678,17 @@ public partial class MapLogic : Shared.Models.Methods.IMapLogic if (j == i) continue; if (face.Mapping.MappingFromFilterPre.InSkipCollection is not null && face.Mapping.MappingFromFilterPre.InSkipCollection.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); if (face.Mapping.MappingFromFilterPre.IsFocusModel is not null && !face.Mapping.MappingFromFilterPre.IsFocusModel.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); if (face.Mapping.MappingFromFilterPre.IsFocusRelativePath is not null && !face.Mapping.MappingFromFilterPre.IsFocusRelativePath.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); if (face.Mapping.MappingFromFilterPost.InSkipCollection is not null && face.Mapping.MappingFromFilterPost.InSkipCollection.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); if (face.Mapping.MappingFromFilterPost.IsFocusPerson is not null && !face.Mapping.MappingFromFilterPost.IsFocusPerson.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); if (face.Mapping.MappingFromFilterPost.InSkipCollection is not null && face.Mapping.MappingFromFilterPost.InSkipCollection.Value) - throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); + throw new NotSupportedException(nameof(Shared.Models.Methods.IDistance)); faceDistanceLength = faceDistanceLengths[j]; if (faceDistanceLength.WholePercentages is null || faceDistanceLength.Length is null) throw new NotSupportedException(); diff --git a/Map/Models/Stateless/DecadeLogic.cs b/Map/Models/Stateless/DecadeLogic.cs index d84c8f1..936ef2d 100644 --- a/Map/Models/Stateless/DecadeLogic.cs +++ b/Map/Models/Stateless/DecadeLogic.cs @@ -32,7 +32,7 @@ internal abstract class DecadeLogic return result; } - internal static void SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, MappingFromItem mappingFromItem, ReadOnlyCollection> locationContainers) + internal static void SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) { DateTime dateTime; string halfDecade; @@ -42,7 +42,7 @@ internal abstract class DecadeLogic string personNameDirectoryName; string? personKeyFormattedDirectory; string? personKeyFormattedDirectoryName; - foreach (LocationContainer locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { if (!File.Exists(locationContainer.FilePath.FullName)) continue; diff --git a/Map/Models/Stateless/MapLogic.cs b/Map/Models/Stateless/MapLogic.cs index 193bf3d..a956cf8 100644 --- a/Map/Models/Stateless/MapLogic.cs +++ b/Map/Models/Stateless/MapLogic.cs @@ -470,7 +470,7 @@ internal abstract class MapLogic return results; } - private static void ParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, Dictionary> skipCollection, List> locationContainers, MappedFile mappedFile) + private static void ParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, Dictionary> skipCollection, List locationContainers, MappedFile mappedFile) { int? id; string checkFile; @@ -480,9 +480,9 @@ internal abstract class MapLogic FileHolder fileHolder; int? wholePercentages; const string lnk = ".lnk"; + ExifDirectory? exifDirectory; string personDisplayDirectoryName; const bool fromDistanceContent = true; - IReadOnlyList directories; List<(string File, int WholePercentages)>? wholePercentagesCollection; if (!mappedFile.FilePath.Name.EndsWith(lnk)) { @@ -518,14 +518,14 @@ internal abstract class MapLogic } dateOnly = DateOnly.FromDateTime(new DateTime(mappedFile.FilePath.CreationTicks)); if (mappedFile.FilePath.Name.EndsWith(lnk) || (!configuration.DistanceMoveUnableToMatch && !configuration.DistanceRenameToMatch) || !File.Exists(mappedFile.FilePath.FullName)) - directories = new List(); + exifDirectory = null; else - directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(mappedFile.FilePath.FullName); + exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(mappedFile.FilePath); RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value); personDisplayDirectoryName = mappedFile.PersonDisplayDirectoryName is null ? configuration.MappingDefaultName : mappedFile.PersonDisplayDirectoryName; lock (locationContainers) locationContainers.Add(new(dateOnly, - directories, + exifDirectory, mappedFile.DirectoryNumber, personDisplayDirectoryName, mappedFile.FilePath, @@ -537,7 +537,7 @@ internal abstract class MapLogic wholePercentages.Value)); } - private static void LookForPossibleDuplicates(Configuration configuration, ReadOnlyCollection> locationContainers) + private static void LookForPossibleDuplicates(Configuration configuration, ReadOnlyCollection locationContainers) { string key; float? percent; @@ -547,7 +547,7 @@ internal abstract class MapLogic RectangleF? itemPercentagesRectangle; (FilePath FilePath, int WholePercentages) item; Dictionary distinct = []; - foreach (LocationContainer locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { key = string.Concat(locationContainer.PersonKey, locationContainer.Id); if (distinct.TryGetValue(key, out item)) @@ -890,9 +890,9 @@ internal abstract class MapLogic } } - internal static List> GetLocationContainers(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, Dictionary> skipCollection, List records) + internal static List GetLocationContainers(int maxDegreeOfParallelism, Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection personContainers, Dictionary> skipCollection, List records) { - List> results = []; + List results = []; List mappedFiles = GetMappedFiles(propertyConfiguration, configuration, personContainers, records); if (mappedFiles.Count > 0 && (configuration.DistanceMoveUnableToMatch || configuration.DistanceRenameToMatch)) { @@ -909,7 +909,7 @@ internal abstract class MapLogic } if (string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory)) { - ReadOnlyCollection> locationContainers = new(results.OrderBy(l => l.DirectoryNumber).ToArray()); + ReadOnlyCollection locationContainers = new(results.OrderBy(l => l.DirectoryNumber).ToArray()); LookForPossibleDuplicates(configuration, locationContainers); } return results; @@ -1248,10 +1248,10 @@ internal abstract class MapLogic return new(results); } - internal static ReadOnlyDictionary>> ConvertLocationContainers(List> locationContainers) + internal static ReadOnlyDictionary> ConvertLocationContainers(List locationContainers) { - Dictionary>> results = []; - foreach (LocationContainer locationContainer in locationContainers) + Dictionary> results = []; + foreach (LocationContainer locationContainer in locationContainers) { if (!results.ContainsKey(locationContainer.Id)) results.Add(locationContainer.Id, []); diff --git a/Map/Models/Stateless/Methods/IMapLogic.cs b/Map/Models/Stateless/Methods/IMapLogic.cs index e8fd965..889f001 100644 --- a/Map/Models/Stateless/Methods/IMapLogic.cs +++ b/Map/Models/Stateless/Methods/IMapLogic.cs @@ -35,9 +35,9 @@ public interface IMapLogic static List<(string, long)> GetJLinkDirectories(string genealogicalDataCommunicationFile, string[] jLinks, string personBirthdayFormat, char[] personCharacters, string a2PeopleSingletonDirectory, string a2PeopleContentDirectory) => MapLogic.GetJLinkDirectories(genealogicalDataCommunicationFile, jLinks, personBirthdayFormat, personCharacters, a2PeopleSingletonDirectory, a2PeopleContentDirectory); - void TestStatic_SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, Shared.Models.MappingFromItem mappingFromItem, ReadOnlyCollection> locationContainers) => + void TestStatic_SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, Shared.Models.MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => SetCreationTimeMaybeMoveToDecade(propertyConfiguration, moveToDecade, mappingFromItem, locationContainers); - static void SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, Shared.Models.MappingFromItem mappingFromItem, ReadOnlyCollection> locationContainers) => + static void SetCreationTimeMaybeMoveToDecade(Property.Models.Configuration propertyConfiguration, bool moveToDecade, Shared.Models.MappingFromItem mappingFromItem, ReadOnlyCollection locationContainers) => DecadeLogic.SetCreationTimeMaybeMoveToDecade(propertyConfiguration, moveToDecade, mappingFromItem, locationContainers); bool? TestStatic_CanReMap(long[] jLinkResolvedPersonKeys, ReadOnlyDictionary>? wholePercentagesToPersonContainers, Shared.Models.MappingFromLocation mappingFromLocation) => diff --git a/Map/Models/Stateless/RelationLogic.cs b/Map/Models/Stateless/RelationLogic.cs index 29e4c13..216282b 100644 --- a/Map/Models/Stateless/RelationLogic.cs +++ b/Map/Models/Stateless/RelationLogic.cs @@ -8,14 +8,14 @@ namespace View_by_Distance.Map.Models.Stateless; internal abstract class RelationLogic { - internal record Group(string Key, long PersonKey, ReadOnlyCollection> RelationContainersCollection); + internal record Group(string Key, long PersonKey, ReadOnlyCollection RelationContainersCollection); - private static Dictionary>>> GetPersonKeyTo(Configuration configuration, List> locationContainers) + private static Dictionary>> GetPersonKeyTo(Configuration configuration, List locationContainers) { - List>? collection; - Dictionary>>? yearTo; - Dictionary>>> personKeyTo = []; - foreach (LocationContainer locationContainer in locationContainers) + List? collection; + Dictionary>? yearTo; + Dictionary>> personKeyTo = []; + foreach (LocationContainer locationContainer in locationContainers) { if (!locationContainer.FromDistanceContent) continue; @@ -38,7 +38,7 @@ internal abstract class RelationLogic return personKeyTo; } - private static ReadOnlyCollection GetGroups(Configuration configuration, List> locationContainers) + private static ReadOnlyCollection GetGroups(Configuration configuration, List locationContainers) { List results = []; string key; @@ -46,10 +46,10 @@ internal abstract class RelationLogic List years = []; List indices = []; List<(int Index, int Year)> sort = []; - List> collection = []; - KeyValuePair>> keyValue; - Dictionary>>> personKeyTo = GetPersonKeyTo(configuration, locationContainers); - foreach (KeyValuePair>>> keyValuePair in personKeyTo) + List collection = []; + KeyValuePair> keyValue; + Dictionary>> personKeyTo = GetPersonKeyTo(configuration, locationContainers); + foreach (KeyValuePair>> keyValuePair in personKeyTo) { sort.Clear(); years.Clear(); @@ -403,7 +403,7 @@ internal abstract class RelationLogic } } - internal static void SaveMappedRelations(Configuration configuration, Shared.Models.Methods.IDistance distance, string a2PeopleContentDirectory, string eDistanceContentDirectory, long ticks, List> locationContainers, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection) + internal static void SaveMappedRelations(Configuration configuration, Shared.Models.Methods.IDistance distance, string a2PeopleContentDirectory, string eDistanceContentDirectory, long ticks, List locationContainers, ReadOnlyDictionary readOnlyPersonKeyFormattedToPersonContainer, ReadOnlyDictionary> readOnlyPersonKeyToPersonContainerCollection) { int take; string directory; diff --git a/Metadata-Query/Metadata-Query.csproj b/Metadata-Query/Metadata-Query.csproj index 747eabf..44fc628 100644 --- a/Metadata-Query/Metadata-Query.csproj +++ b/Metadata-Query/Metadata-Query.csproj @@ -42,6 +42,7 @@ + diff --git a/Metadata-Query/MetadataQuery.cs b/Metadata-Query/MetadataQuery.cs index 859117d..c1dfd75 100644 --- a/Metadata-Query/MetadataQuery.cs +++ b/Metadata-Query/MetadataQuery.cs @@ -4,8 +4,8 @@ using Phares.Shared; using ShellProgressBar; using System.Collections.ObjectModel; using System.Text; -using System.Text.Json; using View_by_Distance.Metadata.Query.Models; +using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; namespace View_by_Distance.Metadata.Query; @@ -56,18 +56,18 @@ public class MetadataQuery { } } - private List<(string FileName, string Count, string TagGroup, string TagIdName, string Value)> GetCollection(long ticks) + private List<(string FileName, string TagGroup, string TagIdName, string Value)> GetCollection(long ticks) { - int count; - string json; string message; - string fileName; + FileInfo fileInfo; + FilePath filePath; + FileHolder fileHolder; ProgressBar progressBar; + ExifDirectory exifDirectory; const string fileSearchFilter = "*"; const bool useCeilingAverage = true; const string directorySearchFilter = "*"; - Dictionary>>? dictionary; - List<(string FileName, string Count, string TagGroup, string TagIdName, string Value)> collection = []; + List<(string FileName, string TagGroup, string TagIdName, string Value)> collection = []; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; ReadOnlyCollection filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); foreach (string[] files in filesCollection) @@ -78,57 +78,33 @@ public class MetadataQuery progressBar = new(files.Length, message, options); foreach (string file in files) { - count = 0; progressBar.Tick(); - json = File.ReadAllText(file); - fileName = Path.GetFileName(file); - dictionary = JsonSerializer.Deserialize>>>(json); - if (dictionary is null) - continue; - foreach (KeyValuePair>> keyValuePair in dictionary) - { - foreach (KeyValuePair keyValue in keyValuePair.Value) - count++; - } - foreach (KeyValuePair>> keyValuePair in dictionary) - { - foreach (KeyValuePair keyValue in keyValuePair.Value) - collection.Add(new(fileName, count.ToString("000000"), keyValuePair.Key, keyValue.Key, keyValue.Value)); - } + fileInfo = new(file); + fileHolder = FileHolder.Get(fileInfo); + filePath = FilePath.Get(_PropertyConfiguration, fileHolder, index: null); + exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath); + // exifDirectory.ExifDirectoryBase.Artist; + // exifDirectory.ExifDirectoryBase.WinComment; + // exifDirectory.ExifDirectoryBase.Model; + // exifDirectory.ExifDirectoryBase.CameraOwnerName; + // exifDirectory.ExifDirectoryBase.Make; + // exifDirectory.ExifDirectoryBase.BodySerialNumber; + // exifDirectory.ExifDirectoryBase.LensSerialNumber; + // exifDirectory.ExifDirectoryBase.Software; + // collection.Add(new(fileInfo.Name, keyValuePair.Key, keyValue.Key, keyValue.Value)); } progressBar.Dispose(); } return collection; } - private static Dictionary> GetKeyValuePairs(List<(string FileName, string Count, string TagGroup, string TagIdName, string Value)> matches) - { - string key; - string line; - List? valuePairs; - Dictionary> keyValuePairs = []; - foreach ((string fileName, string count, string tagGroup, string tagIdName, string value) in matches) - { - key = $"{tagGroup}\t{tagIdName}\t{value.Trim()}"; - line = $"{tagGroup}\t{tagIdName}\t{value.Trim()}\t{count}\t{fileName}"; - if (!keyValuePairs.TryGetValue(key, out valuePairs)) - { - keyValuePairs.Add(key, []); - if (!keyValuePairs.TryGetValue(key, out valuePairs)) - throw new Exception(); - } - valuePairs.Add(line); - } - return keyValuePairs; - } - private void MetadataQueryFilesInDirectories(ILogger? logger, long ticks) { - List<(string FileName, string Count, string TagGroup, string TagIdName, string Value)> collection = GetCollection(ticks); + List<(string FileName, string TagGroup, string TagIdName, string Value)> collection = GetCollection(ticks); logger?.LogInformation($"Ready to query {collection.Count} entries?"); - IEnumerable<(string FileName, string Count, string TagGroup, string TagIdName, string Value)> enumerable() + IEnumerable<(string FileName, string TagGroup, string TagIdName, string Value)> enumerable() { - foreach ((string FileName, string Count, string TagGroup, string TagIdName, string Value) l in collection) + foreach ((string FileName, string TagGroup, string TagIdName, string Value) l in collection) { if (l.TagIdName.StartsWith("42016\t") && l.Value != "00000000000000000000000000000000") { @@ -136,19 +112,18 @@ public class MetadataQuery } } } - List<(string FileName, string Count, string TagGroup, string TagIdName, string Value)> matches = enumerable().ToList(); + List<(string FileName, string TagGroup, string TagIdName, string Value)> matches = enumerable().ToList(); if (matches.Count != 0) { matches.Sort(); StringBuilder stringBuilder = new(); - Dictionary> keyValuePairs = GetKeyValuePairs(matches); - foreach (KeyValuePair> keyValuePair in keyValuePairs) - { - if (keyValuePair.Value.Count != 2) - continue; - foreach (string line in keyValuePair.Value) - _ = stringBuilder.AppendLine(line); - } + // foreach (KeyValuePair> keyValuePair in keyValuePairs) + // { + // if (keyValuePair.Value.Count != 2) + // continue; + // foreach (string line in keyValuePair.Value) + // _ = stringBuilder.AppendLine(line); + // } string checkFile = $"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv"; string text = stringBuilder.ToString(); _ = Shared.Models.Stateless.Methods.IPath.WriteAllText(checkFile, text, updateToWhenMatches: null, compareBeforeWrite: true, updateDateWhenMatches: false); diff --git a/Metadata/Models/B_Metadata.cs b/Metadata/Models/B_Metadata.cs index 7e7b290..1043794 100644 --- a/Metadata/Models/B_Metadata.cs +++ b/Metadata/Models/B_Metadata.cs @@ -39,14 +39,13 @@ public class B_Metadata : IMetadata public override string ToString() { - string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); + string result = JsonSerializer.Serialize(this, _WriteIndentedJsonSerializerOptions); return result; } - public ReadOnlyDictionary GetMetadataCollection(FilePath filePath, List> subFileTuples, List parseExceptions, string[] changesFrom, MappingFromItem mappingFromItem) + public ExifDirectory GetMetadataCollection(FilePath filePath, List> subFileTuples, List parseExceptions, string[] changesFrom, MappingFromItem mappingFromItem) { - Dictionary? results = null; - string json = string.Empty; + ExifDirectory? result = null; List dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList(); (_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration, filePath); FileInfo fileInfo = new(Path.Combine(_FileGroups[_PropertyConfiguration.ResultSingleton][directoryIndex], $"{mappingFromItem.ImageFileHolder.NameWithoutExtension}{mappingFromItem.ImageFileHolder.ExtensionLowered}.json")); @@ -61,34 +60,33 @@ public class B_Metadata : IMetadata fileInfo.Refresh(); } if (_PropertiesChangedForMetadata) - results = null; + result = null; else if (!fileInfo.Exists) - results = null; + 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) - results = null; + result = null; else { - json = File.ReadAllText(fileInfo.FullName); + string json = File.ReadAllText(fileInfo.FullName); try { - results = Stateless.Methods.Metadata.Deserialize(json); - if (results is null) + result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + if (result is null) throw new Exception(); subFileTuples.Add(new Tuple(nameof(B_Metadata), fileInfo.LastWriteTime)); } catch (Exception) { - results = null; + result = null; parseExceptions.Add(nameof(B_Metadata)); } } - if (results is null || results.Count == 0) + if (result is null) { - IReadOnlyList directories = ImageMetadataReader.ReadMetadata(mappingFromItem.ImageFileHolder.FullName); - results = Stateless.Methods.Metadata.GetKeyValuePairs(directories); - json = JsonSerializer.Serialize(results, DictionaryStringMetadataExtractorDirectorySourceGenerationContext.Default.DictionaryStringMetadataExtractorDirectory); + result = Stateless.Methods.IMetadata.GetExifDirectory(filePath); + string json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory); bool updateDateWhenMatches = dateTimes.Count != 0 && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime; DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max(); if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime)) @@ -103,7 +101,7 @@ public class B_Metadata : IMetadata } } } - return new(results); + return result; } (DateTime?, DateTime?[]) IMetadata.GetDateTimes(FileHolder fileHolder, IReadOnlyList directories) @@ -203,7 +201,7 @@ public class B_Metadata : IMetadata } } } - return new(result, results.ToArray()); + return new(result, [.. results]); } } \ No newline at end of file diff --git a/Metadata/Models/Exif.cs b/Metadata/Models/Exif.cs deleted file mode 100644 index 298f1f3..0000000 --- a/Metadata/Models/Exif.cs +++ /dev/null @@ -1,194 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace View_by_Distance.Metadata.Models; - -public record Exif(string? InteropIndex1, - string? Noise37389, - string? FocalPlaneXResolutionTiffEp37390, - string? FocalPlaneYResolutionTiffEp37391, - string? ImageNumber37393, - string? SecurityClassification37394, - string? ImageHistory37395, - string? SubjectLocationTiffEp37396, - string? ExposureIndexTiffEp37397, - string? SpatialFreqResponseTiffEp37388, - string? StandardIdTiffEp37398, - string? UserComment37510, - string? SubsecondTime37520, - string? SubsecondTimeOriginal37521, - string? SubsecondTimeDigitized37522, - string? WinTitle40091, - string? WinComment40092, - string? WinAuthor40093, - string? WinKeywords40094, - string? Makernote37500, - string? WinSubject40095, - string? FlashEnergyTiffEp37387, - string? Flash37385, - string? IsoSpeedLatitudeYYY34868, - string? IsoSpeedLatitudeZZZ34869, - string? ExifVersion36864, - string? DateTimeOriginal36867, - string? DateTimeDigitized36868, - string? TimeZone36880, - string? TimeZoneOriginal36881, - string? TimeZoneDigitized36882, - string? FocalLength37386, - string? ComponentsConfiguration37121, - string? ShutterSpeed37377, - string? Aperture37378, - string? BrightnessValue37379, - string? ExposureBias37380, - string? MaxAperture37381, - string? SubjectDistance37382, - string? MeteringMode37383, - string? WhiteBalance37384, - string? CompressedAverageBitsPerPixel37122, - string? IsoSpeed34867, - string? FlashpixVersion40960, - string? ExifImageWidth40962, - string? Sharpness41994, - string? DeviceSettingDescription41995, - string? SubjectDistanceRange41996, - string? ImageUniqueId42016, - string? CameraOwnerName42032, - string? BodySerialNumber42033, - string? LensSpecification42034, - string? LensMake42035, - string? Saturation41993, - string? LensModel42036, - string? GdalMetadata42112, - string? GdalNoData42113, - string? Gamma42240, - string? PrintImageMatchingInfo50341, - string? PanasonicTitle50898, - string? PanasonicTitle250899, - string? Padding59932, - string? Lens65002, - string? LensSerialNumber42037, - string? ColorSpace40961, - string? Contrast41992, - string? SceneCaptureType41990, - string? ExifImageHeight40963, - string? RelatedSoundFile40964, - string? FlashEnergy41483, - string? SpatialFreqResponse41484, - string? FocalPlaneXResolution41486, - string? FocalPlaneYResolution41487, - string? FocalPlaneResolutionUnit41488, - string? SubjectLocation41492, - string? GainControl41991, - string? ExposureIndex41493, - string? FileSource41728, - string? SceneType41729, - string? CfaPattern41730, - string? CustomRendered41985, - string? ExposureMode41986, - string? WhiteBalanceMode41987, - string? DigitalZoomRatio41988, - string? Film35MMEquivFocalLength41989, - string? SensingMethod41495, - string? RecommendedExposureIndex34866, - string? StandardOutputSensitivity34865, - string? SensitivityType34864, - string? YResolution283, - string? PlanarConfiguration284, - string? PageName285, - string? ResolutionUnit296, - string? PageNumber297, - string? TransferFunction301, - string? Software305, - string? DateTime306, - string? XResolution282, - string? Artist315, - string? Predictor317, - string? WhitePoint318, - string? PrimaryChromaticities319, - string? TileWidth322, - string? TileLength323, - string? TileOffsets324, - string? TileByteCounts325, - string? SubIfdOffset330, - string? HostComputer316, - string? ExtraSamples338, - string? MaxSampleValue281, - string? StripByteCounts279, - string? InteropVersion2, - string? NewSubfileType254, - string? SubfileType255, - string? ImageWidth256, - string? ImageHeight257, - string? BitsPerSample258, - string? Compression259, - string? PhotometricInterpretation262, - string? MinSampleValue280, - string? Thresholding263, - string? DocumentName269, - string? ImageDescription270, - string? Make271, - string? Model272, - string? StripOffsets273, - string? Orientation274, - string? SamplesPerPixel277, - string? RowsPerStrip278, - string? FillOrder266, - string? SampleFormat339, - string? TransferRange342, - string? JpegTables347, - string? FNumber33437, - string? PixelScale33550, - string? IptcNaa33723, - string? ModelTiePoint33922, - string? PhotoshopSettings34377, - string? InterColorProfile34675, - string? GeoTiffGeoKeys34735, - string? GeoTiffGeoDoubleParams34736, - string? ExposureTime33434, - string? GeoTiffGeoAsciiParams34737, - string? SpectralSensitivity34852, - string? IsoEquivalent34855, - string? OptoElectricConversionFunction34856, - string? Interlace34857, - string? TimeZoneOffsetTiffEp34858, - string? SelfTimerModeTiffEp34859, - string? TimeZoneOffset34858, - string? SelfTimerMode34859, - string? ExposureProgram34850, - string? Copyright33432, - string? BatteryLevel33423, - string? CfaPattern233422, - string? JpegProc512, - string? JpegRestartInterval515, - string? JpegLosslessPredictors517, - string? JpegPointTransforms518, - string? JpegQTables519, - string? JpegDcTables520, - string? JpegAcTables521, - string? YCbCrCoefficients529, - string? YCbCrSubsampling530, - string? YCbCrPositioning531, - string? ReferenceBlackWhite532, - string? StripRowCounts559, - string? ApplicationNotes700, - string? RelatedImageFileFormat4096, - string? RelatedImageWidth4097, - string? RelatedImageHeight4098, - string? Rating18246, - string? RatingPercent18249, - string? CfaRepeatPatternDim33421) -{ - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, ExifSourceGenerationContext.Default.Exif); - return result; - } - -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(Exif))] -public partial class ExifSourceGenerationContext : JsonSerializerContext -{ -} \ No newline at end of file diff --git a/Metadata/Models/MetadataExtractorDirectory.cs b/Metadata/Models/MetadataExtractorDirectory.cs deleted file mode 100644 index 89ee468..0000000 --- a/Metadata/Models/MetadataExtractorDirectory.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.ObjectModel; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace View_by_Distance.Metadata.Models; - -public record MetadataExtractorDirectory(string Name, - bool HasError, - ReadOnlyCollection Errors, - ReadOnlyDictionary Tags) -{ - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, MetadataExtractorDirectorySourceGenerationContext.Default.MetadataExtractorDirectory); - return result; - } - -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(MetadataExtractorDirectory))] -public partial class MetadataExtractorDirectorySourceGenerationContext : JsonSerializerContext -{ -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(List))] -public partial class MetadataExtractorDirectoryCollectionSourceGenerationContext : JsonSerializerContext -{ -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(Dictionary))] -public partial class DictionaryStringMetadataExtractorDirectorySourceGenerationContext : JsonSerializerContext -{ -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(ReadOnlyDictionary))] -public partial class ReadOnlyDictionaryStringMetadataExtractorDirectorySourceGenerationContext : JsonSerializerContext -{ -} \ No newline at end of file diff --git a/Metadata/Models/MetadataExtractorTag.cs b/Metadata/Models/MetadataExtractorTag.cs deleted file mode 100644 index 150cbbc..0000000 --- a/Metadata/Models/MetadataExtractorTag.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.ObjectModel; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace View_by_Distance.Metadata.Models; - -public record MetadataExtractorTag(int Type, - string? Description, - bool HasName, - string Name) -{ - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, MetadataExtractorTagSourceGenerationContext.Default.MetadataExtractorTag); - return result; - } - -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(MetadataExtractorTag))] -public partial class MetadataExtractorTagSourceGenerationContext : JsonSerializerContext -{ -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(List))] -public partial class MetadataExtractorTagCollectionSourceGenerationContext : JsonSerializerContext -{ -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(Dictionary))] -public partial class DictionaryStringMetadataExtractorTagSourceGenerationContext : JsonSerializerContext -{ -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(ReadOnlyDictionary))] -public partial class ReadOnlyDictionaryStringMetadataExtractorTagSourceGenerationContext : JsonSerializerContext -{ -} \ No newline at end of file diff --git a/Metadata/Models/Record.cs b/Metadata/Models/Record.cs deleted file mode 100644 index d300537..0000000 --- a/Metadata/Models/Record.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace View_by_Distance.Metadata.Models.Stateless.Methods; - -internal record Record(string Name, - bool HasError, - List Errors, - Dictionary Tags) -{ - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, RecordSourceGenerationContext.Default.Record); - return result; - } - -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(Record))] -internal partial class RecordSourceGenerationContext : JsonSerializerContext -{ -} \ No newline at end of file diff --git a/Metadata/Models/Stateless/Base.cs b/Metadata/Models/Stateless/Base.cs new file mode 100644 index 0000000..8c56003 --- /dev/null +++ b/Metadata/Models/Stateless/Base.cs @@ -0,0 +1,54 @@ +using View_by_Distance.Shared.Models; + +namespace View_by_Distance.Metadata.Models.Stateless.Methods; + +internal static class Base +{ + + internal static string GetMaker(ExifDirectoryBase[]? exifBaseDirectories) + { + string? result = null; + if (exifBaseDirectories is not null) + { + string value; + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + value = exifDirectoryBase?.Make is null ? string.Empty : exifDirectoryBase.Make.ToString().Trim(); + if (string.IsNullOrEmpty(value)) + result = null; + else + { + result = $"{value[0].ToString().ToUpper()}{value[1..].ToLower()}"; + break; + } + } + } + if (string.IsNullOrEmpty(result)) + result = "Unknown"; + return result; + } + + internal static string GetModel(ExifDirectoryBase[]? exifBaseDirectories) + { + string? result = null; + if (exifBaseDirectories is not null) + { + string value; + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + value = exifDirectoryBase?.Model is null ? string.Empty : exifDirectoryBase.Model.ToString().Trim(); + if (string.IsNullOrEmpty(value)) + result = null; + else + { + result = value; + break; + } + } + } + if (string.IsNullOrEmpty(result)) + result = "Unknown"; + return result; + } + +} \ No newline at end of file diff --git a/Metadata/Models/Stateless/Dimensions.cs b/Metadata/Models/Stateless/Dimensions.cs new file mode 100644 index 0000000..fd53715 --- /dev/null +++ b/Metadata/Models/Stateless/Dimensions.cs @@ -0,0 +1,126 @@ +using System.Drawing; + +namespace View_by_Distance.Metadata.Models.Stateless.Methods; + +internal static class Dimensions +{ + +#pragma warning disable IDE0230 + private static readonly 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 }, + { new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP }, + }; +#pragma warning restore IDE0230 + + private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) + { + for (int i = 0; i < thatBytes.Length; i += 1) + { + if (thisBytes[i] == thatBytes[i]) + continue; + return false; + } + return true; + } + + private static short ReadLittleEndianInt16(BinaryReader binaryReader) + { + byte[] bytes = new byte[sizeof(short)]; + for (int i = 0; i < sizeof(short); i += 1) + bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); + return BitConverter.ToInt16(bytes, 0); + } + + private static int ReadLittleEndianInt32(BinaryReader binaryReader) + { + byte[] bytes = new byte[sizeof(int)]; + for (int i = 0; i < sizeof(int); i += 1) + bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); + return BitConverter.ToInt32(bytes, 0); + } + + private static Size? DecodeBitmap(BinaryReader binaryReader) + { + _ = binaryReader.ReadBytes(16); + int width = binaryReader.ReadInt32(); + int height = binaryReader.ReadInt32(); + return new Size(width, height); + } + + private static Size? DecodeGif(BinaryReader binaryReader) + { + int width = binaryReader.ReadInt16(); + int height = binaryReader.ReadInt16(); + return new Size(width, height); + } + + private static Size? DecodePng(BinaryReader binaryReader) + { + _ = binaryReader.ReadBytes(8); + int width = ReadLittleEndianInt32(binaryReader); + int height = ReadLittleEndianInt32(binaryReader); + return new Size(width, height); + } + + private static Size? DecodeJfif(BinaryReader binaryReader) + { + while (binaryReader.ReadByte() == 0xff) + { + byte marker = binaryReader.ReadByte(); + short chunkLength = ReadLittleEndianInt16(binaryReader); + if (marker == 0xc0) + { + _ = binaryReader.ReadByte(); + int height = ReadLittleEndianInt16(binaryReader); + int width = ReadLittleEndianInt16(binaryReader); + return new Size(width, height); + } + if (chunkLength >= 0) + _ = binaryReader.ReadBytes(chunkLength - 2); + else + { + ushort uChunkLength = (ushort)chunkLength; + _ = binaryReader.ReadBytes(uChunkLength - 2); + } + } + return null; + } + + private static Size? DecodeWebP(BinaryReader binaryReader) + { + _ = binaryReader.ReadUInt32(); // Size + _ = binaryReader.ReadBytes(15); // WEBP, VP8 + more + _ = binaryReader.ReadBytes(3); // SYNC + int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width + int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height + return new Size(width, height); + } + + internal static Size? GetDimensions(BinaryReader binaryReader) + { + int 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)) + return kvPair.Value(binaryReader); + } + } + return null; + } + + internal static Size? GetDimensions(string path) + { + using BinaryReader binaryReader = new(File.OpenRead(path)); + return GetDimensions(binaryReader); + } + +} \ No newline at end of file diff --git a/Metadata/Models/Stateless/Exif.cs b/Metadata/Models/Stateless/Exif.cs new file mode 100644 index 0000000..0ce16f3 --- /dev/null +++ b/Metadata/Models/Stateless/Exif.cs @@ -0,0 +1,529 @@ +using MetadataExtractor; +using MetadataExtractor.Formats.Exif; +using MetadataExtractor.Formats.Exif.Makernotes; +using System.Globalization; +using View_by_Distance.Metadata.Models.Stateless.Methods; + +namespace View_by_Distance.Metadata.Models.Stateless; + +internal abstract class Exif +{ + + private static DateTime? GetDateTime(string? value) + { + DateTime? result; + string dateTimeFormat = "yyyy:MM:dd HH:mm:ss"; + string alternateFormat = "ddd MMM dd HH:mm:ss yyyy"; + if (value is not null && DateTime.TryParse(value, out DateTime dateTime)) + result = dateTime; + else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + result = dateTime; + else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime)) + result = dateTime; + else + result = null; + return result; + } + + private static Shared.Models.AviDirectory[] GetAviDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable aviDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.Avi.AviDirectory aviDirectory in aviDirectories) + { + if (aviDirectory.Tags.Count == 0) + continue; + DateTime? dateTimeOriginal; + string? duration = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration); + string? height = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight); + string? width = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagWidth); + if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal, out DateTime checkDateTime)) + dateTimeOriginal = checkDateTime; + else + dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); + if (dateTimeOriginal is null && duration is null && height is null && width is null) + continue; + results.Add(new(dateTimeOriginal, duration, height, width)); + } + return results.ToArray(); + } + + private static Shared.Models.ExifDirectoryBase[] GetExifBaseDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable exifBaseDirectories = directories.OfType(); + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) + { + if (exifDirectoryBase.Tags.Count == 0) + continue; + DateTime? dateTime; + DateTime checkDateTime; + DateTime? dateTimeOriginal; + DateTime? dateTimeDigitized; + string? aperture = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagAperture); + string? applicationNotes = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagApplicationNotes); + string? artist = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagArtist); + string? bitsPerSample = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBitsPerSample); + string? bodySerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBodySerialNumber); + string? cameraOwnerName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCameraOwnerName); + string? compressedAverageBitsPerPixel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompressedAverageBitsPerPixel); + string? compression = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompression); + string? copyright = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCopyright); + string? documentName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagDocumentName); + string? exifVersion = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExifVersion); + string? exposureTime = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExposureTime); + string? fileSource = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagFileSource); + string? imageDescription = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageDescription); + string? imageHeight = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageHeight); + string? imageNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageNumber); + string? imageUniqueId = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageUniqueId); + string? imageWidth = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageWidth); + string? isoSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagIsoSpeed); + string? lensMake = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensMake); + string? lensModel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensModel); + string? lensSerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensSerialNumber); + string? make = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMake); + string? makerNote = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMakernote); + string? model = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagModel); + string? orientation = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagOrientation); + int? orientationValue = orientation is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagOrientation); + string? rating = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRating); + string? ratingPercent = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRatingPercent); + string? securityClassification = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSecurityClassification); + string? shutterSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagShutterSpeed); + string? software = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSoftware); + string? timeZone = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZone); + string? timeZoneDigitized = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneDigitized); + string? timeZoneOriginal = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneOriginal); + string? userComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagUserComment); + string? winAuthor = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinAuthor); + string? winComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinComment); + string? winKeywords = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinKeywords); + string? winSubject = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinSubject); + string? winTitle = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinTitle); + string? xResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagXResolution); + string? yResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagYResolution); + if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime)) + dateTime = checkDateTime; + else + dateTime = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTime)); + if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime)) + dateTimeOriginal = checkDateTime; + else + dateTimeOriginal = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeOriginal)); + if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime)) + dateTimeDigitized = checkDateTime; + else + dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized)); + if (userComment is not null && userComment.Length > 255) + userComment = "..."; + if (aperture is null + && applicationNotes is null + && artist is null + && bitsPerSample is null + && bodySerialNumber is null + && cameraOwnerName is null + && compressedAverageBitsPerPixel is null + && compression is null + && copyright is null + && dateTime is null + && dateTimeDigitized is null + && dateTimeOriginal is null + && documentName is null + && exifVersion is null + && exposureTime is null + && fileSource is null + && imageDescription is null + && imageHeight is null + && imageNumber is null + && imageUniqueId is null + && imageWidth is null + && isoSpeed is null + && lensMake is null + && lensModel is null + && lensSerialNumber is null + && make is null + && makerNote is null + && model is null + && orientation is null + && orientationValue is null + && rating is null + && ratingPercent is null + && securityClassification is null + && shutterSpeed is null + && software is null + && timeZone is null + && timeZoneDigitized is null + && timeZoneOriginal is null + && userComment is null + && winAuthor is null + && winComment is null + && winKeywords is null + && winSubject is null + && winTitle is null + && xResolution is not null + && yResolution is null) + continue; + results.Add(new(aperture, + applicationNotes, + artist, + bitsPerSample, + bodySerialNumber, + cameraOwnerName, + compressedAverageBitsPerPixel, + compression, + copyright, + dateTime, + dateTimeDigitized, + dateTimeOriginal, + documentName, + exifVersion, + exposureTime, + fileSource, + imageDescription, + imageHeight, + imageNumber, + imageUniqueId, + imageWidth, + isoSpeed, + lensMake, + lensModel, + lensSerialNumber, + make, + makerNote, + model, + orientation, + orientationValue, + rating, + ratingPercent, + securityClassification, + shutterSpeed, + software, + timeZone, + timeZoneDigitized, + timeZoneOriginal, + userComment, + winAuthor, + winComment, + winKeywords, + winSubject, + winTitle, + xResolution, + yResolution)); + } + return results.ToArray(); + } + + private static Shared.Models.FileMetadataDirectory[] GetFileMetadataDirectories(string file, IReadOnlyList directories) + { + List results = []; + IEnumerable fileMetadataDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.FileSystem.FileMetadataDirectory fileMetadataDirectory in fileMetadataDirectories) + { + if (fileMetadataDirectory.Tags.Count == 0) + continue; + DateTime? fileModifiedDate; + string? fileName = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName); + string? fileSize = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize); + if (fileMetadataDirectory.TryGetDateTime(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate, out DateTime checkDateTime)) + fileModifiedDate = checkDateTime; + else + fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate)); + if (fileName is null || !file.EndsWith(fileName)) + throw new NotSupportedException($"!{file}.EndsWith({fileName})"); + if (fileModifiedDate is null && fileName is null && fileSize is null) + continue; + results.Add(new(fileModifiedDate, fileName, fileSize)); + } + return results.ToArray(); + } + + private static Shared.Models.GifHeaderDirectory[] GetGifHeaderDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable gifHeaderDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.Gif.GifHeaderDirectory gifHeaderDirectory in gifHeaderDirectories) + { + if (gifHeaderDirectory.Tags.Count == 0) + continue; + string? imageHeight = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight); + string? imageWidth = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth); + if (imageHeight is null && imageWidth is null) + continue; + results.Add(new(imageHeight, imageWidth)); + } + return results.ToArray(); + } + + private static Shared.Models.GpsDirectory[] GetGpsDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable gpsDirectories = directories.OfType(); + foreach (GpsDirectory gpsDirectory in gpsDirectories) + { + if (gpsDirectory.Tags.Count == 0) + continue; + DateTime? timeStamp; + string? altitude = gpsDirectory.GetDescription(GpsDirectory.TagAltitude); + string? latitude = gpsDirectory.GetDescription(GpsDirectory.TagLatitude); + string? latitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLatitudeRef); + string? longitude = gpsDirectory.GetDescription(GpsDirectory.TagLongitude); + string? longitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLongitudeRef); + if (gpsDirectory.TryGetDateTime(GpsDirectory.TagTimeStamp, out DateTime checkDateTime)) + timeStamp = checkDateTime; + else + timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp)); + if (altitude is null && latitude is null && latitudeRef is null && longitude is null && longitudeRef is null && timeStamp is null) + continue; + results.Add(new(altitude, + latitude, + latitudeRef, + longitude, + longitudeRef, + timeStamp)); + } + return results.ToArray(); + } + + private static Shared.Models.JpegDirectory[] GetJpegDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable jpegDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.Jpeg.JpegDirectory jpegDirectory in jpegDirectories) + { + if (jpegDirectory.Tags.Count == 0) + continue; + string? imageHeight = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight); + string? imageWidth = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth); + if (imageHeight is null && imageWidth is null) + continue; + results.Add(new(imageHeight, imageWidth)); + } + return results.ToArray(); + } + + private static Shared.Models.MakernoteDirectory[] GetMakernoteDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable appleMakernoteDirectories = directories.OfType(); + foreach (AppleMakernoteDirectory appleMakernoteDirectory in appleMakernoteDirectories) + { + if (appleMakernoteDirectory.Tags.Count == 0) + continue; + string? cameraSerialNumber = null; + string? firmwareVersion = null; + string? qualityAndFileFormat = null; + if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) + continue; + results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); + } + IEnumerable canonMakernoteDirectories = directories.OfType(); + foreach (CanonMakernoteDirectory canonMakernoteDirectory in canonMakernoteDirectories) + { + if (canonMakernoteDirectory.Tags.Count == 0) + continue; + string? cameraSerialNumber = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.TagModelId); + string? firmwareVersion = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.TagCanonFirmwareVersion); + string? qualityAndFileFormat = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.CameraSettings.TagQuality); + if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) + continue; + results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); + } + IEnumerable nikonType2MakernoteDirectories = directories.OfType(); + foreach (NikonType2MakernoteDirectory nikonType2MakernoteDirectory in nikonType2MakernoteDirectories) + { + if (nikonType2MakernoteDirectory.Tags.Count == 0) + continue; + string? cameraSerialNumber = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagCameraSerialNumber); + string? firmwareVersion = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagFirmwareVersion); + string? qualityAndFileFormat = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagQualityAndFileFormat); + if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) + continue; + results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); + } + IEnumerable olympusMakernoteDirectories = directories.OfType(); + foreach (OlympusMakernoteDirectory olympusMakernoteDirectory in olympusMakernoteDirectories) + { + if (olympusMakernoteDirectory.Tags.Count == 0) + continue; + string? cameraSerialNumber = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagSerialNumber1); + string? firmwareVersion = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagBodyFirmwareVersion); + string? qualityAndFileFormat = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagJpegQuality); + if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) + continue; + results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); + } + IEnumerable panasonicMakernoteDirectories = directories.OfType(); + foreach (PanasonicMakernoteDirectory panasonicMakernoteDirectory in panasonicMakernoteDirectories) + { + if (panasonicMakernoteDirectory.Tags.Count == 0) + continue; + string? cameraSerialNumber = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagInternalSerialNumber); + string? firmwareVersion = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagFirmwareVersion); + string? qualityAndFileFormat = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagQualityMode); + if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) + continue; + results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); + } + IEnumerable samsungType2MakernoteDirectories = directories.OfType(); + foreach (SamsungType2MakernoteDirectory samsungType2MakernoteDirectory in samsungType2MakernoteDirectories) + { + if (samsungType2MakernoteDirectory.Tags.Count == 0) + continue; + string? cameraSerialNumber = samsungType2MakernoteDirectory.GetDescription(SamsungType2MakernoteDirectory.TagSerialNumber); + string? firmwareVersion = samsungType2MakernoteDirectory.GetDescription(SamsungType2MakernoteDirectory.TagFirmwareName); + string? qualityAndFileFormat = null; + if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) + continue; + results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); + } + IEnumerable sonyType6MakernoteDirectories = directories.OfType(); + foreach (SonyType6MakernoteDirectory sonyType6MakernoteDirectory in sonyType6MakernoteDirectories) + { + if (sonyType6MakernoteDirectory.Tags.Count == 0) + continue; + string? cameraSerialNumber = null; + string? firmwareVersion = null; + string? qualityAndFileFormat = null; + if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null) + continue; + results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat)); + } + return results.ToArray(); + } + + private static Shared.Models.PhotoshopDirectory[] GetPhotoshopDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable photoshopDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.Photoshop.PhotoshopDirectory photoshopDirectory in photoshopDirectories) + { + if (photoshopDirectory.Tags.Count == 0) + continue; + string? jpegQuality = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality); + string? url = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl); + if (jpegQuality is null && url is null) + continue; + results.Add(new(jpegQuality, url)); + } + return results.ToArray(); + } + + private static Shared.Models.PngDirectory[] GetPngDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable pngDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.Png.PngDirectory pngDirectory in pngDirectories) + { + if (pngDirectory.Tags.Count == 0) + continue; + string? imageHeight = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight); + string? imageWidth = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth); + string? textualData = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagTextualData); + if (imageHeight is null && imageWidth is null && textualData is null) + continue; + results.Add(new(imageHeight, imageWidth, textualData)); + } + return results.ToArray(); + } + + private static Shared.Models.QuickTimeMovieHeaderDirectory[] GetQuickTimeMovieHeaderDirectoryDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable quickTimeMovieHeaderDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in quickTimeMovieHeaderDirectories) + { + if (quickTimeMovieHeaderDirectory.Tags.Count == 0) + continue; + DateTime? created; + if (quickTimeMovieHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime)) + created = checkDateTime; + else + created = GetDateTime(quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); + if (created is null) + continue; + results.Add(new(created)); + } + return results.ToArray(); + } + + private static Shared.Models.QuickTimeTrackHeaderDirectory[] GetQuickTimeTrackHeaderDirectoryDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable quickTimeTrackHeaderDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in quickTimeTrackHeaderDirectories) + { + if (quickTimeTrackHeaderDirectory.Tags.Count == 0) + continue; + DateTime? created; + if (quickTimeTrackHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime)) + created = checkDateTime; + else + created = GetDateTime(quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); + if (created is null) + continue; + results.Add(new(created)); + } + return results.ToArray(); + } + + private static Shared.Models.WebPDirectory[] GetWebPDirectories(IReadOnlyList directories) + { + List results = []; + IEnumerable webPDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.WebP.WebPDirectory webPDirectory in webPDirectories) + { + if (webPDirectory.Tags.Count == 0) + continue; + string? imageHeight = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight); + string? imageWidth = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth); + if (imageHeight is null && imageWidth is null) + continue; + results.Add(new(imageHeight, imageWidth)); + } + return results.ToArray(); + } + + private static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, System.Drawing.Size? size, IReadOnlyList directories) + { + Shared.Models.ExifDirectory result; + Shared.Models.AviDirectory[] aviDirectories = GetAviDirectories(directories); + Shared.Models.GpsDirectory[] gpsDirectories = GetGpsDirectories(directories); + Shared.Models.PngDirectory[] pngDirectories = GetPngDirectories(directories); + Shared.Models.JpegDirectory[] jpegDirectories = GetJpegDirectories(directories); + Shared.Models.WebPDirectory[] webPDirectories = GetWebPDirectories(directories); + Shared.Models.ExifDirectoryBase[] exifBaseDirectories = GetExifBaseDirectories(directories); + Shared.Models.GifHeaderDirectory[] gifHeaderDirectories = GetGifHeaderDirectories(directories); + Shared.Models.MakernoteDirectory[] makernoteDirectories = GetMakernoteDirectories(directories); + Shared.Models.PhotoshopDirectory[] photoshopDirectories = GetPhotoshopDirectories(directories); + Shared.Models.FileMetadataDirectory[] fileMetadataDirectories = GetFileMetadataDirectories(filePath.FullName, directories); + Shared.Models.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories); + Shared.Models.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories); + result = new(aviDirectories, + exifBaseDirectories, + fileMetadataDirectories, + gifHeaderDirectories, + gpsDirectories, + size?.Height, + jpegDirectories, + makernoteDirectories, + filePath.Name, + photoshopDirectories, + pngDirectories, + quickTimeMovieHeaderDirectories, + quickTimeTrackHeaderDirectories, + webPDirectories, + size?.Width); + return result; + } + + internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath) + { + Shared.Models.ExifDirectory? result; + System.Drawing.Size? size = Dimensions.GetDimensions(filePath.FullName); + IReadOnlyList directories = ImageMetadataReader.ReadMetadata(filePath.FullName); + result = Covert(filePath, size, directories); + return result; + } + +} \ No newline at end of file diff --git a/Metadata/Models/Stateless/Face.cs b/Metadata/Models/Stateless/Face.cs new file mode 100644 index 0000000..30c77ee --- /dev/null +++ b/Metadata/Models/Stateless/Face.cs @@ -0,0 +1,42 @@ +using View_by_Distance.Shared.Models; + +namespace View_by_Distance.Metadata.Models.Stateless.Methods; + +internal static class Face +{ + + internal static string? GetFaceEncoding(PngDirectory[]? pngDirectories) + { + string? result = null; + if (pngDirectories is not null) + { + const string comment = "Comment:"; + foreach (PngDirectory pngDirectory in pngDirectories) + { + if (pngDirectory.TextualData is null || !pngDirectory.TextualData.StartsWith(comment)) + continue; + result = pngDirectory.TextualData[comment.Length..]; + break; + } + } + return result; + } + + internal static string? GetOutputResolution(PngDirectory[]? pngDirectories) + { + string? result = null; + if (pngDirectories is not null) + { + const string artist = "Artist:"; + foreach (PngDirectory pngDirectory in pngDirectories) + { + if (pngDirectory.TextualData is null || !pngDirectory.TextualData.StartsWith(artist)) + continue; + result = pngDirectory.TextualData[artist.Length..]; + break; + } + } + return result; + } + +} \ No newline at end of file diff --git a/Metadata/Models/Stateless/GPS.cs b/Metadata/Models/Stateless/GPS.cs new file mode 100644 index 0000000..ed12cd6 --- /dev/null +++ b/Metadata/Models/Stateless/GPS.cs @@ -0,0 +1,121 @@ +using MetadataExtractor; +using View_by_Distance.Metadata.Models.Stateless.Methods; +using View_by_Distance.Shared.Models; + +namespace View_by_Distance.Metadata.Models.Stateless; + +internal abstract class GPS +{ + + private static bool CoordinateValidatorValidate(double latitude, double longitude) + { + if (latitude is < (-90) or > 90) + return false; + if (longitude is < (-180) or > 180) + return false; + return true; + } + + private static double GetRadius(IMetadata.DistanceUnit distanceUnit) + { + return distanceUnit switch + { + IMetadata.DistanceUnit.Kilometers => 6371.0, // EarthRadiusInKilometers; + IMetadata.DistanceUnit.Meters => 6371000.0, // EarthRadiusInMeters; + IMetadata.DistanceUnit.NauticalMiles => 3440.0, // EarthRadiusInNauticalMiles; + IMetadata.DistanceUnit.Miles => 3959.0, // EarthRadiusInMiles; + _ => throw new NotSupportedException() + }; + } + + private static double ToRadian(double d) => + d * (Math.PI / 180); + + private static double DiffRadian(double val1, double val2) => + ToRadian(val2) - ToRadian(val1); + + internal static double GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, IMetadata.DistanceUnit distanceUnit = IMetadata.DistanceUnit.Miles) + { + if (!CoordinateValidatorValidate(originLatitude, originLongitude)) + throw new ArgumentException("Invalid origin coordinates supplied."); + if (!CoordinateValidatorValidate(destinationLatitude, destinationLongitude)) + throw new ArgumentException("Invalid destination coordinates supplied."); + double radius = GetRadius(distanceUnit); + return Math.Round( + radius * 2 * + Math.Asin(Math.Min(1, + Math.Sqrt( + Math.Pow(Math.Sin(DiffRadian(originLatitude, destinationLatitude) / 2.0), 2.0) + + Math.Cos(ToRadian(originLatitude)) * Math.Cos(ToRadian(destinationLatitude)) * + Math.Pow(Math.Sin(DiffRadian(originLongitude, destinationLongitude) / 2.0), + 2.0)))), decimalPlaces); + } + + private static double ParseValueFromDmsString(string value) + { + double result; + if (string.IsNullOrEmpty(value)) + return double.MinValue; + + double secondsValue; + string[] degrees = value.Split('°'); + if (degrees.Length != 2) + return double.MinValue; + if (!double.TryParse(degrees[0], out double degreesValue)) + return double.MinValue; + + string[] minutes = degrees[1].Split('\''); + if (minutes.Length != 2) + return double.MinValue; + if (!double.TryParse(minutes[0], out double minutesValue)) + return double.MinValue; + + string[] seconds = minutes[1].Split('"'); + if (seconds.Length != 2) + secondsValue = 0; + else + { + if (!double.TryParse(seconds[0], out secondsValue)) + return double.MinValue; + } + result = Math.Abs(degreesValue) + (minutesValue / 60) + (secondsValue / 3600); + + if (degreesValue < 0) + result *= -1; + + return result; + } + + internal static GeoLocation? GeoLocation(GpsDirectory[]? gpsDirectories) + { + GeoLocation? result = null; + if (gpsDirectories is not null) + { + foreach (GpsDirectory gpsDirectory in gpsDirectories) + { + if (string.IsNullOrEmpty(gpsDirectory?.Latitude)) + result = null; + else + { + string latitudeDMS = gpsDirectory.Latitude; + double latitude = ParseValueFromDmsString(latitudeDMS); + if (string.IsNullOrEmpty(gpsDirectory.Longitude)) + result = null; + else + { + string longitudeDMS = gpsDirectory.Longitude; + double longitude = ParseValueFromDmsString(longitudeDMS); + result = new(latitude, longitude); + string dms = result.ToDmsString(); + if ($"{latitudeDMS}, {longitudeDMS}" != dms) + result = null; + } + } + if (result is not null) + break; + } + } + return result; + } + +} \ No newline at end of file diff --git a/Metadata/Models/Stateless/Methods/IMetadata.cs b/Metadata/Models/Stateless/Methods/IMetadata.cs index b622203..8029fde 100644 --- a/Metadata/Models/Stateless/Methods/IMetadata.cs +++ b/Metadata/Models/Stateless/Methods/IMetadata.cs @@ -1,5 +1,5 @@ using MetadataExtractor; -using System.Collections.ObjectModel; +using View_by_Distance.Shared.Models; namespace View_by_Distance.Metadata.Models.Stateless.Methods; @@ -14,39 +14,54 @@ public interface IMetadata Meters } - ReadOnlyDictionary TestStatic_GetKeyValuePairs(IReadOnlyList directories) => - GetKeyValuePairs(directories); - static ReadOnlyDictionary GetKeyValuePairs(IReadOnlyList directories) => - new(Metadata.GetKeyValuePairs(directories)); + ExifDirectory TestStatic_GetExifDirectory(FilePath filePath) => + GetExifDirectory(filePath); + static ExifDirectory GetExifDirectory(FilePath filePath) => + Exif.GetExifDirectory(filePath); - string? TestStatic_GetModel(IReadOnlyList directories) => - GetModel(directories); - static string? GetModel(IReadOnlyList directories) => - Metadata.GetModel(directories); + string TestStatic_GetMaker(ExifDirectory? exifDirectory) => + GetMaker(exifDirectory); + static string GetMaker(ExifDirectory? exifDirectory) => + Base.GetMaker(exifDirectory?.ExifBaseDirectories); - string? TestStatic_GetFaceEncoding(IReadOnlyList directories) => - GetFaceEncoding(directories); - static string? GetFaceEncoding(IReadOnlyList directories) => - Metadata.GetFaceEncoding(directories); + string TestStatic_GetModel(ExifDirectory? exifDirectory) => + GetModel(exifDirectory); + static string GetModel(ExifDirectory? exifDirectory) => + Base.GetModel(exifDirectory?.ExifBaseDirectories); - string? TestStatic_GetOutputResolution(IReadOnlyList directories) => - GetOutputResolution(directories); - static string? GetOutputResolution(IReadOnlyList directories) => - Metadata.GetOutputResolution(directories); + string? TestStatic_GetOutputResolution(ExifDirectory? exifDirectory) => + GetOutputResolution(exifDirectory); + static string? GetOutputResolution(ExifDirectory? exifDirectory) => + Face.GetOutputResolution(exifDirectory?.PngDirectories); - GeoLocation? TestStatic_GeoLocation(IReadOnlyList directories) => - GeoLocation(directories); - static GeoLocation? GeoLocation(IReadOnlyList directories) => - Metadata.GeoLocation(directories); + string? TestStatic_GetFaceEncoding(ExifDirectory? exifDirectory) => + GetFaceEncoding(exifDirectory); + static string? GetFaceEncoding(ExifDirectory? exifDirectory) => + Face.GetFaceEncoding(exifDirectory?.PngDirectories); - GeoLocation? TestStatic_GeoLocation(ReadOnlyDictionary metadataExtractorDirectories) => - GeoLocation(metadataExtractorDirectories); - static GeoLocation? GeoLocation(ReadOnlyDictionary metadataExtractorDirectories) => - Metadata.GeoLocation(metadataExtractorDirectories); + GeoLocation? TestStatic_GeoLocation(ExifDirectory? exifDirectory) => + GeoLocation(exifDirectory); + static GeoLocation? GeoLocation(ExifDirectory? exifDirectory) => + GPS.GeoLocation(exifDirectory?.GpsDirectories); double? TestStatic_GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) => GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit); static double? GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) => - Metadata.GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit); + GPS.GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit); + + // string? TestStatic_GetFaceEncoding(IReadOnlyList directories) => + // GetFaceEncoding(directories); + // static string? GetFaceEncoding(IReadOnlyList directories) => + // Metadata.GetFaceEncoding(directories); + + // string? TestStatic_GetOutputResolution(IReadOnlyList directories) => + // GetOutputResolution(directories); + // static string? GetOutputResolution(IReadOnlyList directories) => + // Metadata.GetOutputResolution(directories); + + // Dictionary TestStatic_GetMetadataCollection(FileInfo fileInfo, List> subFileTuples, List parseExceptions) => + // GetMetadataCollection(fileInfo, subFileTuples, parseExceptions); + // static Dictionary GetMetadataCollection(FileInfo fileInfo, List> subFileTuples, List parseExceptions) => + // Metadata.GetMetadataCollection(fileInfo, subFileTuples, parseExceptions); } \ No newline at end of file diff --git a/Metadata/Models/Stateless/Methods/Metadata.cs b/Metadata/Models/Stateless/Methods/Metadata.cs deleted file mode 100644 index 1f94d76..0000000 --- a/Metadata/Models/Stateless/Methods/Metadata.cs +++ /dev/null @@ -1,229 +0,0 @@ -using MetadataExtractor; -using MetadataExtractor.Formats.Exif; -using System.Collections.ObjectModel; -using System.Text.Json; -using static View_by_Distance.Metadata.Models.Stateless.Methods.IMetadata; - -namespace View_by_Distance.Metadata.Models.Stateless.Methods; - -internal partial class Metadata -{ - - internal static Dictionary GetKeyValuePairs(IReadOnlyList directories) - { - Dictionary results = []; - MetadataExtractorTag metadataExtractorTag; - MetadataExtractorDirectory? metadataExtractorDirectory; - Dictionary metadataExtractorTags; - foreach (MetadataExtractor.Directory directory in directories) - { - metadataExtractorTags = []; - if (results.TryGetValue(directory.Name, out metadataExtractorDirectory)) - continue; - foreach (Tag tag in directory.Tags) - { - metadataExtractorTag = new(tag.Type, tag.Description, tag.HasName, tag.Name); - metadataExtractorTags.Add(tag.Type, metadataExtractorTag); - } - metadataExtractorDirectory = new(directory.Name, directory.HasError, new(directory.Errors.ToArray()), new(metadataExtractorTags)); - results.Add(directory.Name, metadataExtractorDirectory); - } - return results; - } - - internal static Dictionary Deserialize(string json) - { - Dictionary results = []; - Record? record; - MetadataExtractorDirectory metadataExtractorDirectory; - Dictionary? keyValuePairs = JsonSerializer.Deserialize>(json); - if (keyValuePairs is null) - throw new NullReferenceException(nameof(keyValuePairs)); - foreach (KeyValuePair keyValuePair in keyValuePairs) - { - record = JsonSerializer.Deserialize(keyValuePair.Value.ToString(), RecordSourceGenerationContext.Default.Record); - if (record is null) - throw new NullReferenceException(nameof(record)); - metadataExtractorDirectory = new(record.Name, record.HasError, new(record.Errors), new(record.Tags)); - results.Add(record.Name, metadataExtractorDirectory); - } - return results; - } - - internal static string? GetFaceEncoding(IReadOnlyList directories) - { - string? result; - List results = []; - const string comment = "Comment: "; - foreach (MetadataExtractor.Directory directory in directories) - { - if (directory.Name != "PNG-tEXt") - continue; - foreach (Tag tag in directory.Tags) - { - if (tag.Name != "Textual Data" || string.IsNullOrEmpty(tag.Description)) - continue; - if (!tag.Description.StartsWith(comment)) - continue; - results.Add(tag.Description); - } - } - result = results.Count != 0 ? results[0][comment.Length..] : null; - return result; - } - - internal static string? GetOutputResolution(IReadOnlyList directories) - { - string? result; - List results = []; - const string artist = "Artist: "; - foreach (MetadataExtractor.Directory directory in directories) - { - if (directory.Name != "PNG-tEXt") - continue; - foreach (Tag tag in directory.Tags) - { - if (tag.Name != "Textual Data" || string.IsNullOrEmpty(tag.Description)) - continue; - if (!tag.Description.StartsWith(artist)) - continue; - results.Add(tag.Description); - } - } - result = results.Count != 0 ? results[0][artist.Length..] : null; - return result; - } - - internal static string? GetModel(IReadOnlyList directories) - { - string? result; - ExifDirectoryBase? exifDirectoryBase = directories.OfType().FirstOrDefault(); - if (exifDirectoryBase is null) - result = null; - else - result = exifDirectoryBase.GetString(ExifDirectoryBase.TagModel); - return result; - } - - internal static GeoLocation? GeoLocation(IReadOnlyList directories) - { - GeoLocation? result; - GpsDirectory? gpsDirectory = directories.OfType().FirstOrDefault(); - if (gpsDirectory is null) - result = null; - else - result = gpsDirectory.GetGeoLocation(); - return result; - } - - private static bool CoordinateValidatorValidate(double latitude, double longitude) - { - if (latitude is < (-90) or > 90) - return false; - if (longitude is < (-180) or > 180) - return false; - - return true; - } - - private static double GetRadius(DistanceUnit distanceUnit) - { - return distanceUnit switch - { - DistanceUnit.Kilometers => 6371.0, // EarthRadiusInKilometers; - DistanceUnit.Meters => 6371000.0, // EarthRadiusInMeters; - DistanceUnit.NauticalMiles => 3440.0, // EarthRadiusInNauticalMiles; - DistanceUnit.Miles => 3959.0, // EarthRadiusInMiles; - _ => throw new NotSupportedException() - }; - } - - private static double ToRadian(double d) => - d * (Math.PI / 180); - - private static double DiffRadian(double val1, double val2) => - ToRadian(val2) - ToRadian(val1); - - internal static double GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) - { - if (!CoordinateValidatorValidate(originLatitude, originLongitude)) - throw new ArgumentException("Invalid origin coordinates supplied."); - if (!CoordinateValidatorValidate(destinationLatitude, destinationLongitude)) - throw new ArgumentException("Invalid destination coordinates supplied."); - double radius = GetRadius(distanceUnit); - return Math.Round( - radius * 2 * - Math.Asin(Math.Min(1, - Math.Sqrt( - Math.Pow(Math.Sin(DiffRadian(originLatitude, destinationLatitude) / 2.0), 2.0) + - Math.Cos(ToRadian(originLatitude)) * Math.Cos(ToRadian(destinationLatitude)) * - Math.Pow(Math.Sin(DiffRadian(originLongitude, destinationLongitude) / 2.0), - 2.0)))), decimalPlaces); - } - - private static double ParseValueFromDmsString(string value) - { - double result; - if (string.IsNullOrEmpty(value)) - return double.MinValue; - - double secondsValue; - string[] degrees = value.Split('°'); - if (degrees.Length != 2) - return double.MinValue; - if (!double.TryParse(degrees[0], out double degreesValue)) - return double.MinValue; - - string[] minutes = degrees[1].Split('\''); - if (minutes.Length != 2) - return double.MinValue; - if (!double.TryParse(minutes[0], out double minutesValue)) - return double.MinValue; - - string[] seconds = minutes[1].Split('"'); - if (seconds.Length != 2) - secondsValue = 0; - else - { - if (!double.TryParse(seconds[0], out secondsValue)) - return double.MinValue; - } - result = Math.Abs(degreesValue) + (minutesValue / 60) + (secondsValue / 3600); - - if (degreesValue < 0) - result *= -1; - - return result; - } - - internal static GeoLocation? GeoLocation(ReadOnlyDictionary metadataExtractorDirectories) - { - GeoLocation? result; - if (!metadataExtractorDirectories.TryGetValue("GPS", out MetadataExtractorDirectory? metadataExtractorDirectory)) - result = null; - else - { - MetadataExtractorTag? metadataExtractorTag; - if (!metadataExtractorDirectory.Tags.TryGetValue((int)Shared.Models.Stateless.IExif.Tags.GPSLatitude, out metadataExtractorTag) || string.IsNullOrEmpty(metadataExtractorTag.Description)) - result = null; - else - { - string latitudeDMS = metadataExtractorTag.Description; - double latitude = ParseValueFromDmsString(latitudeDMS); - if (!metadataExtractorDirectory.Tags.TryGetValue((int)Shared.Models.Stateless.IExif.Tags.GPSLongitude, out metadataExtractorTag) || string.IsNullOrEmpty(metadataExtractorTag.Description)) - result = null; - else - { - string longitudeDMS = metadataExtractorTag.Description; - double longitude = ParseValueFromDmsString(longitudeDMS); - result = new(latitude, longitude); - string dms = result.ToDmsString(); - if ($"{latitudeDMS}, {longitudeDMS}" != dms) - result = null; - } - } - } - return result; - } - -} \ No newline at end of file diff --git a/Offset-Date-Time-Original/OffsetDateTimeOriginal.cs b/Offset-Date-Time-Original/OffsetDateTimeOriginal.cs index ac5bac4..ef820f3 100644 --- a/Offset-Date-Time-Original/OffsetDateTimeOriginal.cs +++ b/Offset-Date-Time-Original/OffsetDateTimeOriginal.cs @@ -10,7 +10,6 @@ using System.Text; using View_by_Distance.Offset.Date.Time.Original.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; -using View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Offset.Date.Time.Original; @@ -113,7 +112,7 @@ public class OffsetDateTimeOriginal string checkFile; PropertyItem? propertyItem; string? ticksDirectory = null; - int dateTimeOriginal = (int)IExif.Tags.DateTimeOriginal; + int dateTimeOriginal = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal; for (int i = 0; i < int.MaxValue; i++) { ticksDirectory = Path.Combine(sourceDirectory, ticks.ToString()); diff --git a/Property/Models/Stateless/Property.cs b/Property/Models/Stateless/Property.cs index 127b836..2829895 100644 --- a/Property/Models/Stateless/Property.cs +++ b/Property/Models/Stateless/Property.cs @@ -8,7 +8,6 @@ using System.Text; using System.Text.RegularExpressions; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; -using View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Property.Models.Stateless; @@ -207,13 +206,12 @@ internal partial class Property DateTime? dateTimeOriginal = null; DateTime? dateTimeDigitized = null; DateTime? dateTimeOriginalByLogic = null; - IReadOnlyList directories; DateTime? dateTimeFromName = GetDateTimeFromName(fileHolder); if (!isValidImageFormatExtension && fileHolder.Exists && metadata is not null) { try { - directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); + IReadOnlyList directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); (dateTimeOriginalByLogic, DateTime?[] metadataDateTimes) = metadata.GetDateTimes(fileHolder, directories); dateTimesByLogic = GetDateTimes(metadataDateTimes); message = null; @@ -244,9 +242,9 @@ internal partial class Property id ??= Shared.Models.Stateless.Methods.IId.GetDeterministicHashCode(bytes); } dateTimeFormat = IProperty.DateTimeFormat(); - if (image.PropertyIdList.Contains((int)IExif.Tags.DateTime)) + if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime)) { - propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTime); + propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime); if (propertyItem?.Value is not null) { value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); @@ -256,9 +254,9 @@ internal partial class Property dateTime = checkDateTime; } } - if (image.PropertyIdList.Contains((int)IExif.Tags.DateTimeDigitized)) + if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized)) { - propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTimeDigitized); + propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized); if (propertyItem?.Value is not null) { value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); @@ -268,9 +266,9 @@ internal partial class Property dateTimeDigitized = checkDateTime; } } - if (image.PropertyIdList.Contains((int)IExif.Tags.DateTimeOriginal)) + if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal)) { - propertyItem = image.GetPropertyItem((int)IExif.Tags.DateTimeOriginal); + propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal); if (propertyItem?.Value is not null) { value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); @@ -280,9 +278,9 @@ internal partial class Property dateTimeOriginal = checkDateTime; } } - if (image.PropertyIdList.Contains((int)IExif.Tags.GPSDateStamp)) + if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.GpsDirectory.TagDateStamp)) { - propertyItem = image.GetPropertyItem((int)IExif.Tags.GPSDateStamp); + propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.GpsDirectory.TagDateStamp); if (propertyItem?.Value is not null) { value = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); @@ -292,27 +290,27 @@ internal partial class Property gpsDateStamp = checkDateTime; } } - if (image.PropertyIdList.Contains((int)IExif.Tags.Make)) + if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagMake)) { - propertyItem = image.GetPropertyItem((int)IExif.Tags.Make); + propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagMake); if (propertyItem?.Value is not null) make = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); } - if (image.PropertyIdList.Contains((int)IExif.Tags.Model)) + if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagModel)) { - propertyItem = image.GetPropertyItem((int)IExif.Tags.Model); + propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagModel); if (propertyItem?.Value is not null) model = asciiEncoding.GetString(propertyItem.Value, 0, propertyItem.Len - 1); } - if (image.PropertyIdList.Contains((int)IExif.Tags.Orientation)) + if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation)) { - propertyItem = image.GetPropertyItem((int)IExif.Tags.Orientation); + propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation); if (propertyItem?.Value is not null) orientation = BitConverter.ToInt16(propertyItem.Value, 0); } - if (image.PropertyIdList.Contains((int)IExif.Tags.XPKeywords)) + if (image.PropertyIdList.Contains(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinKeywords)) { - propertyItem = image.GetPropertyItem((int)IExif.Tags.XPKeywords); + propertyItem = image.GetPropertyItem(MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinKeywords); if (propertyItem?.Value is not null) { if (propertyItem.Type == 2) @@ -333,7 +331,7 @@ internal partial class Property { try { - directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); + IReadOnlyList directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); (dateTimeOriginalByLogic, DateTime?[] metadataDateTimes) = metadata.GetDateTimes(fileHolder, directories); dateTimesByLogic = GetDateTimes(dateTimes, metadataDateTimes); message = null; diff --git a/Resize/Models/_C_Resize.cs b/Resize/Models/_C_Resize.cs index da8e290..ee50634 100644 --- a/Resize/Models/_C_Resize.cs +++ b/Resize/Models/_C_Resize.cs @@ -10,7 +10,6 @@ using View_by_Distance.Property.Models; using View_by_Distance.Property.Models.Stateless; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; -using View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Resize.Models; @@ -98,7 +97,7 @@ public class C_Resize public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetGifLowQuality() { (ImageCodecInfo, EncoderParameters, string) result; - System.Drawing.Imaging.ImageFormat imageFormat = System.Drawing.Imaging.ImageFormat.Gif; + ImageFormat imageFormat = ImageFormat.Gif; ImageCodecInfo imageCodecInfo = (from l in ImageCodecInfo.GetImageEncoders() where l.FormatID == imageFormat.Guid select l).First(); EncoderParameters encoderParameters = new(1); encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); @@ -111,7 +110,7 @@ public class C_Resize public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetPngLowQuality() { (ImageCodecInfo, EncoderParameters, string) result; - System.Drawing.Imaging.ImageFormat imageFormat = System.Drawing.Imaging.ImageFormat.Png; + ImageFormat imageFormat = ImageFormat.Png; ImageCodecInfo imageCodecInfo = (from l in ImageCodecInfo.GetImageEncoders() where l.FormatID == imageFormat.Guid select l).First(); EncoderParameters encoderParameters = new(1); encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); @@ -124,22 +123,22 @@ public class C_Resize public static (ImageCodecInfo imageCodecInfo, EncoderParameters encoderParameters, string filenameExtension) GetTuple(string outputExtension, int outputQuality) { (ImageCodecInfo, EncoderParameters, string) result; - System.Drawing.Imaging.ImageFormat imageFormat = outputExtension switch + ImageFormat imageFormat = outputExtension switch { - ".gif" => System.Drawing.Imaging.ImageFormat.Gif, - ".jfif" => System.Drawing.Imaging.ImageFormat.Jpeg, - ".jpe" => System.Drawing.Imaging.ImageFormat.Jpeg, - ".jpeg" => System.Drawing.Imaging.ImageFormat.Jpeg, - ".jpg" => System.Drawing.Imaging.ImageFormat.Jpeg, - ".png" => System.Drawing.Imaging.ImageFormat.Png, - ".tif" => System.Drawing.Imaging.ImageFormat.Tiff, - ".tiff" => System.Drawing.Imaging.ImageFormat.Tiff, + ".gif" => ImageFormat.Gif, + ".jfif" => ImageFormat.Jpeg, + ".jpe" => ImageFormat.Jpeg, + ".jpeg" => ImageFormat.Jpeg, + ".jpg" => ImageFormat.Jpeg, + ".png" => ImageFormat.Png, + ".tif" => ImageFormat.Tiff, + ".tiff" => ImageFormat.Tiff, _ => throw new Exception(), }; ImageCodecInfo imageCodecInfo = (from l in ImageCodecInfo.GetImageEncoders() where l.FormatID == imageFormat.Guid select l).First(); EncoderParameters encoderParameters = new(1); - // encoderParameters.Param[0] = New EncoderParameter(System.Drawing.Imaging.Encoder.Quality, CType(75L, Int32)) 'Default - // encoderParameters.Param[0] = New EncoderParameter(System.Drawing.Imaging.Encoder.Quality, CType(95L, Int32)) 'Paint + // encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(75L, Int32)) 'Default + // encoderParameters.Param[0] = New EncoderParameter(Encoder.Quality, CType(95L, Int32)) 'Paint encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, outputQuality); if (string.IsNullOrEmpty(imageCodecInfo.FilenameExtension)) throw new NullReferenceException(nameof(imageCodecInfo.FilenameExtension)); @@ -163,17 +162,17 @@ public class C_Resize private void CopyPropertyItems(byte[] bytes, PropertyItem[] propertyItems, Bitmap bitmap) { bool hasId = false; - int id = (int)IExif.Tags.DateTimeDigitized; - int imageWidth = (int)IExif.Tags.ImageWidth; - int imageLength = (int)IExif.Tags.ImageLength; - int orientation = (int)IExif.Tags.Orientation; + int id = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized; + int imageWidth = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageWidth; + int imageHeight = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagImageHeight; + int orientation = MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagOrientation; foreach (PropertyItem propertyItem in propertyItems) { if (propertyItem.Id == id) hasId = true; else if (propertyItem.Id == imageWidth) continue; - else if (propertyItem.Id == imageLength) + else if (propertyItem.Id == imageHeight) continue; else if (propertyItem.Id == orientation) continue; diff --git a/Shared/Models/ExifDirectory.cs b/Shared/Models/ExifDirectory.cs index 45c5dc6..65747b8 100644 --- a/Shared/Models/ExifDirectory.cs +++ b/Shared/Models/ExifDirectory.cs @@ -3,20 +3,20 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; -public record ExifDirectory(AviDirectory AviDirectory, - ExifDirectoryBase ExifDirectoryBase, - string File, - FileMetadataDirectory FileMetadataDirectory, - GifHeaderDirectory GifHeaderDirectory, - GpsDirectory GpsDirectory, +public record ExifDirectory(AviDirectory[] AviDirectories, + ExifDirectoryBase[] ExifBaseDirectories, + FileMetadataDirectory[] FileMetadataDirectories, + GifHeaderDirectory[] GifHeaderDirectories, + GpsDirectory[] GpsDirectories, int? Height, - string JsonFile, - JpegDirectory JpegDirectory, - PhotoshopDirectory PhotoshopDirectory, - PngDirectory PngDirectory, - QuickTimeMovieHeaderDirectory QuickTimeMovieHeaderDirectory, - QuickTimeTrackHeaderDirectory QuickTimeTrackHeaderDirectory, - WebPDirectory WebPDirectory, + JpegDirectory[] JpegDirectories, + MakernoteDirectory[] MakernoteDirectories, + string OriginalFileName, + PhotoshopDirectory[] PhotoshopDirectories, + PngDirectory[] PngDirectories, + QuickTimeMovieHeaderDirectory[] QuickTimeMovieHeaderDirectories, + QuickTimeTrackHeaderDirectory[] QuickTimeTrackHeaderDirectories, + WebPDirectory[] WebPDirectories, int? Width) { @@ -28,7 +28,7 @@ public record ExifDirectory(AviDirectory AviDirectory, } -[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] [JsonSerializable(typeof(ExifDirectory))] public partial class ExifDirectorySourceGenerationContext : JsonSerializerContext { diff --git a/Shared/Models/LocationContainer.cs b/Shared/Models/LocationContainer.cs index 93caf6a..9b711e9 100644 --- a/Shared/Models/LocationContainer.cs +++ b/Shared/Models/LocationContainer.cs @@ -2,14 +2,14 @@ using System.Drawing; namespace View_by_Distance.Shared.Models; -public record LocationContainer(DateOnly CreationDateOnly, - IReadOnlyList Directories, - int? DirectoryNumber, - string DisplayDirectoryName, - FilePath FilePath, - bool FromDistanceContent, - int Id, - Location? Location, - long PersonKey, - RectangleF? Rectangle, - int WholePercentages); \ No newline at end of file +public record LocationContainer(DateOnly CreationDateOnly, + ExifDirectory? ExifDirectory, + int? DirectoryNumber, + string DisplayDirectoryName, + FilePath FilePath, + bool FromDistanceContent, + int Id, + Location? Location, + long PersonKey, + RectangleF? Rectangle, + int WholePercentages); \ No newline at end of file diff --git a/Shared/Models/MakernoteDirectory.cs b/Shared/Models/MakernoteDirectory.cs new file mode 100644 index 0000000..d5efe00 --- /dev/null +++ b/Shared/Models/MakernoteDirectory.cs @@ -0,0 +1,23 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace View_by_Distance.Shared.Models; + +public record MakernoteDirectory(string? CameraSerialNumber, + string? FirmwareVersion, + string? QualityAndFileFormat) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, MakernoteDirectorySourceGenerationContext.Default.MakernoteDirectory); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(MakernoteDirectory))] +public partial class MakernoteDirectorySourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/Methods/IDistance.cs b/Shared/Models/Methods/IDistance.cs index f029a4a..1958ad1 100644 --- a/Shared/Models/Methods/IDistance.cs +++ b/Shared/Models/Methods/IDistance.cs @@ -2,9 +2,9 @@ using System.Collections.ObjectModel; namespace View_by_Distance.Shared.Models.Methods; -public interface IDistance +public interface IDistance { - ReadOnlyCollection GetRelationContainers(int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection> locationContainers); + ReadOnlyCollection GetRelationContainers(int faceDistancePermyriad, int locationContainerDistanceTake, float locationContainerDistanceTolerance, ReadOnlyCollection locationContainers); } \ No newline at end of file diff --git a/Shared/Models/PngDirectory.cs b/Shared/Models/PngDirectory.cs index 4951a7f..de7f6a6 100644 --- a/Shared/Models/PngDirectory.cs +++ b/Shared/Models/PngDirectory.cs @@ -4,7 +4,8 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; public record PngDirectory(string? ImageHeight, - string? ImageWidth) + string? ImageWidth, + string? TextualData) { public override string ToString() diff --git a/Shared/Models/Stateless/IExif.cs b/Shared/Models/Stateless/IExif.cs index 63926ee..c1e85a6 100644 --- a/Shared/Models/Stateless/IExif.cs +++ b/Shared/Models/Stateless/IExif.cs @@ -1,148 +1,148 @@ -namespace View_by_Distance.Shared.Models.Stateless; +// namespace View_by_Distance.Shared.Models.Stateless; -public interface IExif -{ +// public interface IExif +// { - enum Tags : ushort - { - GPSVersionID = 0, - GPSLatitudeRef = 1, - GPSLatitude = 2, - GPSLongitudeRef = 3, - GPSLongitude = 4, - GPSAltitudeRef = 5, - GPSAltitude = 6, - GPSTimestamp = 7, - GPSSatellites = 8, - GPSStatus = 9, - GPSMeasureMode = 10, - GPSDOP = 11, - GPSSpeedRef = 12, - GPSSpeed = 13, - GPSTrackRef = 14, - GPSTrack = 15, - GPSImgDirectionRef = 16, - GPSImgDirection = 17, - GPSMapDatum = 18, - GPSDestLatitudeRef = 19, - GPSDestLatitude = 20, - GPSDestLongitudeRef = 21, - GPSDestLongitude = 22, - GPSDestBearingRef = 23, - GPSDestBearing = 24, - GPSDestDistanceRef = 25, - GPSDestDistance = 26, - GPSProcessingMethod = 27, - GPSAreaInformation = 28, - GPSDateStamp = 29, - GPSDifferential = 30, - GPSHPositioningError = 31, - ImageWidth = 256, - ImageLength = 257, - BitsPerSample = 258, - Compression = 259, - PhotometricInterpretation = 262, - ImageDescription = 270, - Make = 271, - Model = 272, - StripOffsets = 273, - Orientation = 274, - SamplesPerPixel = 277, - RowsPerStrip = 278, - StripByteCounts = 279, - XResolution = 282, - YResolution = 283, - PlanarConfiguration = 284, - ResolutionUnit = 296, - TransferFunction = 301, - Software = 305, - DateTime = 306, - Artist = 315, - WhitePoint = 318, - PrimaryChromaticities = 319, - JPEGInterchangeFormat = 513, - JPEGInterchangeFormatLength = 514, - YCbCrCoefficients = 529, - YCbCrSubSampling = 530, - YCbCrPositioning = 531, - ReferenceBlackWhite = 532, - Copyright = 33432, - ExposureTime = 33434, - FNumber = 33437, - ExposureProgram = 34850, - SpectralSensitivity = 34852, - ISOSpeedRatings = 34855, -#pragma warning disable CA1069 - PhotographicSensitivity = 34855, -#pragma warning restore CA1069 - OECF = 34856, - SensitivityType = 34864, - StandardOutputSensitivity = 34865, - RecommendedExposureIndex = 34866, - ISOSpeed = 34867, - ISOSpeedLatitudeyyy = 34868, - ISOSpeedLatitudezzz = 34869, - ExifVersion = 36864, - DateTimeOriginal = 36867, - DateTimeDigitized = 36868, - ComponentsConfiguration = 37121, - CompressedBitsPerPixel = 37122, - ShutterSpeedValue = 37377, - ApertureValue = 37378, - BrightnessValue = 37379, - ExposureBiasValue = 37380, - MaxApertureValue = 37381, - SubjectDistance = 37382, - MeteringMode = 37383, - LightSource = 37384, - Flash = 37385, - FocalLength = 37386, - SubjectArea = 37396, - MakerNote = 37500, - UserComment = 37510, - SubsecTime = 37520, - SubsecTimeOriginal = 37521, - SubsecTimeDigitized = 37522, - XPTitle = 40091, - XPComment = 40092, - XPAuthor = 40093, - XPKeywords = 40094, - XPSubject = 40095, - FlashpixVersion = 40960, - ColorSpace = 40961, - PixelXDimension = 40962, - PixelYDimension = 40963, - RelatedSoundFile = 40964, - FlashEnergy = 41483, - SpatialFrequencyResponse = 41484, - FocalPlaneXResolution = 41486, - FocalPlaneYResolution = 41487, - FocalPlaneResolutionUnit = 41488, - SubjectLocation = 41492, - ExposureIndex = 41493, - SensingMethod = 41495, - FileSource = 41728, - SceneType = 41729, - CFAPattern = 41730, - CustomRendered = 41985, - ExposureMode = 41986, - WhiteBalance = 41987, - DigitalZoomRatio = 41988, - FocalLengthIn35mmFilm = 41989, - SceneCaptureType = 41990, - GainControl = 41991, - Contrast = 41992, - Saturation = 41993, - Sharpness = 41994, - DeviceSettingDescription = 41995, - SubjectDistanceRange = 41996, - ImageUniqueID = 42016, - CameraOwnerName = 42032, - BodySerialNumber = 42033, - LensSpecification = 42034, - LensMake = 42035, - LensModel = 42036, - LensSerialNumber = 42037 - } +// enum Tags : ushort +// { +// GPSVersionID = 0, +// GPSLatitudeRef = 1, +// GPSLatitude = 2, +// GPSLongitudeRef = 3, +// GPSLongitude = 4, +// GPSAltitudeRef = 5, +// GPSAltitude = 6, +// GPSTimestamp = 7, +// GPSSatellites = 8, +// GPSStatus = 9, +// GPSMeasureMode = 10, +// GPSDOP = 11, +// GPSSpeedRef = 12, +// GPSSpeed = 13, +// GPSTrackRef = 14, +// GPSTrack = 15, +// GPSImgDirectionRef = 16, +// GPSImgDirection = 17, +// GPSMapDatum = 18, +// GPSDestLatitudeRef = 19, +// GPSDestLatitude = 20, +// GPSDestLongitudeRef = 21, +// GPSDestLongitude = 22, +// GPSDestBearingRef = 23, +// GPSDestBearing = 24, +// GPSDestDistanceRef = 25, +// GPSDestDistance = 26, +// GPSProcessingMethod = 27, +// GPSAreaInformation = 28, +// GPSDateStamp = 29, +// GPSDifferential = 30, +// GPSHPositioningError = 31, +// ImageWidth = 256, +// ImageLength = 257, +// BitsPerSample = 258, +// Compression = 259, +// PhotometricInterpretation = 262, +// ImageDescription = 270, +// Make = 271, +// Model = 272, +// StripOffsets = 273, +// Orientation = 274, +// SamplesPerPixel = 277, +// RowsPerStrip = 278, +// StripByteCounts = 279, +// XResolution = 282, +// YResolution = 283, +// PlanarConfiguration = 284, +// ResolutionUnit = 296, +// TransferFunction = 301, +// Software = 305, +// DateTime = 306, +// Artist = 315, +// WhitePoint = 318, +// PrimaryChromaticities = 319, +// JPEGInterchangeFormat = 513, +// JPEGInterchangeFormatLength = 514, +// YCbCrCoefficients = 529, +// YCbCrSubSampling = 530, +// YCbCrPositioning = 531, +// ReferenceBlackWhite = 532, +// Copyright = 33432, +// ExposureTime = 33434, +// FNumber = 33437, +// ExposureProgram = 34850, +// SpectralSensitivity = 34852, +// ISOSpeedRatings = 34855, +// #pragma warning disable CA1069 +// PhotographicSensitivity = 34855, +// #pragma warning restore CA1069 +// OECF = 34856, +// SensitivityType = 34864, +// StandardOutputSensitivity = 34865, +// RecommendedExposureIndex = 34866, +// ISOSpeed = 34867, +// ISOSpeedLatitudeyyy = 34868, +// ISOSpeedLatitudezzz = 34869, +// ExifVersion = 36864, +// DateTimeOriginal = 36867, +// DateTimeDigitized = 36868, +// ComponentsConfiguration = 37121, +// CompressedBitsPerPixel = 37122, +// ShutterSpeedValue = 37377, +// ApertureValue = 37378, +// BrightnessValue = 37379, +// ExposureBiasValue = 37380, +// MaxApertureValue = 37381, +// SubjectDistance = 37382, +// MeteringMode = 37383, +// LightSource = 37384, +// Flash = 37385, +// FocalLength = 37386, +// SubjectArea = 37396, +// MakerNote = 37500, +// UserComment = 37510, +// SubsecTime = 37520, +// SubsecTimeOriginal = 37521, +// SubsecTimeDigitized = 37522, +// XPTitle = 40091, +// XPComment = 40092, +// XPAuthor = 40093, +// XPKeywords = 40094, +// XPSubject = 40095, +// FlashpixVersion = 40960, +// ColorSpace = 40961, +// PixelXDimension = 40962, +// PixelYDimension = 40963, +// RelatedSoundFile = 40964, +// FlashEnergy = 41483, +// SpatialFrequencyResponse = 41484, +// FocalPlaneXResolution = 41486, +// FocalPlaneYResolution = 41487, +// FocalPlaneResolutionUnit = 41488, +// SubjectLocation = 41492, +// ExposureIndex = 41493, +// SensingMethod = 41495, +// FileSource = 41728, +// SceneType = 41729, +// CFAPattern = 41730, +// CustomRendered = 41985, +// ExposureMode = 41986, +// WhiteBalance = 41987, +// DigitalZoomRatio = 41988, +// FocalLengthIn35mmFilm = 41989, +// SceneCaptureType = 41990, +// GainControl = 41991, +// Contrast = 41992, +// Saturation = 41993, +// Sharpness = 41994, +// DeviceSettingDescription = 41995, +// SubjectDistanceRange = 41996, +// ImageUniqueID = 42016, +// CameraOwnerName = 42032, +// BodySerialNumber = 42033, +// LensSpecification = 42034, +// LensMake = 42035, +// LensModel = 42036, +// LensSerialNumber = 42037 +// } -} \ No newline at end of file +// } \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ILocation.cs b/Shared/Models/Stateless/Methods/ILocation.cs index e88c8d3..a9dca6e 100644 --- a/Shared/Models/Stateless/Methods/ILocation.cs +++ b/Shared/Models/Stateless/Methods/ILocation.cs @@ -25,9 +25,9 @@ public interface ILocation static Models.Location? GetLocation(DatabaseFile databaseFile, Marker marker, Models.OutputResolution outputResolution) => Location.GetLocation(databaseFile, marker, outputResolution); - List TestStatic_GetLocations(List> locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) => + List TestStatic_GetLocations(List locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) => GetLocations(locationContainers, faces, mappingFromPhotoPrismCollection, rectangleIntersectMinimum); - static List GetLocations(List> locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) => + static List GetLocations(List locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) => Location.GetLocations(locationContainers, faces, mappingFromPhotoPrismCollection, rectangleIntersectMinimum); RectangleF? TestStatic_GetPercentagesRectangle(int locationDigits, int wholePercentages) => diff --git a/Shared/Models/Stateless/Methods/Location.cs b/Shared/Models/Stateless/Methods/Location.cs index bbe2c1f..c00c484 100644 --- a/Shared/Models/Stateless/Methods/Location.cs +++ b/Shared/Models/Stateless/Methods/Location.cs @@ -245,7 +245,7 @@ internal abstract class Location return result; } - internal static List GetLocations(List> locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) + internal static List GetLocations(List locationContainers, List faces, List mappingFromPhotoPrismCollection, float rectangleIntersectMinimum) { List results = []; bool any; @@ -266,7 +266,7 @@ internal abstract class Location outputResolution ??= face.OutputResolution; } int before = results.Count; - foreach (LocationContainer locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { if (locationContainer.Location is null) continue; @@ -289,7 +289,7 @@ internal abstract class Location location = GetLocation(mappingFromPhotoPrism.DatabaseFile, marker, prismRectangle.Value); if (location is null) break; - foreach (LocationContainer locationContainer in locationContainers) + foreach (LocationContainer locationContainer in locationContainers) { if (any) continue; diff --git a/Tests/UnitTestResize.cs b/Tests/UnitTestResize.cs index a70479a..5208ac7 100644 --- a/Tests/UnitTestResize.cs +++ b/Tests/UnitTestResize.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; using Phares.Shared; -using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing.Imaging; using System.Reflection; @@ -183,10 +182,10 @@ public class UnitTestResize resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize); string blurHash = blurHasher.Encode(resizedFileHolder); Assert.IsNotNull(blurHash); - ReadOnlyDictionary metadataExtractorDirectories = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); - string json = JsonSerializer.Serialize(metadataExtractorDirectories, ReadOnlyDictionaryStringMetadataExtractorDirectorySourceGenerationContext.Default.ReadOnlyDictionaryStringMetadataExtractorDirectory); + ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); + string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase); File.WriteAllText("../../../.json", json); - MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(metadataExtractorDirectories); + MetadataExtractor.GeoLocation? geoLocation = Metadata.Models.Stateless.Methods.IMetadata.GeoLocation(exifDirectory); double? distance = geoLocation is null ? null : Metadata.Models.Stateless.Methods.IMetadata.GetDistance(1, 1, geoLocation.Latitude, geoLocation.Longitude); NonThrowTryCatch(); } diff --git a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs index 873bd8e..28e400e 100644 --- a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs +++ b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs @@ -1,7 +1,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; using Phares.Shared; -using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing.Imaging; using System.Reflection; @@ -251,14 +250,13 @@ public class UnitTestFace resizedFileHolder = resize.GetResizedFileHolder(cResultsFullGroupDirectory, item, outputResolutionHasNumber, filePath.Id.Value); item.SetResizedFileHolder(resize.FileNameExtension, resizedFileHolder); MappingFromItem mappingFromItem = IMappingFromItem.GetMappingFromItem(item); - IReadOnlyList directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(resizedFileHolder.FullName); Dictionary outputResolutionToResize = resize.GetResizeKeyValuePairs(_PropertyConfiguration, cResultsFullGroupDirectory, item.FilePath, subFileTuples, parseExceptions, item.Property, mappingFromItem); Assert.IsNotNull(mappingFromItem.ResizedFileHolder); resize.SaveResizedSubfile(_PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize); string blurHash = blurHasher.Encode(resizedFileHolder); Assert.IsNotNull(blurHash); - ReadOnlyDictionary? metadataExtractorDirectories = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); - string json = JsonSerializer.Serialize(metadataExtractorDirectories, ReadOnlyDictionaryStringMetadataExtractorDirectorySourceGenerationContext.Default.ReadOnlyDictionaryStringMetadataExtractorDirectory); + ExifDirectory exifDirectory = metadata.GetMetadataCollection(item.FilePath, subFileTuples, parseExceptions, changesFrom, mappingFromItem); + string json = JsonSerializer.Serialize(exifDirectory, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase); File.WriteAllText("../../../.json", json); Image image = FaceRecognition.LoadImageFile(mappingFromItem.ResizedFileHolder.FullName); Assert.IsNotNull(image);