diff --git a/.vscode/settings.json b/.vscode/settings.json index 1e598c7..a38c24f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,6 +22,7 @@ "Hmmssfff", "jfif", "JOSN", + "Makernote", "mmod", "Nicéphore", "Niépce", diff --git a/Metadata/Models/A_Metadata.cs b/Metadata/Models/A_Metadata.cs index 38fbb24..c15eb22 100644 --- a/Metadata/Models/A_Metadata.cs +++ b/Metadata/Models/A_Metadata.cs @@ -1,8 +1,6 @@ -using MetadataExtractor; using System.Collections.ObjectModel; using System.Text.Json; using View_by_Distance.Metadata.Models.Stateless; -using View_by_Distance.Metadata.Models.Stateless.Methods; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless.Methods; @@ -104,9 +102,7 @@ public class A_Metadata if (result is null) { string json; - System.Drawing.Size? size = Dimensions.GetDimensions(filePath.FullName); - IReadOnlyList directories = ImageMetadataReader.ReadMetadata(filePath.FullName); - result = Exif.Covert(filePath, deterministicHashCode, size, directories); + result = Exif.GetExifDirectory(filePath, deterministicHashCode); (int exifYear, string jsonFile) = GetJsonFile(metadataConfiguration.ResultConfiguration, filePath, result); if (exifYear == fileInfoMinimumYear) json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory); 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/Exif.cs b/Metadata/Models/Stateless/Exif.cs index 1804621..3353013 100644 --- a/Metadata/Models/Stateless/Exif.cs +++ b/Metadata/Models/Stateless/Exif.cs @@ -1,6 +1,8 @@ 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; @@ -23,14 +25,14 @@ internal abstract class Exif return result; } - private static Shared.Models.AviDirectory GetAviDirectory(IReadOnlyList directories) + private static Shared.Models.AviDirectory[] GetAviDirectories(IReadOnlyList directories) { - Shared.Models.AviDirectory result; - MetadataExtractor.Formats.Avi.AviDirectory? aviDirectory = directories.OfType().FirstOrDefault(); - if (aviDirectory is null) - result = new(null, null, null, null); - else + 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); @@ -39,19 +41,21 @@ internal abstract class Exif dateTimeOriginal = checkDateTime; else dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); - result = new(dateTimeOriginal, duration, height, width); + if (dateTimeOriginal is null && duration is null && height is null && width is null) + continue; + results.Add(new(dateTimeOriginal, duration, height, width)); } - return result; + return results.ToArray(); } - private static Shared.Models.ExifDirectoryBase GetExifDirectoryBase(IReadOnlyList directories) + private static Shared.Models.ExifDirectoryBase[] GetExifBaseDirectories(IReadOnlyList directories) { - Shared.Models.ExifDirectoryBase result; - ExifDirectoryBase? exifDirectoryBase = directories.OfType().FirstOrDefault(); - if (exifDirectoryBase is null) - result = new(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); - else + List results = []; + IEnumerable exifBaseDirectories = directories.OfType(); + foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories) { + if (exifDirectoryBase.Tags.Count == 0) + continue; DateTime? dateTime; DateTime checkDateTime; DateTime? dateTimeOriginal; @@ -111,64 +115,113 @@ internal abstract class Exif dateTimeDigitized = checkDateTime; else dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized)); - result = 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); + 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 result; + return results.ToArray(); } - private static Shared.Models.FileMetadataDirectory GetFileMetadataDirectory(string file, IReadOnlyList directories) + private static Shared.Models.FileMetadataDirectory[] GetFileMetadataDirectories(string file, IReadOnlyList directories) { - Shared.Models.FileMetadataDirectory result; - MetadataExtractor.Formats.FileSystem.FileMetadataDirectory? fileMetadataDirectory = directories.OfType().FirstOrDefault(); - if (fileMetadataDirectory is null) - result = new(null, null, null); - else + 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); @@ -178,49 +231,38 @@ internal abstract class Exif fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate)); if (fileName is null || !file.EndsWith(fileName)) throw new NotSupportedException($"!{file}.EndsWith({fileName})"); - result = new(fileModifiedDate, fileName, fileSize); + if (fileModifiedDate is null && fileName is null && fileSize is null) + continue; + results.Add(new(fileModifiedDate, fileName, fileSize)); } - return result; + return results.ToArray(); } - private static Shared.Models.GifHeaderDirectory GetGifHeaderDirectory(IReadOnlyList directories) + private static Shared.Models.GifHeaderDirectory[] GetGifHeaderDirectories(IReadOnlyList directories) { - Shared.Models.GifHeaderDirectory result; - MetadataExtractor.Formats.Gif.GifHeaderDirectory? gifHeaderDirectory = directories.OfType().FirstOrDefault(); - if (gifHeaderDirectory is null) - result = new(null, null); - else + 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); - result = new(imageHeight, imageWidth); + if (imageHeight is null && imageWidth is null) + continue; + results.Add(new(imageHeight, imageWidth)); } - return result; + return results.ToArray(); } - private static Shared.Models.PhotoshopDirectory GetPhotoshopDirectory(IReadOnlyList directories) + private static Shared.Models.GpsDirectory[] GetGpsDirectories(IReadOnlyList directories) { - Shared.Models.PhotoshopDirectory result; - MetadataExtractor.Formats.Photoshop.PhotoshopDirectory? PhotoshopDirectory = directories.OfType().FirstOrDefault(); - if (PhotoshopDirectory is null) - result = new(null, null); - else - { - string? jpegQuality = PhotoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality); - string? url = PhotoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl); - result = new(jpegQuality, url); - } - return result; - } - - private static Shared.Models.GpsDirectory GetGpsDirectory(IReadOnlyList directories) - { - Shared.Models.GpsDirectory result; - GpsDirectory? gpsDirectory = directories.OfType().FirstOrDefault(); - if (gpsDirectory is null) - result = new(null, null, null, null, null, null); - else + 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); @@ -231,136 +273,257 @@ internal abstract class Exif timeStamp = checkDateTime; else timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp)); - result = new(altitude, - latitude, - latitudeRef, - longitude, - longitudeRef, - timeStamp); + 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 result; + return results.ToArray(); } - private static Shared.Models.JpegDirectory GetJpegDirectory(IReadOnlyList directories) + private static Shared.Models.JpegDirectory[] GetJpegDirectories(IReadOnlyList directories) { - Shared.Models.JpegDirectory result; - MetadataExtractor.Formats.Jpeg.JpegDirectory? jpegDirectory = directories.OfType().FirstOrDefault(); - if (jpegDirectory is null) - result = new(null, null); - else + 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); - result = new(imageHeight, imageWidth); + if (imageHeight is null && imageWidth is null) + continue; + results.Add(new(imageHeight, imageWidth)); } - return result; + return results.ToArray(); } - private static Shared.Models.PngDirectory GetPngDirectory(IReadOnlyList directories) + private static Shared.Models.MakernoteDirectory[] GetMakernoteDirectories(IReadOnlyList directories) { - Shared.Models.PngDirectory result; - MetadataExtractor.Formats.Png.PngDirectory? pngDirectory = directories.OfType().FirstOrDefault(); - if (pngDirectory is null) - result = new(null, null); - else + 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); - result = new(imageHeight, imageWidth); + 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 result; + return results.ToArray(); } - private static Shared.Models.QuickTimeMovieHeaderDirectory GetQuickTimeMovieHeaderDirectoryDirectory(IReadOnlyList directories) + private static Shared.Models.QuickTimeMovieHeaderDirectory[] GetQuickTimeMovieHeaderDirectoryDirectories(IReadOnlyList directories) { - Shared.Models.QuickTimeMovieHeaderDirectory result; - MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory? aviDirectory = directories.OfType().FirstOrDefault(); - if (aviDirectory is null) - result = new(null); - else + List results = []; + IEnumerable quickTimeMovieHeaderDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in quickTimeMovieHeaderDirectories) { + if (quickTimeMovieHeaderDirectory.Tags.Count == 0) + continue; DateTime? created; - if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime)) + if (quickTimeMovieHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime)) created = checkDateTime; else - created = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); - result = new(created); + created = GetDateTime(quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); + if (created is null) + continue; + results.Add(new(created)); } - return result; + return results.ToArray(); } - private static Shared.Models.QuickTimeTrackHeaderDirectory GetQuickTimeTrackHeaderDirectoryDirectory(IReadOnlyList directories) + private static Shared.Models.QuickTimeTrackHeaderDirectory[] GetQuickTimeTrackHeaderDirectoryDirectories(IReadOnlyList directories) { - Shared.Models.QuickTimeTrackHeaderDirectory result; - MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory? aviDirectory = directories.OfType().FirstOrDefault(); - if (aviDirectory is null) - result = new(null); - else + List results = []; + IEnumerable quickTimeTrackHeaderDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in quickTimeTrackHeaderDirectories) { + if (quickTimeTrackHeaderDirectory.Tags.Count == 0) + continue; DateTime? created; - if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime)) + if (quickTimeTrackHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime)) created = checkDateTime; else - created = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); - result = new(created); + created = GetDateTime(quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); + if (created is null) + continue; + results.Add(new(created)); } - return result; + return results.ToArray(); } - private static Shared.Models.WebPDirectory GetWebPDirectory(IReadOnlyList directories) + private static Shared.Models.WebPDirectory[] GetWebPDirectories(IReadOnlyList directories) { - Shared.Models.WebPDirectory result; - MetadataExtractor.Formats.WebP.WebPDirectory? WebPDirectory = directories.OfType().FirstOrDefault(); - if (WebPDirectory is null) - result = new(null, null); - else + List results = []; + IEnumerable webPDirectories = directories.OfType(); + foreach (MetadataExtractor.Formats.WebP.WebPDirectory webPDirectory in webPDirectories) { - string? imageHeight = WebPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight); - string? imageWidth = WebPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth); - result = new(imageHeight, imageWidth); + 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, Shared.Models.DeterministicHashCode deterministicHashCode, 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, + deterministicHashCode.Id ?? filePath.Id, + jpegDirectories, + MakernoteDirectories, + filePath.Name, + photoshopDirectories, + pngDirectories, + quickTimeMovieHeaderDirectories, + quickTimeTrackHeaderDirectories, + webPDirectories, + size?.Width); return result; } - internal static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode, System.Drawing.Size? size, IReadOnlyList directories) + internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode) { - Shared.Models.ExifDirectory results; - Shared.Models.AviDirectory aviDirectory = GetAviDirectory(directories); - Shared.Models.GpsDirectory gpsDirectory = GetGpsDirectory(directories); - Shared.Models.PngDirectory pngDirectory = GetPngDirectory(directories); - Shared.Models.JpegDirectory jpegDirectory = GetJpegDirectory(directories); - Shared.Models.WebPDirectory webPDirectory = GetWebPDirectory(directories); - Shared.Models.ExifDirectoryBase exifDirectoryBase = GetExifDirectoryBase(directories); - Shared.Models.GifHeaderDirectory gifHeaderDirectory = GetGifHeaderDirectory(directories); - Shared.Models.PhotoshopDirectory photoshopDirectory = GetPhotoshopDirectory(directories); - Shared.Models.FileMetadataDirectory fileMetadataDirectory = GetFileMetadataDirectory(filePath.FullName, directories); - Shared.Models.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory = GetQuickTimeMovieHeaderDirectoryDirectory(directories); - Shared.Models.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory = GetQuickTimeTrackHeaderDirectoryDirectory(directories); - results = new(aviDirectory, - exifDirectoryBase, - fileMetadataDirectory, - gifHeaderDirectory, - gpsDirectory, - size?.Height, - deterministicHashCode.Id ?? filePath.Id, - jpegDirectory, - filePath.Name, - photoshopDirectory, - pngDirectory, - quickTimeMovieHeaderDirectory, - quickTimeTrackHeaderDirectory, - webPDirectory, - size?.Width); - return results; - } - - internal static string GetMaker(Shared.Models.ExifDirectoryBase exifDirectoryBase) - { - string result; - if (string.IsNullOrEmpty(exifDirectoryBase.Make?.ToString().Trim())) - result = "Unknown"; - else - result = $"{exifDirectoryBase.Make[0].ToString().ToUpper()}{exifDirectoryBase.Make[1..].ToLower()}".Trim(); + Shared.Models.ExifDirectory? result; + System.Drawing.Size? size = Dimensions.GetDimensions(filePath.FullName); + IReadOnlyList directories = ImageMetadataReader.ReadMetadata(filePath.FullName); + result = Covert(filePath, deterministicHashCode, size, directories); return result; } 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 index 9fdd359..946c5b7 100644 --- a/Metadata/Models/Stateless/GPS.cs +++ b/Metadata/Models/Stateless/GPS.cs @@ -1,4 +1,6 @@ +using MetadataExtractor; using View_by_Distance.Metadata.Models.Stateless.Methods; +using View_by_Distance.Shared.Models; namespace View_by_Distance.Metadata.Models.Stateless; @@ -85,34 +87,36 @@ internal abstract class GPS 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; - // } + 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 e344103..1f2f513 100644 --- a/Metadata/Models/Stateless/Methods/IMetadata.cs +++ b/Metadata/Models/Stateless/Methods/IMetadata.cs @@ -1,3 +1,6 @@ +using MetadataExtractor; +using View_by_Distance.Shared.Models; + namespace View_by_Distance.Metadata.Models.Stateless.Methods; public interface IMetadata @@ -11,10 +14,35 @@ public interface IMetadata Meters } - string TestStatic_GetMaker(Shared.Models.ExifDirectoryBase exifDirectoryBase) => - GetMaker(exifDirectoryBase); - static string GetMaker(Shared.Models.ExifDirectoryBase exifDirectoryBase) => - Exif.GetMaker(exifDirectoryBase); + ExifDirectory TestStatic_GetExifDirectory(FilePath filePath, DeterministicHashCode deterministicHashCode) => + GetExifDirectory(filePath, deterministicHashCode); + static ExifDirectory GetExifDirectory(FilePath filePath, DeterministicHashCode deterministicHashCode) => + Exif.GetExifDirectory(filePath, deterministicHashCode); + + string TestStatic_GetMaker(ExifDirectory? exifDirectory) => + GetMaker(exifDirectory); + static string GetMaker(ExifDirectory? exifDirectory) => + Base.GetMaker(exifDirectory?.ExifBaseDirectories); + + string TestStatic_GetModel(ExifDirectory? exifDirectory) => + GetModel(exifDirectory); + static string GetModel(ExifDirectory? exifDirectory) => + Base.GetModel(exifDirectory?.ExifBaseDirectories); + + string? TestStatic_GetOutputResolution(ExifDirectory? exifDirectory) => + GetOutputResolution(exifDirectory); + static string? GetOutputResolution(ExifDirectory? exifDirectory) => + Face.GetOutputResolution(exifDirectory?.PngDirectories); + + string? TestStatic_GetFaceEncoding(ExifDirectory? exifDirectory) => + GetFaceEncoding(exifDirectory); + static string? GetFaceEncoding(ExifDirectory? exifDirectory) => + Face.GetFaceEncoding(exifDirectory?.PngDirectories); + + 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); diff --git a/Rename/Rename.cs b/Rename/Rename.cs index 47a9224..f26a22f 100644 --- a/Rename/Rename.cs +++ b/Rename/Rename.cs @@ -221,7 +221,7 @@ public class Rename : IRename throw new NotImplementedException(); else { - string maker = IMetadata.GetMaker(record.ExifDirectory.ExifDirectoryBase); + string maker = IMetadata.GetMaker(record.ExifDirectory); (int seasonValue, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear); string splat = fileHolder.DirectoryName[^3..][1] == '!' ? fileHolder.DirectoryName[^3..] : string.Empty; directoryName = $"{year}.{seasonValue} {seasonName} {maker.Split(' ')[0]}{splat}"; diff --git a/Shared/Models/ExifDirectory.cs b/Shared/Models/ExifDirectory.cs index 1b7dce1..ffb15e5 100644 --- a/Shared/Models/ExifDirectory.cs +++ b/Shared/Models/ExifDirectory.cs @@ -3,20 +3,21 @@ using System.Text.Json.Serialization; namespace View_by_Distance.Shared.Models; -public record ExifDirectory(AviDirectory AviDirectory, - ExifDirectoryBase ExifDirectoryBase, - FileMetadataDirectory FileMetadataDirectory, - GifHeaderDirectory GifHeaderDirectory, - GpsDirectory GpsDirectory, +public record ExifDirectory(AviDirectory[] AviDirectories, + ExifDirectoryBase[] ExifBaseDirectories, + FileMetadataDirectory[] FileMetadataDirectories, + GifHeaderDirectory[] GifHeaderDirectories, + GpsDirectory[] GpsDirectories, int? Height, int? Id, - JpegDirectory JpegDirectory, + JpegDirectory[] JpegDirectories, + MakernoteDirectory[] MakernoteDirectories, string OriginalFileName, - PhotoshopDirectory PhotoshopDirectory, - PngDirectory PngDirectory, - QuickTimeMovieHeaderDirectory QuickTimeMovieHeaderDirectory, - QuickTimeTrackHeaderDirectory QuickTimeTrackHeaderDirectory, - WebPDirectory WebPDirectory, + PhotoshopDirectory[] PhotoshopDirectories, + PngDirectory[] PngDirectories, + QuickTimeMovieHeaderDirectory[] QuickTimeMovieHeaderDirectories, + QuickTimeTrackHeaderDirectory[] QuickTimeTrackHeaderDirectories, + WebPDirectory[] WebPDirectories, int? Width) { @@ -28,7 +29,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/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/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/XDate.cs b/Shared/Models/Stateless/XDate.cs index bfd9cfa..efe9991 100644 --- a/Shared/Models/Stateless/XDate.cs +++ b/Shared/Models/Stateless/XDate.cs @@ -54,14 +54,26 @@ internal abstract class XDate { DateTime? result; List results = []; - if (exifDirectory.ExifDirectoryBase.DateTimeOriginal is not null) - results.Add(exifDirectory.ExifDirectoryBase.DateTimeOriginal.Value); - if (exifDirectory.AviDirectory.DateTimeOriginal is not null) - results.Add(exifDirectory.AviDirectory.DateTimeOriginal.Value); - if (exifDirectory.QuickTimeMovieHeaderDirectory.Created is not null) - results.Add(exifDirectory.QuickTimeMovieHeaderDirectory.Created.Value); - if (exifDirectory.QuickTimeTrackHeaderDirectory.Created is not null) - results.Add(exifDirectory.QuickTimeTrackHeaderDirectory.Created.Value); + foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) + { + if (exifDirectoryBase.DateTimeOriginal is not null) + results.Add(exifDirectoryBase.DateTimeOriginal.Value); + } + foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories) + { + if (aviDirectory.DateTimeOriginal is not null) + results.Add(aviDirectory.DateTimeOriginal.Value); + } + foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories) + { + if (quickTimeMovieHeaderDirectory.Created is not null) + results.Add(quickTimeMovieHeaderDirectory.Created.Value); + } + foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories) + { + if (quickTimeTrackHeaderDirectory.Created is not null) + results.Add(quickTimeTrackHeaderDirectory.Created.Value); + } result = results.Count == 0 ? null : results.Min(); return result; } @@ -121,27 +133,48 @@ internal abstract class XDate { DateTime result; List results = []; - if (exifDirectory.ExifDirectoryBase.DateTimeOriginal is not null) - results.Add(exifDirectory.ExifDirectoryBase.DateTimeOriginal.Value); - if (exifDirectory.AviDirectory.DateTimeOriginal is not null) - results.Add(exifDirectory.AviDirectory.DateTimeOriginal.Value); - if (exifDirectory.QuickTimeMovieHeaderDirectory.Created is not null) - results.Add(exifDirectory.QuickTimeMovieHeaderDirectory.Created.Value); - if (exifDirectory.QuickTimeTrackHeaderDirectory.Created is not null) - results.Add(exifDirectory.QuickTimeTrackHeaderDirectory.Created.Value); + foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) + { + if (exifDirectoryBase.DateTimeOriginal is not null) + results.Add(exifDirectoryBase.DateTimeOriginal.Value); + } + foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories) + { + if (aviDirectory.DateTimeOriginal is not null) + results.Add(aviDirectory.DateTimeOriginal.Value); + } + foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories) + { + if (quickTimeMovieHeaderDirectory.Created is not null) + results.Add(quickTimeMovieHeaderDirectory.Created.Value); + } + foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories) + { + if (quickTimeTrackHeaderDirectory.Created is not null) + results.Add(quickTimeTrackHeaderDirectory.Created.Value); + } if (results.Count == 0) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(exifDirectory.OriginalFileName); DateTime? dateTime = GetDateTimeFromName(fileNameWithoutExtension); if (dateTime is not null) results.Add(dateTime.Value); - if (exifDirectory.ExifDirectoryBase.DateTime is not null) - results.Add(exifDirectory.ExifDirectoryBase.DateTime.Value); - if (exifDirectory.ExifDirectoryBase.DateTimeDigitized is not null) - results.Add(exifDirectory.ExifDirectoryBase.DateTimeDigitized.Value); + foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories) + { + if (exifDirectoryBase.DateTime is not null) + results.Add(exifDirectoryBase.DateTime.Value); + if (exifDirectoryBase.DateTimeDigitized is not null) + results.Add(exifDirectoryBase.DateTimeDigitized.Value); + } + } + if (results.Count == 0) + { + foreach (FileMetadataDirectory fileMetadataDirectory in exifDirectory.FileMetadataDirectories) + { + if (fileMetadataDirectory.FileModifiedDate is not null) + results.Add(fileMetadataDirectory.FileModifiedDate.Value); + } } - if (results.Count == 0 && exifDirectory.FileMetadataDirectory.FileModifiedDate is not null) - results.Add(exifDirectory.FileMetadataDirectory.FileModifiedDate.Value); result = results.Count == 0 ? DateTime.MinValue : results.Min(); return result; }