diff --git a/Damselfly.Core.DbModels/Models/Entities/ImageMetaData.cs b/Damselfly.Core.DbModels/Models/Entities/ImageMetaData.cs index 20681c57..d4509361 100644 --- a/Damselfly.Core.DbModels/Models/Entities/ImageMetaData.cs +++ b/Damselfly.Core.DbModels/Models/Entities/ImageMetaData.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using System.Runtime.CompilerServices; namespace Damselfly.Core.Models; @@ -51,4 +52,24 @@ public class ImageMetaData // Date we last performed face/object/image recognition // If this is null, AI will be reprocessed public DateTime? AILastUpdated { get; set; } + + public void Clear() + { + this.DateTaken = DateTime.MinValue; + this.Height = 0; + this.Width = 0; + this.Description = null; + this.Caption = null; + this.DominantColor = null; + this.AspectRatio = 1; + this.Rating = 0; + this.Credit = null; + this.ISO = null; + this.FNum = null; + this.Exposure = null; + this.FNum = null; + this.FlashFired = false; + this.Latitude = null; + this.Longitude = null; + } } \ No newline at end of file diff --git a/Damselfly.Core.ScopedServices/Client Services/ClientImageCacheService.cs b/Damselfly.Core.ScopedServices/Client Services/ClientImageCacheService.cs index 5d68e732..dfb70118 100644 --- a/Damselfly.Core.ScopedServices/Client Services/ClientImageCacheService.cs +++ b/Damselfly.Core.ScopedServices/Client Services/ClientImageCacheService.cs @@ -171,8 +171,7 @@ private async Task LoadAndCacheImage(int imageId) if (_memoryCache.TryGetValue(imageId, out var image)) return image; - // This should never happen, if our caching is working. :) - _logger.LogWarning($"No image found in client-side cache for ID: {imageId}."); + _logger.LogTrace($"No image found in client-side cache for ID: {imageId}."); try { diff --git a/Damselfly.Core/Services/ImageRecognitionService.cs b/Damselfly.Core/Services/ImageRecognitionService.cs index ea1d7c02..b5b33927 100644 --- a/Damselfly.Core/Services/ImageRecognitionService.cs +++ b/Damselfly.Core/Services/ImageRecognitionService.cs @@ -328,17 +328,13 @@ public async Task CreateMissingPeople(IEnumerable detectedFac State = Person.PersonState.Unknown, LastUpdated = DateTime.UtcNow, PersonGuid = x.PersonGuid, - FaceData = new List { new() - { - Score = x.Score, - Embeddings = string.Join( ",", x.Embeddings) - } }, + FaceData = BuildFaceData(x), }).ToList(); if ( newPeople.Any() ) { await db.People.AddRangeAsync( newPeople ); - await db.SaveChangesAsync(); + await db.SaveChangesAsync("AddPeople"); // Add or replace the new people in the cache (this should always add) newPeople.ForEach(x => _peopleCache[x.PersonGuid] = x); @@ -352,6 +348,32 @@ public async Task CreateMissingPeople(IEnumerable detectedFac } } + /// + /// Create the face data for an image detect result - being careful + /// not to return anything if the detect result didn't actually + /// contain any embeddings. + /// + /// + /// + private List BuildFaceData( ImageDetectResult detectResult ) + { + List? result = new(); + + if( detectResult.Embeddings.Length > 0 ) + { + result = new List + { + new() + { + Score = detectResult.Score, + Embeddings = string.Join( ",", detectResult.Embeddings) + } + }; + } + + return result; + } + /// /// Given a collection of detected objects, create the tags, put them in the cache, /// and then return a list of keyword => TagID key-value pairs diff --git a/Damselfly.Core/Services/MetaDataService.cs b/Damselfly.Core/Services/MetaDataService.cs index 142b17ff..360841fe 100644 --- a/Damselfly.Core/Services/MetaDataService.cs +++ b/Damselfly.Core/Services/MetaDataService.cs @@ -185,7 +185,7 @@ public void StartService() _workService.AddJobSource(this); } - + /// /// Read the metadata, and handle any exceptions. /// @@ -216,6 +216,20 @@ private IReadOnlyList SafeReadImageMetadata(string imagePath) return metadata; } + private string? GetXMPFieldValue( Dictionary kvps, string prefix ) + { + if( kvps.TryGetValue( prefix, out var result ) && ! string.IsNullOrEmpty(result)) + return result; + + var possibilities = Enumerable.Range(1, 5).Select( n => $"{prefix}[{n}]"); + + foreach( var key in possibilities ) + if( kvps.TryGetValue( key, out var indexed ) && ! string.IsNullOrEmpty(indexed) ) + return indexed; + + return null; + } + /// /// Pull out the XMP attributes /// @@ -227,18 +241,19 @@ private void ReadXMPData(XmpDirectory xmpDirectory, Image image) { var nvps = xmpDirectory.XmpMeta.Properties .Where(x => !string.IsNullOrEmpty(x.Path)) - .ToDictionary(x => x.Path, y => y.Value); + .ToDictionary(x => x.Path, y => y.Value, StringComparer.OrdinalIgnoreCase); if( image.MetaData.DateTaken == DateTime.MinValue ) { if( nvps.ContainsKey("exif:DateTimeOriginal") && DateTime.TryParse(nvps["exif:DateTimeOriginal"], out var dateTime) ) image.MetaData.DateTaken = dateTime; } - if( string.IsNullOrEmpty(image.MetaData.Description) && nvps.ContainsKey("exif:Description")) - image.MetaData.Description = nvps["exif:Description"]; - - if( string.IsNullOrEmpty(image.MetaData.Caption) && nvps.ContainsKey("exif:Caption") ) - image.MetaData.Caption = nvps["exif:Caption"]; + + if( string.IsNullOrEmpty(image.MetaData.Description) ) + image.MetaData.Description = GetXMPFieldValue( nvps, "dc:Description" ); + + if( string.IsNullOrEmpty(image.MetaData.Caption) ) + image.MetaData.Caption = GetXMPFieldValue( nvps, "dc:Caption" ); } catch ( Exception ex ) { @@ -359,6 +374,9 @@ private bool GetImageMetaData(ref ImageMetaData imgMetaData, out string[] keywor if ( metadata != null ) { + // We've read some EXIF data, so clear out the existing metadata to replace it + imgMetaData.Clear(); + metaDataReadSuccess = true; var subIfdDirectory = metadata.OfType().FirstOrDefault(); diff --git a/Damselfly.Core/Utils/DBSetExtensions.cs b/Damselfly.Core/Utils/DBSetExtensions.cs index 0e9a624c..559b54cf 100644 --- a/Damselfly.Core/Utils/DBSetExtensions.cs +++ b/Damselfly.Core/Utils/DBSetExtensions.cs @@ -61,6 +61,6 @@ public static async Task BulkUpdateWithSaveChanges(this BaseDBModel db, dbSet.Update(e); } - return await db.SaveChangesAsync(); + return await db.SaveChangesAsync("BulkUpdateWithSaveChanges"); } } \ No newline at end of file diff --git a/Damselfly.Web.Client/Program.cs b/Damselfly.Web.Client/Program.cs index c88939e2..9e7032b1 100644 --- a/Damselfly.Web.Client/Program.cs +++ b/Damselfly.Web.Client/Program.cs @@ -45,7 +45,7 @@ public static async Task Main(string[] args) // Supply HttpClient instances that include access tokens when making requests to the server project builder.Services.AddScoped(sp => sp.GetRequiredService().CreateClient("DamselflyAPI")); - builder.Services.AddMemoryCache(x => x.SizeLimit = 5000); + builder.Services.AddMemoryCache(x => x.SizeLimit = 500); builder.Services.AddAuthorizationCore(config => config.SetupPolicies(builder.Services)); diff --git a/VERSION b/VERSION index ee74734a..627a3f43 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.1.0 +4.1.1