diff --git a/.editorconfig b/.editorconfig index 27bc698..7410050 100644 --- a/.editorconfig +++ b/.editorconfig @@ -82,33 +82,60 @@ csharp_style_var_elsewhere = false:warning csharp_style_var_for_built_in_types = false:warning csharp_style_var_when_type_is_apparent = false:warning csharp_using_directive_placement = outside_namespace +dotnet_analyzer_diagnostic.category-Design.severity = error +dotnet_analyzer_diagnostic.category-Documentation.severity = error +dotnet_analyzer_diagnostic.category-Globalization.severity = none +dotnet_analyzer_diagnostic.category-Interoperability.severity = error +dotnet_analyzer_diagnostic.category-Maintainability.severity = error +dotnet_analyzer_diagnostic.category-Naming.severity = none +dotnet_analyzer_diagnostic.category-Performance.severity = none +dotnet_analyzer_diagnostic.category-Reliability.severity = error +dotnet_analyzer_diagnostic.category-Security.severity = error +dotnet_analyzer_diagnostic.category-SingleFile.severity = error +dotnet_analyzer_diagnostic.category-Style.severity = error +dotnet_analyzer_diagnostic.category-Usage.severity = error dotnet_code_quality_unused_parameters = all dotnet_code_quality_unused_parameters = non_public # IDE0060: Remove unused parameter dotnet_code_quality.CAXXXX.api_surface = private, internal +dotnet_diagnostic.CA1001.severity = none # CA1001: Types that own disposable fields should be disposable +dotnet_diagnostic.CA1051.severity = none # CA1051: Do not declare visible instance fields dotnet_diagnostic.CA1511.severity = warning # CA1511: Use 'ArgumentException.ThrowIfNullOrEmpty' instead of explicitly throwing a new exception instance +dotnet_diagnostic.CA1511.severity = warning # CA1511: Use 'ArgumentException.ThrowIfNullOrEmpty' instead of explicitly throwing a new exception instance +dotnet_diagnostic.CA1513.severity = warning # Use 'ObjectDisposedException.ThrowIf' instead of explicitly throwing a new exception instance +dotnet_diagnostic.CA1816.severity = none # CA1816: Call GC.SuppressFinalize correctly dotnet_diagnostic.CA1825.severity = warning # CA1823: Avoid zero-length array allocations dotnet_diagnostic.CA1829.severity = warning # CA1829: Use Length/Count property instead of Count() when available dotnet_diagnostic.CA1834.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable +dotnet_diagnostic.CA1854.severity = warning # CA1854: Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' check to avoid double lookup dotnet_diagnostic.CA1860.severity = warning # CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance +dotnet_diagnostic.CA1861.severity = none # CA1861: Prefer 'static readonly' fields over constant array arguments dotnet_diagnostic.CA1862.severity = warning # CA1862: Prefer using 'string.Equals(string, StringComparison)' to perform a case-insensitive comparison, but keep in mind that this might cause subtle changes in behavior, so make sure to conduct thorough testing after applying the suggestion, or if culturally sensitive comparison is not required, consider using 'StringComparison.OrdinalIgnoreCase' +dotnet_diagnostic.CA1866.severity = none # CA1866: Use 'string.EndsWith(char)' instead of 'string.EndsWith(string)' when you have a string with a single char dotnet_diagnostic.CA1869.severity = none # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead. +dotnet_diagnostic.CA2201.severity = none # CA2201: Exception type System.NullReferenceException is reserved by the runtime dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])' dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2"); dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant. dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary +dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement (IDE0010) dotnet_diagnostic.IDE0028.severity = warning # IDE0028: Collection initialization can be simplified dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031) dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed +dotnet_diagnostic.IDE0048.severity = none # Parentheses preferences (IDE0047 and IDE0048) dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references (IDE0049) +dotnet_diagnostic.IDE0051.severity = error # Private member '' is unused [, ] dotnet_diagnostic.IDE0058.severity = warning # IDE0058: Expression value is never used dotnet_diagnostic.IDE0060.severity = warning # IDE0060: Remove unused parameter dotnet_diagnostic.IDE0074.severity = warning # IDE0074: Use compound assignment +dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure (IDE0130) dotnet_diagnostic.IDE0200.severity = warning # IDE0200: Lambda expression can be removed [Map] +dotnet_diagnostic.IDE0230.severity = warning # IDE0230: Use UTF-8 string literal dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290) dotnet_diagnostic.IDE0300.severity = warning # IDE0300: Collection initialization can be simplified dotnet_diagnostic.IDE0301.severity = warning #IDE0301: Collection initialization can be simplified dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified +dotnet_diagnostic.JSON002.severity = warning # JSON002: Probable JSON string detected dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning dotnet_naming_rule.abstract_method_should_be_pascal_case.style = pascal_case dotnet_naming_rule.abstract_method_should_be_pascal_case.symbols = abstract_method diff --git a/.gitignore b/.gitignore index 5449c42..fbfe420 100644 --- a/.gitignore +++ b/.gitignore @@ -468,3 +468,5 @@ globalStorage/ [Ll]ib/ Shared/.kanbn + +.Immich/immich-assets.json diff --git a/.vscode/mklink.md b/.vscode/mklink.md index 5ab8c3a..8c71e4f 100644 --- a/.vscode/mklink.md +++ b/.vscode/mklink.md @@ -7,8 +7,9 @@ updated: "2023-10-20T03:57:15.006Z" # mklink ```bash +mklink /J "D:\1-Images-A\Images-4083e56a-Results\A2)People\4083e56a\{}\!" "D:\1-Images-A\Images-4083e56a-Results\E)Distance\4083e56a\{}\!" ``` ```bash -mklink /J "D:\1-Images-A\Images-4083e56a-Results\A2)People\4083e56a\{}\!" "D:\1-Images-A\Images-4083e56a-Results\E)Distance\4083e56a\{}\!" +mklink /J "L:\Git\View-by-Distance-MKLink-Console\.Immich" "D:\1-Images-A\Images-c9dbce3b-Results\F)Immich\c9dbce3b\{}" ``` diff --git a/.vscode/settings.json b/.vscode/settings.json index 84ebc62..0025030 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,7 @@ "Hasher", "Hmmss", "Hmmssfff", + "Immich", "jfif", "JOSN", "mmod", @@ -39,6 +40,7 @@ "Subfile", "Subfiles", "Syncthing", + "Thumbhash", "Unmanaged", "Upsample", "Vericruz" diff --git a/Distance/Models/_E_Distance.cs b/Distance/Models/_E_Distance.cs index 19e8207..e85b0a1 100644 --- a/Distance/Models/_E_Distance.cs +++ b/Distance/Models/_E_Distance.cs @@ -341,8 +341,9 @@ public partial class E_Distance : IDistance continue; if (!configuration.ReMap && face.Mapping.MappingFromPerson is not null) continue; - if (!configuration.ReMap && face.FaceEncoding is not null && face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding _) - throw new NotSupportedException($"{face.Mapping.MappingFromPerson} should not be null!"); + if (!configuration.ReMap && face.FaceEncoding is not null && face.FaceDistance?.Encoding is not null && face.FaceDistance.Encoding is FaceRecognitionDotNet.FaceEncoding) + // throw new NotSupportedException($"{face.FaceEncoding} should not be null!"); + continue; faces.Add(face); } int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); diff --git a/Drag-Drop-Explorer/DragDropExplorer.cs b/Drag-Drop-Explorer/DragDropExplorer.cs index 7fcb7f2..6f73869 100644 --- a/Drag-Drop-Explorer/DragDropExplorer.cs +++ b/Drag-Drop-Explorer/DragDropExplorer.cs @@ -59,7 +59,7 @@ public partial class DragDropExplorer : Form Controls.Add(_FirstTextBox); } - void Form1_Load(object? sender, EventArgs e) + private void Form1_Load(object? sender, EventArgs e) { try { @@ -81,7 +81,7 @@ public partial class DragDropExplorer : Form return result; } - void TextBox_LostFocus(object? sender, EventArgs e) + private void TextBox_LostFocus(object? sender, EventArgs e) { try { @@ -99,7 +99,7 @@ public partial class DragDropExplorer : Form } } - void Form1_DragEnter(object? sender, DragEventArgs e) + private void Form1_DragEnter(object? sender, DragEventArgs e) { try { @@ -112,7 +112,7 @@ public partial class DragDropExplorer : Form } } - void Form1_DragDrop(object? sender, DragEventArgs e) + private void Form1_DragDrop(object? sender, DragEventArgs e) { try { diff --git a/Drag-Drop-Explorer/Program.cs b/Drag-Drop-Explorer/Program.cs index e4112ad..a2fd7fc 100644 --- a/Drag-Drop-Explorer/Program.cs +++ b/Drag-Drop-Explorer/Program.cs @@ -6,7 +6,7 @@ public class Program /// The main entry point for the application. /// [STAThread] - static void Main() + private static void Main() { ApplicationConfiguration.Initialize(); Application.Run(new DragDropExplorer()); diff --git a/Drag-Drop-Move/Program.cs b/Drag-Drop-Move/Program.cs index 36aadf2..8506512 100644 --- a/Drag-Drop-Move/Program.cs +++ b/Drag-Drop-Move/Program.cs @@ -6,7 +6,7 @@ public class Program /// The main entry point for the application. /// [STAThread] - static void Main() + private static void Main() { ApplicationConfiguration.Initialize(); Application.Run(new DragDropMove()); diff --git a/Drag-Drop-Search/DragDropSearch.cs b/Drag-Drop-Search/DragDropSearch.cs index 4c386d9..8bfd8d4 100644 --- a/Drag-Drop-Search/DragDropSearch.cs +++ b/Drag-Drop-Search/DragDropSearch.cs @@ -64,7 +64,7 @@ public partial class DragDropSearch : Form Controls.Add(_TextBox); } - void Form1_Load(object? sender, EventArgs e) + private void Form1_Load(object? sender, EventArgs e) { try { @@ -79,7 +79,7 @@ public partial class DragDropSearch : Form } } - void TextBox_LostFocus(object? sender, EventArgs e) + private void TextBox_LostFocus(object? sender, EventArgs e) { try { @@ -92,7 +92,7 @@ public partial class DragDropSearch : Form } } - void Form1_DragEnter(object? sender, DragEventArgs e) + private void Form1_DragEnter(object? sender, DragEventArgs e) { try { @@ -105,7 +105,7 @@ public partial class DragDropSearch : Form } } - void LoadData() + private void LoadData() { Container[] containers; string aPropertySingletonDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(_Configuration.PropertyConfiguration, nameof(A_Property), "{}"); @@ -168,7 +168,7 @@ public partial class DragDropSearch : Form } } - void Form1_DragDrop(object? sender, DragEventArgs e) + private void Form1_DragDrop(object? sender, DragEventArgs e) { try { diff --git a/Drag-Drop-Search/Program.cs b/Drag-Drop-Search/Program.cs index 4f0b6ed..9475ece 100644 --- a/Drag-Drop-Search/Program.cs +++ b/Drag-Drop-Search/Program.cs @@ -8,7 +8,7 @@ public class Program /// The main entry point for the application. /// [STAThread] - static void Main() + private static void Main() { ApplicationConfiguration.Initialize(); Application.Run(new DragDropSearch()); diff --git a/Drag-Drop-Set-Property-Item/Program.cs b/Drag-Drop-Set-Property-Item/Program.cs index c3ac35b..fac5c2e 100644 --- a/Drag-Drop-Set-Property-Item/Program.cs +++ b/Drag-Drop-Set-Property-Item/Program.cs @@ -6,7 +6,7 @@ public class Program /// The main entry point for the application. /// [STAThread] - static void Main() + private static void Main() { ApplicationConfiguration.Initialize(); Application.Run(new DragDropSetPropertyItem()); diff --git a/FaceRecognitionDotNet/DisposableObject.cs b/FaceRecognitionDotNet/DisposableObject.cs index ac2a3c0..9a37cbc 100644 --- a/FaceRecognitionDotNet/DisposableObject.cs +++ b/FaceRecognitionDotNet/DisposableObject.cs @@ -26,16 +26,15 @@ public abstract class DisposableObject : IDisposable /// /// If this object is disposed, then is thrown. /// - public void ThrowIfDisposed() - { - if (IsDisposed) - throw new ObjectDisposedException(GetType().FullName); - } + public void ThrowIfDisposed() => + ObjectDisposedException.ThrowIf(IsDisposed, this); internal void ThrowIfDisposed(string objectName) { +#pragma warning disable CA1513 if (IsDisposed) throw new ObjectDisposedException(objectName); +#pragma warning restore CA1513 } #region Overrides diff --git a/FaceRecognitionDotNet/FaceRecognition.cs b/FaceRecognitionDotNet/FaceRecognition.cs index 44c5c2c..38dee50 100644 --- a/FaceRecognitionDotNet/FaceRecognition.cs +++ b/FaceRecognitionDotNet/FaceRecognition.cs @@ -167,7 +167,13 @@ public class FaceRecognition : DisposableObject } else { - ShapePredictor posePredictor = _PredictorModel switch { PredictorModel.Large => _PosePredictor68Point, PredictorModel.Small => _PosePredictor5Point, _ => throw new Exception() }; + ShapePredictor posePredictor = _PredictorModel switch + { + PredictorModel.Large => _PosePredictor68Point, + PredictorModel.Small => _PosePredictor5Point, + PredictorModel.Custom => throw new NotImplementedException(), + _ => throw new Exception() + }; foreach (Location location in locations) { DlibDotNet.Rectangle rectangle = new(location.Left, location.Top, location.Right, location.Bottom); @@ -422,8 +428,10 @@ public class FaceRecognition : DisposableObject faceEncodingToCompare.ThrowIfDisposed(); foreach (FaceDistance faceDistance in faceDistances) { +#pragma warning disable CA1513 if (faceDistance.Encoding is not FaceEncoding faceEncoding || faceEncoding.IsDisposed) throw new ObjectDisposedException($"{nameof(faceDistances)} contains disposed object."); +#pragma warning restore CA1513 using (Matrix diff = faceEncoding.Encoding - faceEncodingToCompare.Encoding) length = DlibDotNet.Dlib.Length(diff); result = new(faceDistance, length); diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 70f488d..b3761a8 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -263,7 +263,7 @@ public partial class DlibDotNet return new(results); } - private static void DeleteContinueFiles(Property.Models.Configuration propertyConfiguration, ReadOnlyCollection personContainers) + private static void DeleteContinueFiles(ReadOnlyCollection personContainers) { foreach (PersonContainer personContainer in personContainers) { @@ -373,7 +373,7 @@ public partial class DlibDotNet ReadOnlyCollection readOnlyContainers = new(containers); SaveDistinctIds(_Configuration.PropertyConfiguration, bResultsFullGroupDirectory, readOnlyContainers); mapLogic ??= new(_AppSettings.MaxDegreeOfParallelism, _Configuration.PropertyConfiguration, _MapConfiguration, _Distance, personContainers, ticks, a2PeopleContentDirectory, a2PeopleSingletonDirectory, eDistanceContentDirectory); - DeleteContinueFiles(_Configuration.PropertyConfiguration, personContainers); + DeleteContinueFiles(personContainers); FullDoWork(argZero, propertyRoot, ticks, aResultsFullGroupDirectory, bResultsFullGroupDirectory, fPhotoPrismSingletonDirectory, t, readOnlyContainers, propertyLogic, mapLogic); ReadOnlyCollection distinctValidImageItems = Shared.Models.Stateless.Methods.IContainer.GetValidImageItems(_Configuration.PropertyConfiguration, readOnlyContainers, distinctItems: true, filterItems: true); if (_Configuration.LookForAbandoned) @@ -414,7 +414,7 @@ public partial class DlibDotNet && _Exceptions.Count == 0) MapLogic(ticks, readOnlyContainers, fPhotoPrismContentDirectory, mapLogic, outputResolution, new(personKeyToIds), distinctValidImageFaces, distinctValidImageMappingCollection); if (runToDoCollectionFirst && _Configuration.SaveRandomForOutputResolutions.Contains(outputResolution) && personKeyToIds.Count > 0 && distinctValidImageMappingCollection.Count > 0) - _Random.Random(_Configuration.PropertyConfiguration, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, notNineCollection, distinctValidImageMappingCollection); + _Random.Random(_Configuration.PropertyConfiguration, _Configuration.ImmichAssetsFile, _Configuration.RadomUseBirthdayMinimum, _Configuration.ValidKeyWordsToIgnoreInRandom, personKeyToIds, notNineCollection, distinctValidImageMappingCollection); if (_IsEnvironment.Development) continue; if (!_IsEnvironment.Development) diff --git a/Instance/Models/Binder/Configuration.cs b/Instance/Models/Binder/Configuration.cs index a0699f1..24bdf44 100644 --- a/Instance/Models/Binder/Configuration.cs +++ b/Instance/Models/Binder/Configuration.cs @@ -27,6 +27,7 @@ public class Configuration public bool? ForceMetadataLastWriteTimeToCreationTime { get; set; } public bool? ForceResizeLastWriteTimeToCreationTime { get; set; } public string? GenealogicalDataCommunicationFile { get; set; } + public string? ImmichAssetsFile { get; set; } public string[]? IgnoreExtensions { get; set; } public string[]? JLinks { get; set; } public string? LinkedAlpha { get; set; } @@ -145,6 +146,7 @@ public class Configuration if (configuration?.ForceMetadataLastWriteTimeToCreationTime is null) throw new NullReferenceException(nameof(configuration.ForceMetadataLastWriteTimeToCreationTime)); if (configuration?.ForceResizeLastWriteTimeToCreationTime is null) throw new NullReferenceException(nameof(configuration.ForceResizeLastWriteTimeToCreationTime)); if (configuration?.GenealogicalDataCommunicationFile is null) throw new NullReferenceException(nameof(configuration.GenealogicalDataCommunicationFile)); + if (configuration?.ImmichAssetsFile is null) throw new NullReferenceException(nameof(configuration.ImmichAssetsFile)); // if (configuration?.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions)); // if (configuration?.JLinks is null) throw new NullReferenceException(nameof(configuration.JLinks)); // if (configuration?.LinkedAlpha is null) throw new NullReferenceException(nameof(configuration.LinkedAlpha)); @@ -235,6 +237,7 @@ public class Configuration configuration.ForceMetadataLastWriteTimeToCreationTime.Value, configuration.ForceResizeLastWriteTimeToCreationTime.Value, configuration.GenealogicalDataCommunicationFile, + configuration.ImmichAssetsFile, configuration.IgnoreExtensions ?? [], configuration.JLinks ?? [], configuration.LinkedAlpha, diff --git a/Instance/Models/Configuration.cs b/Instance/Models/Configuration.cs index bf8397f..6ada4af 100644 --- a/Instance/Models/Configuration.cs +++ b/Instance/Models/Configuration.cs @@ -21,6 +21,7 @@ public record Configuration(Property.Models.Configuration PropertyConfiguration, bool ForceMetadataLastWriteTimeToCreationTime, bool ForceResizeLastWriteTimeToCreationTime, string GenealogicalDataCommunicationFile, + string ImmichAssetsFile, string[] IgnoreExtensions, string[] JLinks, string? LinkedAlpha, diff --git a/Instance/Models/_F_Random.cs b/Instance/Models/_F_Random.cs index 4339675..3e49789 100644 --- a/Instance/Models/_F_Random.cs +++ b/Instance/Models/_F_Random.cs @@ -1,5 +1,6 @@ using System.Collections.ObjectModel; using System.Text.Json; +using View_by_Distance.Shared.Models; namespace View_by_Distance.Instance.Models; @@ -24,14 +25,16 @@ internal class F_Random return result; } - private static ReadOnlyDictionary> GetDayToRelativePaths(ReadOnlyCollection distinctValidImageMappingCollection, string dateFormat, ReadOnlyDictionary> idToPersonKeys) + private static ReadOnlyDictionary> GetDayToRelativePaths(ReadOnlyCollection distinctValidImageMappingCollection, string dateFormat, Dictionary immichAssets, ReadOnlyDictionary> idToPersonKeys) { Dictionary> results = []; string key; DateTime dateTime; List? personKeys; + ImmichAsset? immichAsset; List? relativePaths; - foreach (Shared.Models.Mapping mapping in distinctValidImageMappingCollection) + bool immichAssetsCountIsZero = immichAssets.Count == 0; + foreach (Mapping mapping in distinctValidImageMappingCollection) { if (mapping.MappingFromItem.FilePath.DirectoryName is null || mapping.MappingFromPerson is null) continue; @@ -49,29 +52,55 @@ internal class F_Random if (!results.TryGetValue(key, out relativePaths)) throw new Exception(); } - relativePaths.Add(mapping.MappingFromItem.RelativePath); + if (immichAssetsCountIsZero) + relativePaths.Add(mapping.MappingFromItem.RelativePath); + else + { + if (!immichAssets.TryGetValue(mapping.MappingFromItem.RelativePath, out immichAsset)) + continue; + relativePaths.Add(immichAsset.PreviewPath); + } } return new(results); } - internal void Random(Property.Models.Configuration configuration, int radomUseBirthdayMinimum, string[] validKeyWordsToIgnoreInRandom, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection? notNineCollection, ReadOnlyCollection distinctValidImageMappingCollection) + private static Dictionary GetImmichAssets(string immichAssetsFile) + { + Dictionary results = []; + if (!string.IsNullOrEmpty(immichAssetsFile) && File.Exists(immichAssetsFile)) + { + string json = File.ReadAllText(immichAssetsFile); + ImmichAsset[]? immichAssets = JsonSerializer.Deserialize(json, ImmichAssetCollectionSourceGenerationContext.Default.ImmichAssetArray); + if (immichAssets is not null) + { + foreach (ImmichAsset immichAsset in immichAssets) + results.Add(immichAsset.OriginalPath, immichAsset); + } + } + return results; + } + + internal void Random(Property.Models.Configuration configuration, string immichAssetsFile, int radomUseBirthdayMinimum, string[] validKeyWordsToIgnoreInRandom, ReadOnlyDictionary> personKeyToIds, ReadOnlyCollection? notNineCollection, ReadOnlyCollection distinctValidImageMappingCollection) { string key; string json; string jsonFile; Random random = new(); List? collection; + ImmichAsset? immichAsset; string dateFormat = "MM-dd"; List relativePaths = []; List distinctCollection = []; DateTime dateTime = new(2024, 1, 1); //Leap year + Dictionary immichAssets = GetImmichAssets(immichAssetsFile); ReadOnlyDictionary> idToPersonKeys = Map.Models.Stateless.Methods.IMapLogic.GetIdToPersonKeys(personKeyToIds); - ReadOnlyDictionary> dayToRelativePaths = GetDayToRelativePaths(distinctValidImageMappingCollection, dateFormat, idToPersonKeys); + ReadOnlyDictionary> dayToRelativePaths = GetDayToRelativePaths(distinctValidImageMappingCollection, dateFormat, immichAssets, idToPersonKeys); string fRandomCollectionDirectory = Property.Models.Stateless.IResult.GetResultsDateGroupDirectory(configuration, nameof(F_Random), "[]"); string[] files = Directory.GetFiles(fRandomCollectionDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) File.Delete(file); - foreach (Shared.Models.Mapping mapping in distinctValidImageMappingCollection) + bool immichAssetsCountIsZero = immichAssets.Count == 0; + foreach (Mapping mapping in distinctValidImageMappingCollection) { if (distinctCollection.Contains(mapping.MappingFromItem.Id)) continue; @@ -81,7 +110,14 @@ internal class F_Random continue; if (mapping.MappingFromItem.Keywords is not null && mapping.MappingFromItem.Keywords.Any(l => validKeyWordsToIgnoreInRandom.Contains(l))) continue; - relativePaths.Add(mapping.MappingFromItem.RelativePath); + if (immichAssetsCountIsZero) + relativePaths.Add(mapping.MappingFromItem.RelativePath); + else + { + if (!immichAssets.TryGetValue(mapping.MappingFromItem.RelativePath, out immichAsset)) + continue; + relativePaths.Add(immichAsset.PreviewPath); + } distinctCollection.Add(mapping.MappingFromItem.Id); } if (relativePaths.Count > 0) diff --git a/Map/Models/Stateless/RelationLogic.cs b/Map/Models/Stateless/RelationLogic.cs index 4ddb0d9..b9cdd57 100644 --- a/Map/Models/Stateless/RelationLogic.cs +++ b/Map/Models/Stateless/RelationLogic.cs @@ -254,7 +254,7 @@ internal abstract class RelationLogic _ = Directory.CreateDirectory(vsCodeDirectory); if (displayDirectoryName is not null) File.WriteAllText(Path.Combine(directory, $"_ {displayDirectoryName}.txt"), string.Empty); - json = "{ \"[markdown]\": { \"editor.wordWrap\": \"off\" }, \"foam.links.hover.enable\": false, \"foam.graph.style\": { \"background\": \"#202020\", \"node\": { \"note\": \"#f2cb1d\", \"distance\": \"green\", \"image\": \"orange\", \"placeholder\": \"white\", } } }"; + json = /*lang=json*/ """{ "[markdown]": { "editor.wordWrap": "off" }, "foam.links.hover.enable": false, "foam.graph.style": { "background": "#202020", "node": { "note": "#f2cb1d", "distance": "green", "image": "orange", "placeholder": "white", } } }"""; _ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "settings.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); json = string.Concat("{ \"version\": \"2.0.0\", \"tasks\": [ { \"label\": \"MKLink\", \"type\": \"shell\", \"command\": \"New-Item\", \"args\": [ \"-ItemType\", \"Junction\", \"-Path\", \"'", directory.Replace('\\', '/'), "/()'\", \"-Target\", \"'", eDistanceContentDirectory.Replace('\\', '/'), "'\" ], \"problemMatcher\": [] } ] }"); _ = IPath.WriteAllText(Path.Combine(vsCodeDirectory, "tasks.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); diff --git a/Resize/Models/_C_Resize.cs b/Resize/Models/_C_Resize.cs index 34594f0..a5f879b 100644 --- a/Resize/Models/_C_Resize.cs +++ b/Resize/Models/_C_Resize.cs @@ -312,10 +312,12 @@ public class C_Resize { if (mappingFromItem.ResizedFileHolder is null) throw new NullReferenceException(nameof(mappingFromItem.ResizedFileHolder)); +#pragma warning disable CA1854 if (!outputResolutionToResize.ContainsKey(_Original)) throw new Exception(); if (!outputResolutionToResize.ContainsKey(outputResolution)) throw new Exception(); +#pragma warning restore CA1854 FileInfo fileInfo = new(mappingFromItem.ResizedFileHolder.FullName); bool check = false; int[] resize = outputResolutionToResize[outputResolution]; diff --git a/Shared/Models/ImmichAsset.cs b/Shared/Models/ImmichAsset.cs new file mode 100644 index 0000000..f652c75 --- /dev/null +++ b/Shared/Models/ImmichAsset.cs @@ -0,0 +1,33 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace View_by_Distance.Shared.Models; + +public record ImmichAsset([property: JsonPropertyName("id")] string Id, + [property: JsonPropertyName("deviceAssetId")] string DeviceAssetId, + [property: JsonPropertyName("originalPath")] string OriginalPath, + [property: JsonPropertyName("previewPath")] string PreviewPath, + [property: JsonPropertyName("isFavorite")] bool IsFavorite, + [property: JsonPropertyName("thumbnailPath")] string ThumbnailPath, + [property: JsonPropertyName("thumbhash")] string Thumbhash) +{ + + public override string ToString() + { + string result = JsonSerializer.Serialize(this, ImmichAssetSourceGenerationContext.Default.ImmichAsset); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] +[JsonSerializable(typeof(ImmichAsset))] +public partial class ImmichAssetSourceGenerationContext : JsonSerializerContext +{ +} + +[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] +[JsonSerializable(typeof(ImmichAsset[]))] +public partial class ImmichAssetCollectionSourceGenerationContext : JsonSerializerContext +{ +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ImageHelper.cs b/Shared/Models/Stateless/Methods/ImageHelper.cs index 352248f..e4493f2 100644 --- a/Shared/Models/Stateless/Methods/ImageHelper.cs +++ b/Shared/Models/Stateless/Methods/ImageHelper.cs @@ -81,6 +81,7 @@ internal abstract class ImageHelper internal static Size GetDimensions(BinaryReader binaryReader, int? faceRight, int? faceBottom) { Size? result = null; +#pragma warning disable IDE0230 Dictionary> _ImageFormatDecoders = new() { { new byte[] { 0x42, 0x4D }, DecodeBitmap }, @@ -89,6 +90,7 @@ internal abstract class ImageHelper { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, { new byte[] { 0xff, 0xd8 }, DecodeJfif }, }; +#pragma warning restore IDE0230 int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; byte[] magicBytes = new byte[maxMagicBytesLength]; for (int i = 0; i < maxMagicBytesLength; i += 1) diff --git a/Shared/Phares/Shared/RijndaelEncryption.cs b/Shared/Phares/Shared/RijndaelEncryption.cs index a8b1ee7..0a000c7 100644 --- a/Shared/Phares/Shared/RijndaelEncryption.cs +++ b/Shared/Phares/Shared/RijndaelEncryption.cs @@ -9,25 +9,28 @@ namespace Phares.Shared; public static class RijndaelEncryption { + /// - /// Change the Inputkey GUID when you use this code in your own program. - /// Keep this inputkey very safe and prevent someone from decoding it some way!! + /// Change the input key GUID when you use this code in your own program. + /// Keep this input key very safe and prevent someone from decoding it some way!! /// Generated 2021-08-10 /// - internal const string _Inputkey = "970CCEF6-4307-4F6A-9AC8-377DADB889BD"; + internal const string _InputKey = "970CCEF6-4307-4F6A-9AC8-377DADB889BD"; /// /// Encrypt the given text and give the byte array back as a BASE64 string /// /// The text to encrypt - /// The pasword salt + /// The password salt /// The encrypted text public static string Encrypt(string text, string salt) { string result; if (string.IsNullOrEmpty(text)) - throw new NullReferenceException(nameof(text)); + throw new ArgumentNullException(nameof(text)); +#pragma warning disable RijndaelManaged aesAlg = NewRijndaelManaged(salt); +#pragma warning restore ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); MemoryStream msEncrypt = new(); using (CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write)) @@ -46,7 +49,9 @@ public static class RijndaelEncryption { bool result; base64String = base64String.Trim(); +#pragma warning disable result = (base64String.Length % 4 == 0) && Regex.IsMatch(base64String, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); +#pragma warning restore return result; } @@ -54,16 +59,18 @@ public static class RijndaelEncryption /// Decrypts the given text /// /// The encrypted BASE64 text - /// The pasword salt + /// The password salt /// De gedecrypte text public static string Decrypt(string cipherText, string salt) { if (string.IsNullOrEmpty(cipherText)) - throw new NullReferenceException(nameof(cipherText)); + throw new ArgumentNullException(nameof(cipherText)); if (!IsBase64String(cipherText)) throw new Exception("The cipherText input parameter is not base64 encoded"); string text; +#pragma warning disable RijndaelManaged aesAlg = NewRijndaelManaged(salt); +#pragma warning restore ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); byte[] cipher = Convert.FromBase64String(cipherText); using (MemoryStream msDecrypt = new(cipher)) @@ -76,19 +83,22 @@ public static class RijndaelEncryption } /// - /// GetPersonName a new RijndaelManaged class and initialize it + /// Create a new RijndaelManaged class and initialize it /// - /// The pasword salt + /// The password salt /// +#pragma warning disable private static RijndaelManaged NewRijndaelManaged(string salt) { if (salt == null) - throw new NullReferenceException(nameof(salt)); + throw new ArgumentNullException(nameof(salt)); byte[] saltBytes = Encoding.ASCII.GetBytes(salt); - Rfc2898DeriveBytes key = new(_Inputkey, saltBytes); + Rfc2898DeriveBytes key = new(_InputKey, saltBytes); RijndaelManaged aesAlg = new(); +#pragma warning restore aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8); return aesAlg; } + } \ No newline at end of file diff --git a/Tests/Models/Binder/Configuration.cs b/Tests/Models/Binder/Configuration.cs index b1e144e..e003aa3 100644 --- a/Tests/Models/Binder/Configuration.cs +++ b/Tests/Models/Binder/Configuration.cs @@ -27,6 +27,7 @@ public class Configuration public bool? ForceMetadataLastWriteTimeToCreationTime { get; set; } public bool? ForceResizeLastWriteTimeToCreationTime { get; set; } public string? GenealogicalDataCommunicationFile { get; set; } + public string? ImmichAssetsFile { get; set; } public string[]? IgnoreExtensions { get; set; } public string[]? JLinks { get; set; } public string[]? LoadOrCreateThenSaveDistanceResultsForOutputResolutions { get; set; } @@ -140,6 +141,7 @@ public class Configuration if (configuration?.ForceMetadataLastWriteTimeToCreationTime is null) throw new NullReferenceException(nameof(configuration.ForceMetadataLastWriteTimeToCreationTime)); if (configuration?.ForceResizeLastWriteTimeToCreationTime is null) throw new NullReferenceException(nameof(configuration.ForceResizeLastWriteTimeToCreationTime)); if (configuration?.GenealogicalDataCommunicationFile is null) throw new NullReferenceException(nameof(configuration.GenealogicalDataCommunicationFile)); + if (configuration?.ImmichAssetsFile is null) throw new NullReferenceException(nameof(configuration.ImmichAssetsFile)); // if (configuration?.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions)); // if (configuration?.JLinks is null) throw new NullReferenceException(nameof(configuration.JLinks)); // if (configuration?.LoadOrCreateThenSaveDistanceResultsForOutputResolutions is null) throw new NullReferenceException(nameof(configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions)); @@ -223,6 +225,7 @@ public class Configuration configuration.ForceMetadataLastWriteTimeToCreationTime.Value, configuration.ForceResizeLastWriteTimeToCreationTime.Value, configuration.GenealogicalDataCommunicationFile, + configuration.ImmichAssetsFile, configuration.IgnoreExtensions ?? [], configuration.JLinks ?? [], configuration.LoadOrCreateThenSaveDistanceResultsForOutputResolutions ?? [], diff --git a/Tests/Models/Configuration.cs b/Tests/Models/Configuration.cs index 74b7e4b..201381b 100644 --- a/Tests/Models/Configuration.cs +++ b/Tests/Models/Configuration.cs @@ -21,6 +21,7 @@ public record Configuration(Property.Models.Configuration PropertyConfiguration, bool ForceMetadataLastWriteTimeToCreationTime, bool ForceResizeLastWriteTimeToCreationTime, string GenealogicalDataCommunicationFile, + string ImmichAssetsFile, string[] IgnoreExtensions, string[] JLinks, string[] LoadOrCreateThenSaveDistanceResultsForOutputResolutions, diff --git a/Tests/UnitTestHardCoded.cs b/Tests/UnitTestHardCoded.cs index c079b67..7bbdee0 100644 --- a/Tests/UnitTestHardCoded.cs +++ b/Tests/UnitTestHardCoded.cs @@ -4,6 +4,7 @@ using Phares.Shared; using System.Diagnostics; using System.Globalization; using System.Reflection; +using System.Text.Json; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Stateless.Methods; using View_by_Distance.Tests.Models; @@ -14,6 +15,7 @@ namespace View_by_Distance.Tests; public partial class UnitTestHardCoded { + private readonly string _Git; private readonly AppSettings _AppSettings; private readonly string _WorkingDirectory; private readonly Configuration _Configuration; @@ -43,6 +45,7 @@ public partial class UnitTestHardCoded Environment.SetEnvironmentVariable(nameof(workingDirectory), workingDirectory); propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); + _Git = "c9dbce3b"; _AppSettings = appSettings; _Configuration = configuration; _IsEnvironment = isEnvironment; @@ -86,7 +89,7 @@ public partial class UnitTestHardCoded [TestMethod] public void TestMethodGetApproximateYears() { - string personDisplayDirectory = "D:/1-Images-A/Images-7007a9df-Results/A2)People/7007a9df/{}/^/Sydney Dupray^9"; + string personDisplayDirectory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/^/Sydney Dupray^9"); if (Directory.Exists(Directory.GetDirectoryRoot(personDisplayDirectory)) && Directory.Exists(personDisplayDirectory)) { char numberSign = '#'; @@ -189,7 +192,7 @@ public partial class UnitTestHardCoded [TestMethod] public void TestMethodRenameAbandoned() { - string directory = "D:/1-Images-A/Images-7007a9df-Results/A2)People/7007a9df/{}/!/Abandoned"; + string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/!/Abandoned"); if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { string checkFile; @@ -209,7 +212,7 @@ public partial class UnitTestHardCoded [TestMethod] public void TestMethodRenameDelete() { - string directory = "D:/1-Images-A/Images-7007a9df-Results/A)Property/7007a9df/{}"; + string directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A)Property/{_Git}", "/{}"); if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { string checkFile; @@ -229,7 +232,7 @@ public partial class UnitTestHardCoded [TestMethod] public void TestMethodRenameOld() { - string directory = "D:/1-Images-A/Images-7007a9df-Results/E)Distance/7007a9df/()"; + string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/()"; if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { string checkFile; @@ -250,7 +253,7 @@ public partial class UnitTestHardCoded public void TestMethodRenameDup() { string directory; - directory = "D:/1-Images-A/Images-7007a9df-Results/E)Distance/7007a9df/()"; + directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/()"; if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { string checkFile; @@ -264,7 +267,7 @@ public partial class UnitTestHardCoded } Assert.IsTrue(true); } - directory = "D:/1-Images-A/Images-7007a9df-Results/A2)People/7007a9df/{}/!"; + directory = string.Concat($"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}", "/{}/!"); if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { string checkFile; @@ -284,9 +287,9 @@ public partial class UnitTestHardCoded [TestMethod] public void TestMethodRename() { - // string directory = "D:/2-Images-B/Not-Copy-Copy-7007a9df"; - string directory = "D:/1-Images-A/Images-7007a9df"; - // string directory = "D:/2-Images-B/Not-Copy-Copy-7007a9df"; + // string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}"; + string directory = $"D:/1-Images-A/Images-{_Git}"; + // string $directory = "D:/2-Images-B/Not-Copy-Copy-{_Git}"; if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { string[] directories = Directory.GetDirectories(directory, "*;*", SearchOption.AllDirectories); @@ -303,7 +306,7 @@ public partial class UnitTestHardCoded [TestMethod] public void TestMethodRenameForUnknown() { - string directory = "D:/1-Images-A/Images-7007a9df-Results/E)Distance/7007a9df/(RectInt-2023-06-19-less-0.99)"; + string directory = $"D:/1-Images-A/Images-{_Git}-Results/E)Distance/{_Git}/(RectInt-2023-06-19-less-0.99)"; if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { string[] files = Directory.GetFiles(directory, "*.unk", SearchOption.AllDirectories); @@ -316,13 +319,13 @@ public partial class UnitTestHardCoded [TestMethod] public void TestMethodRenameForTicks() { - string directory = "D:/1-Images-A/Images-7007a9df-Results/A2)People/7007a9df/([])/ged"; + string directory = $"D:/1-Images-A/Images-{_Git}-Results/A2)People/{_Git}/([])/ged"; if (Directory.Exists(Path.GetPathRoot(directory)) && Directory.Exists(directory)) { string checkName; DateTime dateTime; string weekOfYear; - string checkDirectoy; + string checkDirectory; Calendar calendar = new CultureInfo("en-US").Calendar; string[] files = Directory.GetFiles(directory, "*.ged", SearchOption.TopDirectoryOnly); foreach (string file in files) @@ -331,10 +334,10 @@ public partial class UnitTestHardCoded continue; dateTime = new(ticks); weekOfYear = calendar.GetWeekOfYear(dateTime, CalendarWeekRule.FirstDay, DayOfWeek.Sunday).ToString("00"); - checkDirectoy = Path.Combine(directory, dateTime.Year.ToString(), $"{dateTime.Year}-Week-{weekOfYear}"); - checkName = Path.Combine(checkDirectoy, Path.GetFileName(file)); - if (!Directory.Exists(checkDirectoy)) - _ = Directory.CreateDirectory(checkDirectoy); + checkDirectory = Path.Combine(directory, dateTime.Year.ToString(), $"{dateTime.Year}-Week-{weekOfYear}"); + checkName = Path.Combine(checkDirectory, Path.GetFileName(file)); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); if (File.Exists(checkName)) continue; File.Move(file, checkName); @@ -343,4 +346,22 @@ public partial class UnitTestHardCoded NonThrowTryCatch(); } + [TestMethod] + public void TestMethodImmichAsset() + { + if (!string.IsNullOrEmpty(_Configuration.ImmichAssetsFile) && File.Exists(_Configuration.ImmichAssetsFile)) + { + Dictionary keyValuePairs = []; + string json = File.ReadAllText(_Configuration.ImmichAssetsFile); + ImmichAsset[]? immichAssets = JsonSerializer.Deserialize(json, ImmichAssetCollectionSourceGenerationContext.Default.ImmichAssetArray); + if (immichAssets is not null) + { + foreach (ImmichAsset immichAsset in immichAssets) + keyValuePairs.Add(immichAsset.OriginalPath, immichAsset); + } + Assert.IsTrue(keyValuePairs.Count > 0); + } + NonThrowTryCatch(); + } + } \ No newline at end of file diff --git a/ThumbHash/Models/ThumbHash.cs b/ThumbHash/Models/ThumbHash.cs index 371f789..98833a6 100644 --- a/ThumbHash/Models/ThumbHash.cs +++ b/ThumbHash/Models/ThumbHash.cs @@ -11,13 +11,13 @@ public static partial class ThumbHash private const int _MinHash = 5; [DoesNotReturn] - static void ThrowIfLessThan(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) => throw new ArgumentOutOfRangeException(paramName, value, $"'{value}' must be greater than or equal to '{other}'."); + private static void ThrowIfLessThan(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) => throw new ArgumentOutOfRangeException(paramName, value, $"'{value}' must be greater than or equal to '{other}'."); [DoesNotReturn] - static void ThrowIfGreaterThan(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) => throw new ArgumentOutOfRangeException(paramName, value, $"'{paramName}' must be less than or equal to '{other}'."); + private static void ThrowIfGreaterThan(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null) => throw new ArgumentOutOfRangeException(paramName, value, $"'{paramName}' must be less than or equal to '{other}'."); [DoesNotReturn] - static void ThrowNotEqual(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null, [CallerArgumentExpression(nameof(other))] string? otherName = null) => throw new ArgumentOutOfRangeException(paramName, value, $"'{paramName}' must be equal to '{other}' ('{otherName}')."); + private static void ThrowNotEqual(T value, T other, [CallerArgumentExpression(nameof(value))] string? paramName = null, [CallerArgumentExpression(nameof(other))] string? otherName = null) => throw new ArgumentOutOfRangeException(paramName, value, $"'{paramName}' must be equal to '{other}' ('{otherName}')."); /// /// Encodes an RGBA image to a ThumbHash.