From c4d42d79a017b0f6893d9ed7cc131232c3cbb155 Mon Sep 17 00:00:00 2001 From: "mikep@034E8FF1ED4D1F6" Date: Sun, 27 Jul 2025 16:03:47 -0700 Subject: [PATCH] DigiKam4 Archive Change to only need the one json file and changed AppSettings to not need a binder file Editorconfig for no new line before open braces Nuget package bumps Database lowest-version-history --- .editorconfig | 78 +---- .vscode/settings.json | 1 + requests/ImmichToSlideshow.http | 7 + .../Controllers/AssetsController.cs | 18 +- .../ServiceCollectionExtensions.cs | 6 +- src/ImmichToSlideshow/Domain/ImageTag.cs | 74 +++++ src/ImmichToSlideshow/Domain/Product.cs | 15 +- src/ImmichToSlideshow/Domain/User.cs | 9 +- .../ImmichToSlideshow.csproj | 5 +- src/ImmichToSlideshow/Models/AppSettings.cs | 73 +++-- .../Models/Binder/.editorconfig | 2 - .../Models/Binder/AppSettings.cs | 95 ------- .../Models/DigiKam/AlbumRoots.cs | 11 + .../Models/DigiKam/Albums.cs | 12 + .../Models/DigiKam/ImageComments.cs | 11 + .../Models/DigiKam/ImagePositions.cs | 15 + .../Models/DigiKam/ImageTagProperties.cs | 8 + .../Models/DigiKam/ImageTags.cs | 21 ++ .../Models/DigiKam/Images.cs | 28 ++ .../Models/DigiKam/TagProperties.cs | 7 + src/ImmichToSlideshow/Models/DigiKam/Tags.cs | 24 ++ src/ImmichToSlideshow/Models/DigiKam4.cs | 22 ++ src/ImmichToSlideshow/Models/Identifier.cs | 16 +- src/ImmichToSlideshow/Models/Immich/Asset.cs | 12 +- src/ImmichToSlideshow/Models/Immich/Tag.cs | 12 +- src/ImmichToSlideshow/Models/Settings.cs | 27 ++ .../Persistence/Database/DbConstants.cs | 3 +- src/ImmichToSlideshow/Program.cs | 8 +- .../WebApplicationExtensions.cs | 9 +- .../Services/AssetService.cs | 206 +++++++------- src/ImmichToSlideshow/Services/CommandText.cs | 267 ++++++++++-------- 31 files changed, 644 insertions(+), 458 deletions(-) create mode 100644 src/ImmichToSlideshow/Domain/ImageTag.cs delete mode 100644 src/ImmichToSlideshow/Models/Binder/.editorconfig delete mode 100644 src/ImmichToSlideshow/Models/Binder/AppSettings.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/AlbumRoots.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/Albums.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/ImageComments.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/ImagePositions.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/ImageTagProperties.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/ImageTags.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/Images.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/TagProperties.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam/Tags.cs create mode 100644 src/ImmichToSlideshow/Models/DigiKam4.cs create mode 100644 src/ImmichToSlideshow/Models/Settings.cs diff --git a/.editorconfig b/.editorconfig index 3eab4da..cd5d265 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,12 +21,12 @@ csharp_indent_case_contents = true csharp_indent_case_contents_when_block = true csharp_indent_labels = one_less_than_current csharp_indent_switch_labels = true -csharp_new_line_before_catch = true -csharp_new_line_before_else = true -csharp_new_line_before_finally = true +csharp_new_line_before_catch = false +csharp_new_line_before_else = false +csharp_new_line_before_finally = false csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_open_brace = all +csharp_new_line_before_open_brace = none csharp_new_line_between_query_expression_clauses = true csharp_prefer_braces = false csharp_prefer_qualified_reference = true:error @@ -106,101 +106,37 @@ dotnet_code_quality_unused_parameters = all dotnet_code_quality_unused_parameters = non_public dotnet_code_quality.CAXXXX.api_surface = private, internal dotnet_diagnostic.CA1001.severity = error # CA1001: Types that own disposable fields should be disposable -dotnet_diagnostic.CA1001.severity = none # Question - CA1001: Types that own disposable fields should be disposable dotnet_diagnostic.CA1051.severity = error # CA1051: Do not declare visible instance fields -dotnet_diagnostic.CA1051.severity = none # Question - CA1051: Do not declare visible instance fields -dotnet_diagnostic.CA1416.severity = none # Question - CA1416: This call site is reachable on all platforms. 'EventLogEntryType.Error' is only supported on: 'windows'. -dotnet_diagnostic.CA1510.severity = none # Question - CA1510: Use 'ArgumentNullException.ThrowIfNull' 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.CA1825.severity = warning # CA1825: Avoid zero-length array allocations -dotnet_diagnostic.CA1829.severity = error # CA1829: Use Length/Count property instead of Count() when available -dotnet_diagnostic.CA1829.severity = none # Question - CA1829: Use Length/Count property instead of Enumerable.Count method +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.CA1860.severity = error # CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance -dotnet_diagnostic.CA1860.severity = none # Question - CA1860: Avoid using 'Enumerable.Any()' extension method 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.CA1862.severity = none # Question - 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.CA1869.severity = none # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead. -dotnet_diagnostic.CA2200.severity = none # Question - CA2200: Re-throwing caught exception changes stack information dotnet_diagnostic.CA2201.severity = none # CA2201: Exception type System.NullReferenceException is reserved by the runtime -dotnet_diagnostic.CA2208.severity = none # Question - CA2208: Instantiate argument exceptions correctly -dotnet_diagnostic.CA2211.severity = none # Question - CA2211: Non-constant fields should not be visible -dotnet_diagnostic.CA2249.severity = none # Question - CA2249: Use 'string.Contains' instead of 'string.IndexOf' to improve readability -dotnet_diagnostic.CA2253.severity = none # Question - CA2253: Named placeholders should not be numeric values dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])' -dotnet_diagnostic.CS0103.severity = none # Question - CS0103: The name 'Functions' does not exist in the current context -dotnet_diagnostic.CS0168.severity = none # Question - CS0168: The variable 'ex' is declared but never used -dotnet_diagnostic.CS0219.severity = none # Question - CS0219: The variable 'result' is assigned but its value is never used -dotnet_diagnostic.CS0618.severity = none # Question - CS0618: Compiler Warning (level 2) -dotnet_diagnostic.CS0659.severity = none # Question - CS0659: Compiler Warning (level 3) -dotnet_diagnostic.CS8600.severity = none # Question - CS8600: Converting null literal or possible null value to non-nullable type -dotnet_diagnostic.CS8602.severity = none # Question - CS8602: Dereference of a possibly null reference. -dotnet_diagnostic.CS8603.severity = none # Question - CS8603: Possible null reference return -dotnet_diagnostic.CS8604.severity = none # Question - CS8604: Possible null reference argument for parameter. -dotnet_diagnostic.CS0612.severity = none # Question - CS0612: is obsolete -dotnet_diagnostic.CS8618.severity = none # Question - CS8618: Non-nullable variable must contain a non-null value when exiting constructor -dotnet_diagnostic.CS8625.severity = none # Question - CS8625: Cannot convert null literal to non-nullable reference type. -dotnet_diagnostic.CS8629.severity = none # Question - CS8629: Nullable value type may be null -dotnet_diagnostic.CS8765.severity = none # Question - CS8765: Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes) 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.IDE0004.severity = none # Question - IDE0004: Cast is redundant. -dotnet_diagnostic.IDE0003.severity = none # Question - IDE0003: this and Me preferences -dotnet_diagnostic.IDE0005.severity = error # Using directive is unnecessary -dotnet_diagnostic.IDE0005.severity = none # Question - IDE0005: Remove unnecessary using directives -dotnet_diagnostic.IDE0008.severity = none # Question - IDE0008: 'var' preferences +dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement (IDE0010) -dotnet_diagnostic.IDE0017.severity = none # Question - IDE0017: Use object initializers -dotnet_diagnostic.IDE0019.severity = none # Question - IDE0019: Use pattern matching to avoid 'as' followed by a 'null' check -dotnet_diagnostic.IDE0021.severity = none # Question - IDE0021: Use expression body for constructors -dotnet_diagnostic.IDE0022.severity = none # Question - IDE0022: Use expression body for methods -dotnet_diagnostic.IDE0025.severity = none # Question - IDE0025: Use expression body for properties -dotnet_diagnostic.IDE0027.severity = none # Question - IDE0027: Use expression body for accessor dotnet_diagnostic.IDE0028.severity = error # IDE0028: Collection initialization can be simplified -dotnet_diagnostic.IDE0028.severity = none # Question - IDE0028: Use collection initializers or expressions dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031) -dotnet_diagnostic.IDE0031.severity = none # Question - IDE0031: Use null propagation (IDE0031) -dotnet_diagnostic.IDE0032.severity = none # Question - IDE0032: Use auto-implemented property -dotnet_diagnostic.IDE0037.severity = none # Question - IDE0037: Member name can be simplified -dotnet_diagnostic.IDE0040.severity = none # Question - IDE0040: Add accessibility modifiers -dotnet_diagnostic.IDE0041.severity = none # Question - IDE0041: Use 'is null' check -dotnet_diagnostic.IDE0044.severity = none # Question - IDE0044: Add readonly modifier dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed -dotnet_diagnostic.IDE0047.severity = none # Question - IDE0047: Parentheses preferences 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.IDE0051.severity = none # Question - IDE0051: Remove unused private member -dotnet_diagnostic.IDE0053.severity = none # Question - IDE0053: Use expression body for lambdas -dotnet_diagnostic.IDE0054.severity = none # Question - IDE0054: Use compound assignment -dotnet_diagnostic.IDE0055.severity = none # Question - IDE0055: Formatting rule -dotnet_diagnostic.IDE0057.severity = none # Question - IDE0057: Substring can be simplified -dotnet_diagnostic.IDE0058.severity = error # IDE0058: Expression value is never used -dotnet_diagnostic.IDE0058.severity = none # Question - IDE0058: Remove unnecessary expression value -dotnet_diagnostic.IDE0059.severity = none # Question - IDE0059: Remove unnecessary value assignment +dotnet_diagnostic.IDE0058.severity = warning # IDE0058: Expression value is never used dotnet_diagnostic.IDE0060.severity = error # IDE0060: Remove unused parameter -dotnet_diagnostic.IDE0060.severity = none # Question - IDE0060: Remove unused parameter -dotnet_diagnostic.IDE0063.severity = none # Question - IDE0063: Use simple 'using' statement -dotnet_diagnostic.IDE0066.severity = none # Question - IDE0066: Use switch expression dotnet_diagnostic.IDE0074.severity = warning # IDE0074: Use compound assignment -dotnet_diagnostic.IDE0078.severity = none # Question - IDE0078: Use pattern matching -dotnet_diagnostic.IDE0100.severity = none # Question - IDE0100: Remove redundant equality -dotnet_diagnostic.IDE0090.severity = none # Question - IDE0090: Simplify new expression dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure (IDE0130) -dotnet_diagnostic.IDE0161.severity = none # Question - IDE0161: Namespace declaration preferences -dotnet_diagnostic.IDE0270.severity = none # Question - IDE0270: Null check can be simplified dotnet_diagnostic.IDE0270.severity = warning # IDE0270: Null check can be simplified dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290) dotnet_diagnostic.IDE0300.severity = error # IDE0300: Collection initialization can be simplified -dotnet_diagnostic.IDE0065.severity = none # Question - IDE0065: 'using' directive placement -dotnet_diagnostic.IDE0300.severity = none # Question - IDE0300: Collection initialization can be simplified dotnet_diagnostic.IDE0301.severity = error #IDE0301: Collection initialization can be simplified dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified -dotnet_diagnostic.IDE1006.severity = none # Question - IDE1006: Use collection expression for builder dotnet_style_prefer_collection_expression -dotnet_diagnostic.IDE2000.severity = error # IDE2000: Allow multiple blank lines -dotnet_diagnostic.IDE2000.severity = none # Question - IDE2000: Allow multiple blank lines 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/.vscode/settings.json b/.vscode/settings.json index 13b34df..5a34a0a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,6 +26,7 @@ "dbmdl", "dbproj", "DENITED", + "Digi", "dlldata", "docstates", "ebug", diff --git a/requests/ImmichToSlideshow.http b/requests/ImmichToSlideshow.http index 2116a99..8099e47 100644 --- a/requests/ImmichToSlideshow.http +++ b/requests/ImmichToSlideshow.http @@ -1,3 +1,5 @@ +@host = http://0.0.0.0:5001 + GET {{host}}/api/v1/assets/columns/ Accept: application/json @@ -33,6 +35,11 @@ Accept: application/json ### +GET {{host}}/api/v1/assets/{{ownerId}}/set-digi-kam-4-archive-immich/ +Accept: application/json + +### + GET {{host}}/api/v1/assets/{{ownerId}}/archived-tag/ Accept: application/json diff --git a/src/ImmichToSlideshow/Controllers/AssetsController.cs b/src/ImmichToSlideshow/Controllers/AssetsController.cs index 55d4899..568a977 100644 --- a/src/ImmichToSlideshow/Controllers/AssetsController.cs +++ b/src/ImmichToSlideshow/Controllers/AssetsController.cs @@ -1,27 +1,27 @@ using ImmichToSlideshow.Services; + using Microsoft.AspNetCore.Mvc; namespace ImmichToSlideshow.Controllers; [ApiController] [Route("api/v1/[controller]")] -public class AssetsController(AssetService assetService) : ControllerBase -{ +public class AssetsController(AssetService assetService) : ControllerBase { private readonly string _ContentType = "application/json"; private readonly AssetService _AssetService = assetService; [HttpGet("columns")] public IActionResult GetColumns() => - Content(_AssetService.GetColumns(), _ContentType); + Content(_AssetService.GetColumns() ?? string.Empty, _ContentType); [HttpGet("owner-ids")] public IActionResult GetOwnerIds() => - Content(_AssetService.GetOwnerIds(), _ContentType); + Content(_AssetService.GetOwnerIds() ?? string.Empty, _ContentType); [HttpGet("{ownerId:guid}")] public IActionResult Get(Guid ownerId) => - Content(_AssetService.GetAssets(ownerId), _ContentType); + Content(_AssetService.GetAssets(ownerId) ?? string.Empty, _ContentType); [HttpGet("{ownerId:guid}/random-paths")] public IActionResult GetRandomPaths(Guid ownerId) => @@ -29,11 +29,11 @@ public class AssetsController(AssetService assetService) : ControllerBase [HttpGet("{ownerId:guid}/archived-tag")] public IActionResult GetArchivedTag(Guid ownerId) => - Content(_AssetService.GetArchivedTag(ownerId), _ContentType); + Content(_AssetService.GetArchivedTag(ownerId) ?? string.Empty, _ContentType); [HttpGet("{ownerId:guid}/save-random-paths")] public IActionResult SaveRandomPaths(Guid ownerId) => - Content(_AssetService.SaveRandomPaths(ownerId), _ContentType); + Content(_AssetService.SaveRandomPaths(ownerId) ?? string.Empty, _ContentType); [HttpGet("{ownerId:guid}/sync-immich")] public IActionResult SyncImmich(Guid ownerId) => @@ -43,4 +43,8 @@ public class AssetsController(AssetService assetService) : ControllerBase public IActionResult SetArchiveImmich(Guid ownerId) => Ok(_AssetService.SetArchiveImmich(ownerId)); + [HttpGet("{ownerId:guid}/set-digi-kam-4-archive-immich")] + public IActionResult SetDigiKam4ArchiveImmich(Guid ownerId) => + Ok(_AssetService.SetDigiKam4ArchiveImmich(ownerId)); + } \ No newline at end of file diff --git a/src/ImmichToSlideshow/DependencyInjection/ServiceCollectionExtensions.cs b/src/ImmichToSlideshow/DependencyInjection/ServiceCollectionExtensions.cs index 72c73f4..89bcdb9 100644 --- a/src/ImmichToSlideshow/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/ImmichToSlideshow/DependencyInjection/ServiceCollectionExtensions.cs @@ -3,11 +3,9 @@ using ImmichToSlideshow.Services; namespace ImmichToSlideshow.DependencyInjection; -public static class ServiceCollectionExtensions -{ +public static class ServiceCollectionExtensions { - public static IServiceCollection AddServices(this IServiceCollection services, AppSettings appSettings) - { + public static IServiceCollection AddServices(this IServiceCollection services, AppSettings appSettings) { _ = services.AddScoped(); _ = services.AddSingleton(_ => appSettings); return services; diff --git a/src/ImmichToSlideshow/Domain/ImageTag.cs b/src/ImmichToSlideshow/Domain/ImageTag.cs new file mode 100644 index 0000000..21d1c44 --- /dev/null +++ b/src/ImmichToSlideshow/Domain/ImageTag.cs @@ -0,0 +1,74 @@ +using ImmichToSlideshow.Models.DigiKam; +using System.Collections.ObjectModel; + +namespace ImmichToSlideshow.Domain; + +// Business concerns + +public record ImageTag(long Id, + string Name, + string Tag) { + + internal static ReadOnlyCollection? Get(string tag, string? tagsPath, string? imageTagsPath, string? imagesPath) { + List? results; + ReadOnlyCollection? tags = Tags.GetTags(tagsPath); + if (tags is null || tags.Count == 0) { + results = null; + } else { + ReadOnlyCollection tagIds = GetTagIds(tag, tags); + if (tagIds.Count == 0) { + results = null; + } else { + ReadOnlyCollection? imageTags = ImageTags.GetImageTags(imageTagsPath); + if (imageTags is null || imageTags.Count == 0) { + results = null; + } else { + ReadOnlyCollection imageIds = GetImageIds(imageTags, tagIds); + if (imageIds.Count == 0) { + results = null; + } else { + results = []; + long id; + ImageTag imageTag; + ReadOnlyCollection? images = Images.GetImages(imagesPath); + if (images is null || images.Count == 0) { + results = null; + } else { + foreach (Images i in images) { + if (!imageIds.Contains(i.Id) || !long.TryParse(i.Name.Split('.')[0], out id)) { + continue; + } + imageTag = new(Id: id, Name: i.Name, Tag: tag); + results.Add(imageTag); + } + } + } + } + } + } + return results?.AsReadOnly(); + } + + private static ReadOnlyCollection GetTagIds(string tag, ReadOnlyCollection tags) { + List results = []; + foreach (Tags t in tags) { + if (t.Name != tag) { + continue; + } + results.Add(t.Id); + } + return results.AsReadOnly(); + } + + private static ReadOnlyCollection GetImageIds(ReadOnlyCollection imageTags, ReadOnlyCollection tagIds) { + List results = []; + foreach (ImageTags i in imageTags) { + if (!tagIds.Contains(i.TagId)) { + continue; + } + results.Add(i.ImageId); + } + return results.AsReadOnly(); + } + +} \ No newline at end of file diff --git a/src/ImmichToSlideshow/Domain/Product.cs b/src/ImmichToSlideshow/Domain/Product.cs index 02c89b1..b5fde68 100644 --- a/src/ImmichToSlideshow/Domain/Product.cs +++ b/src/ImmichToSlideshow/Domain/Product.cs @@ -1,13 +1,8 @@ namespace ImmichToSlideshow.Domain; -public class Product -{ +// Business concerns - public Guid Id { get; init; } = Guid.NewGuid(); - public required string Name { get; init; } - public required string Category { get; init; } - public required string SubCategory { get; init; } - - // Business concerns - -} \ No newline at end of file +public record Product(Guid Id, + string Name, + string Category, + string SubCategory); \ No newline at end of file diff --git a/src/ImmichToSlideshow/Domain/User.cs b/src/ImmichToSlideshow/Domain/User.cs index dbabb04..587e3b9 100644 --- a/src/ImmichToSlideshow/Domain/User.cs +++ b/src/ImmichToSlideshow/Domain/User.cs @@ -1,14 +1,11 @@ namespace ImmichToSlideshow.Domain; -public class User -{ +// Business concerns - public Guid Id { get; init; } = Guid.NewGuid(); - public List Products { get; init; } = []; +public record User(Guid Id, + List Products) { internal void AddProduct(Product product) => Products.Add(product); - // Business concerns - } \ No newline at end of file diff --git a/src/ImmichToSlideshow/ImmichToSlideshow.csproj b/src/ImmichToSlideshow/ImmichToSlideshow.csproj index 235e41e..eb818fa 100644 --- a/src/ImmichToSlideshow/ImmichToSlideshow.csproj +++ b/src/ImmichToSlideshow/ImmichToSlideshow.csproj @@ -1,4 +1,4 @@ - + enable enable @@ -7,6 +7,7 @@ cc24ad7a-1d95-4c47-a3ea-0d8475ab06da - + + \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/AppSettings.cs b/src/ImmichToSlideshow/Models/AppSettings.cs index 0845815..e316a2f 100644 --- a/src/ImmichToSlideshow/Models/AppSettings.cs +++ b/src/ImmichToSlideshow/Models/AppSettings.cs @@ -3,31 +3,68 @@ using System.Text.Json.Serialization; namespace ImmichToSlideshow.Models; -public record AppSettings(int AddDays, - string ArchivedTag, - string Company, - string ConnectionString, - string CurrentCommit, - string CurrentResultsDirectory, - string[] FilterTags, - string ImmichUploadDirectory, - string RandomResultsDirectory, - string SyncDirectory, - string URLs, - string[] WithOrigins, - string WorkingDirectoryName) -{ +public record AppSettings(string Company, Settings Settings, string URLs, string[] WithOrigins) { - public override string ToString() - { + public override string ToString() { string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings); return result; } + private static void Verify(AppSettings appSettings) { + if (appSettings?.Company is null) + throw new NullReferenceException(nameof(Company)); + if (appSettings?.URLs is null) + throw new NullReferenceException(nameof(URLs)); + if (appSettings?.WithOrigins is null) + throw new NullReferenceException(nameof(WithOrigins)); + if (appSettings?.Settings?.AddDays is null) + throw new NullReferenceException(nameof(Settings.AddDays)); + if (appSettings?.Settings?.ArchivedTag is null) + throw new NullReferenceException(nameof(Settings.ArchivedTag)); + if (appSettings?.Settings?.ConnectionString is null) + throw new NullReferenceException(nameof(Settings.ConnectionString)); + if (appSettings?.Settings?.DigiKam4 is null) + throw new NullReferenceException(nameof(Settings.DigiKam4)); + if (appSettings?.Settings?.FilterTags is null) + throw new NullReferenceException(nameof(Settings.FilterTags)); + if (appSettings?.Settings?.ImmichUploadDirectory is null) + throw new NullReferenceException(nameof(Settings.ImmichUploadDirectory)); + if (appSettings?.Settings?.RandomResultsDirectory is null) + throw new NullReferenceException(nameof(Settings.RandomResultsDirectory)); + if (appSettings?.Settings?.SyncDirectory is null) + throw new NullReferenceException(nameof(Settings.SyncDirectory)); + } + + public static AppSettings Get(IConfigurationRoot configurationRoot) { + AppSettings result; +#pragma warning disable IL3050, IL2026 + string? urls = configurationRoot.GetSection(nameof(URLs)).Get(); + string? company = configurationRoot.GetSection(nameof(Company)).Get(); + Settings? settings = configurationRoot.GetSection(nameof(Settings)).Get(); + string[]? withOrigins = configurationRoot.GetSection(nameof(WithOrigins)).Get(); +#pragma warning restore IL3050, IL2026 + if (company is null || settings is null || urls is null || withOrigins is null) { + List paths = []; + foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) { + if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) + continue; + if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) + continue; + paths.Add(physicalFileProvider.Root); + } + throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}"); + } + result = new(Company: company, + Settings: settings, + URLs: urls, + WithOrigins: withOrigins); + Verify(result); + return result; + } + } [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(AppSettings))] -internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext -{ +internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext { } \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/Binder/.editorconfig b/src/ImmichToSlideshow/Models/Binder/.editorconfig deleted file mode 100644 index 1c444cd..0000000 --- a/src/ImmichToSlideshow/Models/Binder/.editorconfig +++ /dev/null @@ -1,2 +0,0 @@ -[*.cs] -csharp_preserve_single_line_statements = true \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/Binder/AppSettings.cs b/src/ImmichToSlideshow/Models/Binder/AppSettings.cs deleted file mode 100644 index d1bc484..0000000 --- a/src/ImmichToSlideshow/Models/Binder/AppSettings.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace ImmichToSlideshow.Models.Binder; - -public class AppSettings -{ - - public int? AddDays { get; set; } - public string? ArchivedTag { get; set; } - public string? Company { get; set; } - public string? ConnectionString { get; set; } - public string? CurrentCommit { get; set; } - public string? CurrentResultsDirectory { get; set; } - public string[]? FilterTags { get; set; } - public string? ImmichUploadDirectory { get; set; } - public string? RandomResultsDirectory { get; set; } - public string? SyncDirectory { get; set; } - public string? URLs { get; set; } - public string[]? WithOrigins { get; set; } - public string? WorkingDirectoryName { get; set; } - - public override string ToString() - { - string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings); - return result; - } - - private static void PreVerify(IConfigurationRoot configurationRoot, AppSettings? appSettings) - { - if (appSettings?.Company is null) - { - List paths = []; - foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) - { - if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) - continue; - if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) - continue; - paths.Add(physicalFileProvider.Root); - } - throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}"); - } - } - - private static Models.AppSettings Get(AppSettings? appSettings) - { - Models.AppSettings result; - if (appSettings?.AddDays is null) throw new NullReferenceException(nameof(appSettings.AddDays)); - if (appSettings?.ArchivedTag is null) throw new NullReferenceException(nameof(appSettings.ArchivedTag)); - if (appSettings?.Company is null) throw new NullReferenceException(nameof(appSettings.Company)); - if (appSettings?.ConnectionString is null) throw new NullReferenceException(nameof(appSettings.ConnectionString)); - if (appSettings?.CurrentCommit is null) throw new NullReferenceException(nameof(appSettings.CurrentCommit)); - if (appSettings?.CurrentResultsDirectory is null) throw new NullReferenceException(nameof(appSettings.CurrentResultsDirectory)); - if (appSettings?.FilterTags is null) throw new NullReferenceException(nameof(appSettings.FilterTags)); - if (appSettings?.ImmichUploadDirectory is null) throw new NullReferenceException(nameof(appSettings.ImmichUploadDirectory)); - if (appSettings?.RandomResultsDirectory is null) throw new NullReferenceException(nameof(appSettings.RandomResultsDirectory)); - if (appSettings?.SyncDirectory is null) throw new NullReferenceException(nameof(appSettings.SyncDirectory)); - if (appSettings?.URLs is null) throw new NullReferenceException(nameof(appSettings.URLs)); - if (appSettings?.WithOrigins is null) throw new NullReferenceException(nameof(appSettings.WithOrigins)); - if (appSettings?.WorkingDirectoryName is null) throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName)); - result = new(appSettings.AddDays.Value, - appSettings.ArchivedTag, - appSettings.Company, - appSettings.ConnectionString, - appSettings.CurrentCommit, - appSettings.CurrentResultsDirectory, - appSettings.FilterTags, - appSettings.ImmichUploadDirectory, - appSettings.RandomResultsDirectory, - appSettings.SyncDirectory, - appSettings.URLs, - appSettings.WithOrigins, - appSettings.WorkingDirectoryName); - return result; - } - - public static Models.AppSettings Get(IConfigurationRoot configurationRoot) - { - Models.AppSettings result; -#pragma warning disable IL3050, IL2026 - AppSettings? appSettings = configurationRoot.Get(); -#pragma warning restore IL3050, IL2026 - PreVerify(configurationRoot, appSettings); - result = Get(appSettings); - return result; - } - -} - -[JsonSourceGenerationOptions(WriteIndented = true)] -[JsonSerializable(typeof(AppSettings))] -internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext -{ -} \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/AlbumRoots.cs b/src/ImmichToSlideshow/Models/DigiKam/AlbumRoots.cs new file mode 100644 index 0000000..4e01aed --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/AlbumRoots.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record AlbumRoots([property: JsonPropertyName("caseSensitivity")] int CaseSensitivity, + [property: JsonPropertyName("id")] int Id, + [property: JsonPropertyName("identifier")] string Identifier, + [property: JsonPropertyName("label")] string Label, + [property: JsonPropertyName("specificPath")] string SpecificPath, + [property: JsonPropertyName("status")] int Status, + [property: JsonPropertyName("type")] int Type); \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/Albums.cs b/src/ImmichToSlideshow/Models/DigiKam/Albums.cs new file mode 100644 index 0000000..643a07f --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/Albums.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record Albums([property: JsonPropertyName("albumRoot")] int AlbumRoot, + [property: JsonPropertyName("caption")] object Caption, + [property: JsonPropertyName("collection")] object Collection, + [property: JsonPropertyName("date")] string Date, + [property: JsonPropertyName("icon")] object Icon, + [property: JsonPropertyName("id")] int Id, + [property: JsonPropertyName("modificationDate")] DateTime ModificationDate, + [property: JsonPropertyName("relativePath")] string RelativePath); \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/ImageComments.cs b/src/ImmichToSlideshow/Models/DigiKam/ImageComments.cs new file mode 100644 index 0000000..3e55f2f --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/ImageComments.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record ImageComments([property: JsonPropertyName("author")] object Author, + [property: JsonPropertyName("comment")] string Comment, + [property: JsonPropertyName("date")] object Date, + [property: JsonPropertyName("id")] int Id, + [property: JsonPropertyName("imageid")] int ImageId, + [property: JsonPropertyName("language")] string Language, + [property: JsonPropertyName("type")] int Type); \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/ImagePositions.cs b/src/ImmichToSlideshow/Models/DigiKam/ImagePositions.cs new file mode 100644 index 0000000..6c9aba9 --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/ImagePositions.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record ImagePositions([property: JsonPropertyName("accuracy")] object Accuracy, + [property: JsonPropertyName("altitude")] double? Altitude, + [property: JsonPropertyName("description")] object Description, + [property: JsonPropertyName("imageid")] int ImageId, + [property: JsonPropertyName("latitude")] string Latitude, + [property: JsonPropertyName("latitudeNumber")] double? LatitudeNumber, + [property: JsonPropertyName("longitude")] string Longitude, + [property: JsonPropertyName("longitudeNumber")] double? LongitudeNumber, + [property: JsonPropertyName("orientation")] object Orientation, + [property: JsonPropertyName("roll")] object Roll, + [property: JsonPropertyName("tilt")] object Tilt); \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/ImageTagProperties.cs b/src/ImmichToSlideshow/Models/DigiKam/ImageTagProperties.cs new file mode 100644 index 0000000..071012c --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/ImageTagProperties.cs @@ -0,0 +1,8 @@ +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record ImageTagProperties([property: JsonPropertyName("imageid")] int ImageId, + [property: JsonPropertyName("property")] string Property, + [property: JsonPropertyName("tagid")] int TagId, + [property: JsonPropertyName("value")] string Value); \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/ImageTags.cs b/src/ImmichToSlideshow/Models/DigiKam/ImageTags.cs new file mode 100644 index 0000000..9c74bfe --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/ImageTags.cs @@ -0,0 +1,21 @@ +using System.Collections.ObjectModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record ImageTags([property: JsonPropertyName("imageid")] int ImageId, + [property: JsonPropertyName("tagid")] int TagId) { + + internal static ReadOnlyCollection? GetImageTags(string? path) { + ImageTags[]? results; + if (string.IsNullOrEmpty(path) || !File.Exists(path)) { + results = null; + } else { + string json = File.ReadAllText(path); + results = JsonSerializer.Deserialize(json); + } + return results?.AsReadOnly(); + } + +} \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/Images.cs b/src/ImmichToSlideshow/Models/DigiKam/Images.cs new file mode 100644 index 0000000..6e0936e --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/Images.cs @@ -0,0 +1,28 @@ +using System.Collections.ObjectModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record Images([property: JsonPropertyName("album")] int? Album, + [property: JsonPropertyName("category")] int Category, + [property: JsonPropertyName("fileSize")] object FileSize, + [property: JsonPropertyName("id")] int Id, + [property: JsonPropertyName("manualOrder")] object ManualOrder, + [property: JsonPropertyName("modificationDate")] DateTime ModificationDate, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("status")] int Status, + [property: JsonPropertyName("uniqueHash")] string UniqueHash) { + + internal static ReadOnlyCollection? GetImages(string? path) { + Images[]? results; + if (string.IsNullOrEmpty(path) || !File.Exists(path)) { + results = null; + } else { + string json = File.ReadAllText(path); + results = JsonSerializer.Deserialize(json); + } + return results?.AsReadOnly(); + } + +} \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/TagProperties.cs b/src/ImmichToSlideshow/Models/DigiKam/TagProperties.cs new file mode 100644 index 0000000..273324c --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/TagProperties.cs @@ -0,0 +1,7 @@ +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record TagProperties([property: JsonPropertyName("property")] string Property, + [property: JsonPropertyName("tagid")] int TagId, + [property: JsonPropertyName("value")] object Value); \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam/Tags.cs b/src/ImmichToSlideshow/Models/DigiKam/Tags.cs new file mode 100644 index 0000000..15fb33f --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam/Tags.cs @@ -0,0 +1,24 @@ +using System.Collections.ObjectModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models.DigiKam; + +public record Tags([property: JsonPropertyName("icon")] object Icon, + [property: JsonPropertyName("iconkde")] object IconKDE, + [property: JsonPropertyName("id")] int Id, + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("pid")] int PId) { + + internal static ReadOnlyCollection? GetTags(string? path) { + Tags[]? results; + if (string.IsNullOrEmpty(path) || !File.Exists(path)) { + results = null; + } else { + string json = File.ReadAllText(path); + results = JsonSerializer.Deserialize(json); + } + return results?.AsReadOnly(); + } + +} \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/DigiKam4.cs b/src/ImmichToSlideshow/Models/DigiKam4.cs new file mode 100644 index 0000000..abade5c --- /dev/null +++ b/src/ImmichToSlideshow/Models/DigiKam4.cs @@ -0,0 +1,22 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models; + +public record DigiKam4(string Images, + string ImageTagProperties, + string ImageTags, + string TagProperties, + string Tags) { + + public override string ToString() { + string result = JsonSerializer.Serialize(this, DigiKam4SourceGenerationContext.Default.DigiKam4); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(DigiKam4))] +public partial class DigiKam4SourceGenerationContext : JsonSerializerContext { +} \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/Identifier.cs b/src/ImmichToSlideshow/Models/Identifier.cs index 9b671e3..e956755 100644 --- a/src/ImmichToSlideshow/Models/Identifier.cs +++ b/src/ImmichToSlideshow/Models/Identifier.cs @@ -1,3 +1,4 @@ +using ImmichToSlideshow.Domain; using System.Collections.ObjectModel; using System.Text.Json; using System.Text.Json.Serialization; @@ -10,11 +11,9 @@ public sealed record Identifier(string[] DirectoryNames, int Id, long Length, string PaddedId, - long Ticks) -{ + long Ticks) { - public override string ToString() - { + public override string ToString() { string result = JsonSerializer.Serialize(this, IdentifierSourceGenerationContext.Default.Identifier); return result; } @@ -22,6 +21,9 @@ public sealed record Identifier(string[] DirectoryNames, internal static string GetDeviceAssetIds(ReadOnlyCollection identifiers) => $"'{string.Join($"',{Environment.NewLine}'", (from l in identifiers select GetDeviceAssetId(l)).ToArray())}'"; + internal static string GetDeviceAssetIds(ReadOnlyCollection imageTags) => + $"'{string.Join($"',{Environment.NewLine}'", (from l in imageTags select l.Name).ToArray())}'"; + internal static string GetDeviceAssetId(Identifier identifier) => $"{identifier.PaddedId}{identifier.Extension}"; @@ -29,12 +31,10 @@ public sealed record Identifier(string[] DirectoryNames, [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Identifier))] -public partial class IdentifierSourceGenerationContext : JsonSerializerContext -{ +public partial class IdentifierSourceGenerationContext : JsonSerializerContext { } [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Identifier[]))] -public partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext -{ +public partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext { } \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/Immich/Asset.cs b/src/ImmichToSlideshow/Models/Immich/Asset.cs index 3118023..7a043cd 100644 --- a/src/ImmichToSlideshow/Models/Immich/Asset.cs +++ b/src/ImmichToSlideshow/Models/Immich/Asset.cs @@ -31,11 +31,9 @@ public record Asset([property: JsonPropertyName("id")] string Id, // [property: JsonPropertyName("stackId")]? object? StackId, [property: JsonPropertyName("duplicateId")] string? DuplicateId, // [property: JsonPropertyName("status")] string Status, - [property: JsonPropertyName("path")] string Path) -{ + [property: JsonPropertyName("path")] string Path) { - public override string ToString() - { + public override string ToString() { string result = JsonSerializer.Serialize(this, AssetSourceGenerationContext.Default.Asset); return result; } @@ -44,12 +42,10 @@ public record Asset([property: JsonPropertyName("id")] string Id, [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Asset))] -internal partial class AssetSourceGenerationContext : JsonSerializerContext -{ +internal partial class AssetSourceGenerationContext : JsonSerializerContext { } [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Asset[]))] -internal partial class AssetCollectionSourceGenerationContext : JsonSerializerContext -{ +internal partial class AssetCollectionSourceGenerationContext : JsonSerializerContext { } \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/Immich/Tag.cs b/src/ImmichToSlideshow/Models/Immich/Tag.cs index 86d7651..33903e7 100644 --- a/src/ImmichToSlideshow/Models/Immich/Tag.cs +++ b/src/ImmichToSlideshow/Models/Immich/Tag.cs @@ -10,11 +10,9 @@ public record Tag([property: JsonPropertyName("id")] string Id, // [property: JsonPropertyName("updatedAt")] DateTime UpdatedAt, // [property: JsonPropertyName("color")] char Color, // [property: JsonPropertyName("parentId")] string ParentId, - [property: JsonPropertyName("updateId")] string UpdateId) -{ + [property: JsonPropertyName("updateId")] string UpdateId) { - public override string ToString() - { + public override string ToString() { string result = JsonSerializer.Serialize(this, TagSourceGenerationContext.Default.Tag); return result; } @@ -23,12 +21,10 @@ public record Tag([property: JsonPropertyName("id")] string Id, [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Tag))] -internal partial class TagSourceGenerationContext : JsonSerializerContext -{ +internal partial class TagSourceGenerationContext : JsonSerializerContext { } [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Tag[]))] -internal partial class TagCollectionSourceGenerationContext : JsonSerializerContext -{ +internal partial class TagCollectionSourceGenerationContext : JsonSerializerContext { } \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/Settings.cs b/src/ImmichToSlideshow/Models/Settings.cs new file mode 100644 index 0000000..9333ed5 --- /dev/null +++ b/src/ImmichToSlideshow/Models/Settings.cs @@ -0,0 +1,27 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ImmichToSlideshow.Models; + +public record Settings(int AddDays, + string ArchivedTag, + string ConnectionString, + DigiKam4? DigiKam4, + string[] FilterTags, + string ImmichUploadDirectory, + float LowestVersionHistory, + string NotNinePath, + string RandomResultsDirectory, + string SyncDirectory) { + + public override string ToString() { + string result = JsonSerializer.Serialize(this, SettingsSourceGenerationContext.Default.Settings); + return result; + } + +} + +[JsonSourceGenerationOptions(WriteIndented = true)] +[JsonSerializable(typeof(Settings))] +public partial class SettingsSourceGenerationContext : JsonSerializerContext { +} \ No newline at end of file diff --git a/src/ImmichToSlideshow/Persistence/Database/DbConstants.cs b/src/ImmichToSlideshow/Persistence/Database/DbConstants.cs index e2877bc..f0f2c44 100644 --- a/src/ImmichToSlideshow/Persistence/Database/DbConstants.cs +++ b/src/ImmichToSlideshow/Persistence/Database/DbConstants.cs @@ -1,7 +1,6 @@ namespace ImmichToSlideshow.Persistence.Database; -public static class DbConstants -{ +public static class DbConstants { public const string DefaultConnectionStringPath = "Database:ConnectionStrings:DefaultConnection"; diff --git a/src/ImmichToSlideshow/Program.cs b/src/ImmichToSlideshow/Program.cs index 35e16a0..52358ca 100644 --- a/src/ImmichToSlideshow/Program.cs +++ b/src/ImmichToSlideshow/Program.cs @@ -4,14 +4,12 @@ using ImmichToSlideshow.RequestPipeline; namespace ImmichToSlideshow; -public class Program -{ +public class Program { - public static void Main(string[] args) - { + public static void Main(string[] args) { WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args); _ = webApplicationBuilder.Configuration.AddUserSecrets(); - AppSettings appSettings = Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration); + AppSettings appSettings = AppSettings.Get(webApplicationBuilder.Configuration); _ = webApplicationBuilder.Services.AddControllers(); _ = webApplicationBuilder.Services.AddServices(appSettings); WebApplication webApplication = webApplicationBuilder.Build(); diff --git a/src/ImmichToSlideshow/RequestPipeline/WebApplicationExtensions.cs b/src/ImmichToSlideshow/RequestPipeline/WebApplicationExtensions.cs index 87fe229..f0b14b0 100644 --- a/src/ImmichToSlideshow/RequestPipeline/WebApplicationExtensions.cs +++ b/src/ImmichToSlideshow/RequestPipeline/WebApplicationExtensions.cs @@ -2,19 +2,16 @@ using ImmichToSlideshow.Models; namespace ImmichToSlideshow.RequestPipeline; -public static class WebApplicationExtensions -{ +public static class WebApplicationExtensions { public static WebApplication InitializeDatabase(this WebApplication webApplication) => // DBInitializer.Initialize(application.Configuration[DbConstants.DefaultConnectionStringPath]!); webApplication; - public static WebApplication InitializeCorsAndHttps(this WebApplication webApplication, AppSettings appSettings) - { + public static WebApplication InitializeCorsAndHttps(this WebApplication webApplication, AppSettings appSettings) { _ = webApplication.UseCors(corsPolicyBuilder => corsPolicyBuilder.WithOrigins(appSettings.WithOrigins).AllowAnyHeader().AllowAnyMethod()); - if (appSettings.URLs.Contains("https", StringComparison.InvariantCultureIgnoreCase)) - { + if (appSettings.URLs.Contains("https", StringComparison.InvariantCultureIgnoreCase)) { _ = webApplication.UseHttpsRedirection(); _ = webApplication.UseHsts(); } diff --git a/src/ImmichToSlideshow/Services/AssetService.cs b/src/ImmichToSlideshow/Services/AssetService.cs index fb1c6b2..ec9a162 100644 --- a/src/ImmichToSlideshow/Services/AssetService.cs +++ b/src/ImmichToSlideshow/Services/AssetService.cs @@ -1,3 +1,4 @@ +using ImmichToSlideshow.Domain; using ImmichToSlideshow.Models; using ImmichToSlideshow.Models.Immich; using Npgsql; @@ -8,21 +9,18 @@ using System.Text.Json; namespace ImmichToSlideshow.Services; -public class AssetService(ILogger logger, AppSettings appSettings) -{ +public class AssetService(ILogger logger, AppSettings appSettings) { #pragma warning disable CS9124 private readonly ILogger _Logger = logger; - private readonly AppSettings _AppSettings = appSettings; + private readonly Settings _Settings = appSettings.Settings; #pragma warning restore CS9124 - private static int? ExecuteNonQuery(string connectionString, string commandText, ReadOnlyCollection npgsqlParameters) - { + private static int? ExecuteNonQuery(string connectionString, string commandText, ReadOnlyCollection npgsqlParameters) { int? result; - if (string.IsNullOrEmpty(connectionString)) + if (string.IsNullOrEmpty(connectionString)) { result = null; - else - { + } else { using NpgsqlConnection npgsqlConnection = new(connectionString); npgsqlConnection.Open(); using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection); @@ -32,61 +30,60 @@ public class AssetService(ILogger logger, AppSettings appSettings) return result; } - private static StringBuilder GetForJsonPath(string connectionString, string commandText, ReadOnlyCollection npgsqlParameters) - { + private static StringBuilder GetForJsonPath(string connectionString, string commandText, ReadOnlyCollection npgsqlParameters) { StringBuilder result = new(); using NpgsqlConnection npgsqlConnection = new(connectionString); npgsqlConnection.Open(); using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection); npgsqlCommand.Parameters.AddRange(npgsqlParameters.ToArray()); NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader(CommandBehavior.SequentialAccess); - while (npgsqlDataReader.Read()) + while (npgsqlDataReader.Read()) { _ = result.Append(npgsqlDataReader.GetString(0)); + } return result; } - public string? GetColumns() - { + public string? GetColumns() { string result; NpgsqlParameter[] npgsqlParameters = []; string commandText = CommandText.GetColumns(); - StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); + StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); result = stringBuilder.ToString(); - if (result.Length == 1) + if (result.Length == 1) { File.WriteAllText(".vscode/jsonl/.jsonl", result); + } return result; } - public string? GetOwnerIds() - { + public string? GetOwnerIds() { string result; NpgsqlParameter[] npgsqlParameters = []; - string commandText = CommandText.GetOwnerIdActiveImage(); - StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); + string commandText = CommandText.GetOwnerIdActiveImage(_Settings.LowestVersionHistory); + StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); result = stringBuilder.ToString(); - if (result.Length == 1) + if (result.Length == 1) { File.WriteAllText(".vscode/jsonl/.jsonl", result); + } return result; } - public string? GetAssets(Guid ownerId) - { + public string? GetAssets(Guid ownerId) { string result; NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)]; - string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_AppSettings.FilterTags); - StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); + string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_Settings.LowestVersionHistory, _Settings.FilterTags); + StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); result = stringBuilder.ToString(); - if (result.Length == 1) + if (result.Length == 1) { File.WriteAllText(".vscode/jsonl/assets.jsonl", result); + } return result; } - public ReadOnlyCollection? GetRandomPaths(Guid ownerId) - { + public ReadOnlyCollection? GetRandomPaths(Guid ownerId) { string[]? results; NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)]; - string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_AppSettings.FilterTags); - StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); + string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_Settings.LowestVersionHistory, _Settings.FilterTags); + StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); string json = stringBuilder.ToString(); Random random = new(); string ownerIdValue = ownerId.ToString(); @@ -95,49 +92,44 @@ public class AssetService(ILogger logger, AppSettings appSettings) return results?.AsReadOnly(); } - public Tag[]? GetArchivedTag(AppSettings appSettings, Guid ownerId) - { - Tag[] results; + public Tag[]? GetArchivedTag(Settings settings, Guid ownerId) { + Tag[]? results; Guid userId = ownerId; - string value = appSettings.ArchivedTag; - NpgsqlParameter[] npgsqlParameters = - [ + string value = settings.ArchivedTag; + NpgsqlParameter[] npgsqlParameters = [ new NpgsqlParameter(nameof(value), value), new NpgsqlParameter(nameof(userId), userId), ]; - string commandText = CommandText.GetArchivedTag(); - StringBuilder stringBuilder = GetForJsonPath(appSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); + string commandText = CommandText.GetArchivedTag(_Settings.LowestVersionHistory); + StringBuilder stringBuilder = GetForJsonPath(settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); string json = stringBuilder.ToString(); results = JsonSerializer.Deserialize(json, TagCollectionSourceGenerationContext.Default.TagArray); return results; } - public string? GetArchivedTag(Guid ownerId) - { - string result; - Tag[]? tags = GetArchivedTag(_AppSettings, ownerId); + public string? GetArchivedTag(Guid ownerId) { + string? result; + Tag[]? tags = GetArchivedTag(_Settings, ownerId); result = tags is null || tags.Length != 1 ? null : tags[0].Id; return result; } - public string? SaveRandomPaths(Guid ownerId) - { + public string? SaveRandomPaths(Guid ownerId) { string? results; DateTime dateTime = DateTime.Now; string tomorrow = dateTime.AddDays(1).ToString("MM-dd"); - if (Directory.Exists(_AppSettings.RandomResultsDirectory)) - _ = Directory.CreateDirectory(_AppSettings.RandomResultsDirectory); - FileInfo fileInfo = new(Path.Combine(_AppSettings.RandomResultsDirectory, $"{tomorrow}.json")); - if (fileInfo.Exists && fileInfo.CreationTime > dateTime.AddDays(_AppSettings.AddDays)) + if (Directory.Exists(_Settings.RandomResultsDirectory)) { + _ = Directory.CreateDirectory(_Settings.RandomResultsDirectory); + } + FileInfo fileInfo = new(Path.Combine(_Settings.RandomResultsDirectory, $"{tomorrow}.json")); + if (fileInfo.Exists && fileInfo.CreationTime > dateTime.AddDays(_Settings.AddDays)) { results = null; - else - { + } else { _Logger.LogDebug("Writing <{FullName}>", fileInfo.FullName); ReadOnlyCollection? paths = GetRandomPaths(ownerId); - if (paths is null) + if (paths is null) { results = null; - else - { + } else { _Logger.LogInformation("{count} path(s)", paths.Count.ToString()); results = JsonSerializer.Serialize(paths); File.WriteAllText(fileInfo.FullName, results); @@ -148,32 +140,32 @@ public class AssetService(ILogger logger, AppSettings appSettings) private record Record(string Source, string Destination); - public ReadOnlyCollection SyncImmich(Guid ownerId) - { + public ReadOnlyCollection SyncImmich(Guid ownerId) { List results = []; Record record; List records = []; - if (Directory.Exists(_AppSettings.SyncDirectory)) - _ = Directory.CreateDirectory(_AppSettings.SyncDirectory); - int syncLength = _AppSettings.SyncDirectory.Length; - string[] syncFiles = Directory.GetFiles(_AppSettings.SyncDirectory, "*", SearchOption.AllDirectories); + if (Directory.Exists(_Settings.SyncDirectory)) { + _ = Directory.CreateDirectory(_Settings.SyncDirectory); + } + int syncLength = _Settings.SyncDirectory.Length; + string[] syncFiles = Directory.GetFiles(_Settings.SyncDirectory, "*", SearchOption.AllDirectories); string[] syncCheck = syncFiles.Select(l => l[syncLength..]).ToArray(); - if (Directory.Exists(_AppSettings.ImmichUploadDirectory)) - _ = Directory.CreateDirectory(_AppSettings.ImmichUploadDirectory); - int immichUploadLength = _AppSettings.ImmichUploadDirectory.Length; - string[] immichUploadFiles = Directory.GetFiles(_AppSettings.ImmichUploadDirectory, "*", SearchOption.AllDirectories); + if (Directory.Exists(_Settings.ImmichUploadDirectory)) { + _ = Directory.CreateDirectory(_Settings.ImmichUploadDirectory); + } + int immichUploadLength = _Settings.ImmichUploadDirectory.Length; + string[] immichUploadFiles = Directory.GetFiles(_Settings.ImmichUploadDirectory, "*", SearchOption.AllDirectories); string[] immichUploadCheck = immichUploadFiles.Select(l => l[immichUploadLength..]).ToArray(); - for (int i = 0; i < immichUploadFiles.Length; i++) - { - if (syncCheck.Contains(immichUploadCheck[i])) + for (int i = 0; i < immichUploadFiles.Length; i++) { + if (syncCheck.Contains(immichUploadCheck[i])) { continue; + } results.Add(immichUploadCheck[i]); - record = new(immichUploadFiles[i], string.Concat(_AppSettings.SyncDirectory, immichUploadCheck[i])); + record = new(immichUploadFiles[i], string.Concat(_Settings.SyncDirectory, immichUploadCheck[i])); records.Add(record); } _Logger.LogInformation("{count} file(s)", results.Count.ToString()); - for (int i = 0; i < records.Count; i++) - { + for (int i = 0; i < records.Count; i++) { record = records[i]; _Logger.LogDebug("Copying <{source}>", record.Source); File.Copy(record.Source, record.Destination); @@ -181,51 +173,73 @@ public class AssetService(ILogger logger, AppSettings appSettings) return results.AsReadOnly(); } - public ReadOnlyCollection SetArchiveImmich(Guid ownerId) - { + public ReadOnlyCollection? SetArchiveImmich(Guid ownerId) { ReadOnlyCollection? results; - string checkDirectory = Path.Combine(_AppSettings.CurrentResultsDirectory, "B)Metadata", _AppSettings.CurrentCommit, "[]"); - if (!Directory.Exists(checkDirectory)) + if (!File.Exists(_Settings.NotNinePath)) { results = null; - else - { - string checkFile = Path.Combine(checkDirectory, "!9.json"); - if (!File.Exists(checkFile)) + } else { + string json = File.ReadAllText(_Settings.NotNinePath); + Identifier[]? identifiers = JsonSerializer.Deserialize(json); + if (identifiers is null || identifiers.Length == 0) { results = null; - else - { - string json = File.ReadAllText(checkFile); - Identifier[]? identifiers = JsonSerializer.Deserialize(json); - if (identifiers is null || identifiers.Length == 0) + } else { + Tag[]? tags = GetArchivedTag(_Settings, ownerId); + if (tags is null || tags.Length != 1) { results = null; - else - { - Tag[]? tags = GetArchivedTag(_AppSettings, ownerId); - if (tags is null || tags.Length != 1) - results = null; - else - results = SetArchiveImmich(logger, appSettings, ownerId, identifiers.AsReadOnly(), tags.AsReadOnly()); + } else { + results = SetArchiveImmich(logger, _Settings, ownerId, identifiers.AsReadOnly(), tags.AsReadOnly()); } } } return results?.AsReadOnly(); } - private static ReadOnlyCollection SetArchiveImmich(ILogger logger, AppSettings appSettings, Guid ownerId, ReadOnlyCollection identifiers, ReadOnlyCollection tags) - { + private static ReadOnlyCollection? SetArchiveImmich(ILogger logger, Settings settings, Guid ownerId, string tagsId, string deviceAssetIds) { ReadOnlyCollection? results; - string tagsId = tags[0].Id; - NpgsqlParameter[] npgsqlParameters = - [ + NpgsqlParameter[] npgsqlParameters = [ new NpgsqlParameter(nameof(tagsId), tagsId), new NpgsqlParameter(nameof(ownerId), ownerId), ]; - string deviceAssetIds = Identifier.GetDeviceAssetIds(identifiers); - string commandText = CommandText.SetAssetArchived(deviceAssetIds); + string commandText = CommandText.SetAssetArchived(settings.LowestVersionHistory, deviceAssetIds); logger.LogDebug(commandText.Replace($"@{nameof(tagsId)}", $"'{tagsId}'").Replace($"@{nameof(ownerId)}", $"'{ownerId}'".ToString())); - int? result = ExecuteNonQuery(appSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); + int? result = ExecuteNonQuery(settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); results = result is null ? null : new([result.Value]); return results?.AsReadOnly(); } + private static ReadOnlyCollection? SetArchiveImmich(ILogger logger, Settings settings, Guid ownerId, ReadOnlyCollection identifiers, ReadOnlyCollection tags) { + ReadOnlyCollection? results; + string tagsId = tags[0].Id; + string deviceAssetIds = Identifier.GetDeviceAssetIds(identifiers); + results = SetArchiveImmich(logger, settings, ownerId, tagsId, deviceAssetIds); + return results?.AsReadOnly(); + } + + public ReadOnlyCollection? SetDigiKam4ArchiveImmich(Guid ownerId) { + ReadOnlyCollection? results; + ReadOnlyCollection? imageTags = ImageTag.Get(tag: _Settings.ArchivedTag, + tagsPath: _Settings.DigiKam4?.Tags, + imageTagsPath: _Settings.DigiKam4?.ImageTags, + imagesPath: _Settings.DigiKam4?.Images); + if (imageTags is null || imageTags.Count == 0) { + results = null; + } else { + Tag[]? tags = GetArchivedTag(_Settings, ownerId); + if (tags is null || tags.Length != 1) { + results = null; + } else { + results = SetArchiveImmich(logger, _Settings, ownerId, imageTags, tags.AsReadOnly()); + } + } + return results?.AsReadOnly(); + } + + private static ReadOnlyCollection? SetArchiveImmich(ILogger logger, Settings settings, Guid ownerId, ReadOnlyCollection imageTags, ReadOnlyCollection tags) { + ReadOnlyCollection? results; + string tagsId = tags[0].Id; + string deviceAssetIds = Identifier.GetDeviceAssetIds(imageTags); + results = SetArchiveImmich(logger, settings, ownerId, tagsId, deviceAssetIds); + return results?.AsReadOnly(); + } + } \ No newline at end of file diff --git a/src/ImmichToSlideshow/Services/CommandText.cs b/src/ImmichToSlideshow/Services/CommandText.cs index 01d223a..485255b 100644 --- a/src/ImmichToSlideshow/Services/CommandText.cs +++ b/src/ImmichToSlideshow/Services/CommandText.cs @@ -1,129 +1,176 @@ namespace ImmichToSlideshow.Services; -internal static class CommandText -{ +internal static class CommandText { - internal static string GetColumns() - { // cSpell:disable - List results = new(); - // results.Add(" SELECT COALESCE(SUM(checksum_failures), 0) "); - // results.Add(" FROM pg_stat_database "); - // results.Add(" SELECT json_agg(t) "); - // results.Add(" FROM information_schema.tables t "); - // results.Add(" WHERE table_schema= 'public' "); - // results.Add(" AND table_type= 'BASE TABLE' "); - results.Add(" SELECT json_agg(c) "); - results.Add(" FROM information_schema.columns c "); - // results.Add(" WHERE table_name = 'assets' "); - // results.Add(" WHERE table_name = 'libraries' "); - results.Add(" WHERE table_name = 'asset_files' "); + internal static string GetColumns() { // cSpell:disable + List results = [ + // " SELECT COALESCE(SUM(checksum_failures), 0) ", + // " FROM pg_stat_database ", + // " SELECT json_agg(t) ", + // " FROM information_schema.tables t ", + // " WHERE table_schema= 'public' ", + // " AND table_type= 'BASE TABLE' ", + " SELECT json_agg(c) ", + " FROM information_schema.columns c ", + // " WHERE table_name = 'assets' ", + // " WHERE table_name = 'libraries' ", + " WHERE table_name = 'asset_files'; " + ]; return string.Join(Environment.NewLine, results); } // cSpell:enable - internal static string GetOwnerIdActiveImage() - { // cSpell:disable - List results = new(); - results.Add(" SELECT json_agg(j) "); - results.Add(" FROM ( "); - results.Add(" SELECT a.\"ownerId\" "); - results.Add(" FROM assets a "); - results.Add(" WHERE a.\"status\" = 'active' "); - results.Add(" AND a.\"type\" = 'IMAGE' "); - results.Add(" GROUP"); - results.Add(" BY a.\"ownerId\" "); - results.Add(" ) j "); + internal static string GetOwnerIdActiveImage(float lowestVersionHistory) { // cSpell:disable + List results = [ + " SELECT json_agg(j) ", + " FROM ( ", + " SELECT a.\"ownerId\" " + ]; + if (lowestVersionHistory <= 1.129) { + results.Add(" FROM assets a "); + } else { + results.Add(" FROM public.asset a "); + } + results.AddRange([ + " WHERE a.\"status\" = 'active' ", + " AND a.\"type\" = 'IMAGE' ", + " GROUP", + " BY a.\"ownerId\" ", + " ) j; " + ]); return string.Join(Environment.NewLine, results); } // cSpell:enable - internal static string GetAssetActiveImagePreviewNotDuplicate(string[] filterTags) - { // cSpell:disable - List results = new(); - results.Add(" SELECT json_agg(j) "); - results.Add(" FROM ( "); - results.Add(" SELECT a.\"id\" "); - results.Add(" , a.\"deviceAssetId\" "); - // results.Add(" , a.\"ownerId\" "); - // results.Add(" , a.\"deviceId\" "); - // results.Add(" , a.\"type\" "); - results.Add(" , a.\"originalPath\" "); - // results.Add(" , a.\"fileCreatedAt\" "); - // results.Add(" , a.\"fileModifiedAt\" "); - // results.Add(" , a.\"isFavorite\" "); - // results.Add(" , a.\"duration\" "); - // results.Add(" , a.\"encodedVideoPath\" "); - // results.Add(" , a.\"checksum\" "); - // results.Add(" , a.\"isVisible\" "); - // results.Add(" , a.\"livePhotoVideoId\" "); - // results.Add(" , a.\"updatedAt\" "); - // results.Add(" , a.\"createdAt\" "); - // results.Add(" , a.\"isArchived\" "); - results.Add(" , a.\"originalFileName\" "); - // results.Add(" , a.\"sidecarPath\" "); - // results.Add(" , a.\"thumbhash\" "); - // results.Add(" , a.\"isOffline\" "); - // results.Add(" , a.\"libraryId\" "); - // results.Add(" , a.\"isExternal\" "); - // results.Add(" , a.\"deletedAt\" "); - // results.Add(" , a.\"localDateTime\" "); - // results.Add(" , a.\"stackId\" "); - results.Add(" , a.\"duplicateId\" "); - // results.Add(" , a.\"status\" "); - results.Add(" , f.\"path\" "); - results.Add(" FROM assets a "); - results.Add(" INNER "); - results.Add(" JOIN asset_files f "); - results.Add(" ON a.\"id\" = f.\"assetId\" "); - results.Add(" WHERE a.\"status\" = 'active' "); - results.Add(" AND a.\"type\" = 'IMAGE' "); - results.Add(" AND f.\"type\" = 'preview' "); - results.Add(" AND a.\"duplicateId\" is null "); - results.Add(" AND a.\"id\" not in ( "); - results.Add(" SELECT \"assetsId\" "); - results.Add(" FROM tag_asset g "); - results.Add(" JOIN tags t "); - results.Add(" ON g.\"tagsId\" = t.\"id\" "); - results.Add($" WHERE t.\"value\" in ('{string.Join("','", filterTags)}') "); - results.Add(" ) "); - results.Add(" AND a.\"isExternal\" = true "); - results.Add(" AND a.\"isOffline\" = false "); - results.Add(" AND a.\"ownerId\" = @ownerId "); - results.Add(" ) j "); + internal static string GetAssetActiveImagePreviewNotDuplicate(float lowestVersionHistory, string[] filterTags) { // cSpell:disable + List results = [ + " SELECT json_agg(j) ", + " FROM ( ", + " SELECT a.\"id\" ", + " , a.\"deviceAssetId\" ", + // " , a.\"ownerId\" ", + // " , a.\"deviceId\" ", + // " , a.\"type\" ", + " , a.\"originalPath\" ", + // " , a.\"fileCreatedAt\" ", + // " , a.\"fileModifiedAt\" ", + // " , a.\"isFavorite\" ", + // " , a.\"duration\" ", + // " , a.\"encodedVideoPath\" ", + // " , a.\"checksum\" ", + // " , a.\"isVisible\" ", + // " , a.\"livePhotoVideoId\" ", + // " , a.\"updatedAt\" ", + // " , a.\"createdAt\" ", + // " , a.\"isArchived\" ", + " , a.\"originalFileName\" ", + // " , a.\"sidecarPath\" ", + // " , a.\"thumbhash\" ", + // " , a.\"isOffline\" ", + // " , a.\"libraryId\" ", + // " , a.\"isExternal\" ", + // " , a.\"deletedAt\" ", + // " , a.\"localDateTime\" ", + // " , a.\"stackId\" ", + " , a.\"duplicateId\" ", + // " , a.\"status\" ", + " , f.\"path\" " + ]; + if (lowestVersionHistory <= 1.129) { + results.Add(" FROM assets a "); + } else { + results.Add(" FROM public.asset a "); + } + results.AddRange(" INNER "); + if (lowestVersionHistory <= 1.129) { + results.Add(" JOIN asset_files f "); + } else { + results.Add(" JOIN public.asset_file f "); + } + results.AddRange([ + " ON a.\"id\" = f.\"assetId\" ", + " WHERE a.\"status\" = 'active' ", + " AND a.\"type\" = 'IMAGE' ", + " AND f.\"type\" = 'preview' ", + " AND a.\"duplicateId\" is null ", + " AND a.\"id\" not in ( ", + " SELECT \"assetsId\" " + ]); + if (lowestVersionHistory <= 1.129) { + results.Add(" FROM tag_asset g "); + } else { + results.Add(" FROM public.tag_asset g "); + } + if (lowestVersionHistory <= 1.129) { + results.Add(" JOIN tags t "); + } else { + results.Add(" JOIN public.tag t "); + } + results.AddRange([ + " ON g.\"tagsId\" = t.\"id\" ", + $" WHERE t.\"value\" in ('{string.Join("','", filterTags)}') ", + " ) ", + " AND a.\"isExternal\" = true ", + " AND a.\"isOffline\" = false ", + " AND a.\"ownerId\" = @ownerId ", + " ) j; " + ]); return string.Join(Environment.NewLine, results); } // cSpell:enable - internal static string SetAssetArchived(string deviceAssetIds) - { // cSpell:disable - List results = new(); - results.Add(" INSERT INTO tag_asset "); - results.Add(" (\"assetsId\", \"tagsId\") "); - results.Add(" SELECT a.\"id\", @tagsId::uuid "); - results.Add(" FROM assets a "); - results.Add(" WHERE a.\"type\" = 'IMAGE' "); - results.Add(" AND a.\"ownerId\" = @ownerId "); - results.Add(" AND a.\"deviceAssetId\" in ( "); - results.Add(deviceAssetIds); - results.Add(" ) "); - results.Add(" AND a.\"id\" not in ( "); - results.Add(" SELECT \"id\" "); - results.Add(" FROM assets b "); + internal static string SetAssetArchived(float lowestVersionHistory, string deviceAssetIds) { // cSpell:disable + List results = [ + " INSERT INTO tag_asset ", + " (\"assetsId\", \"tagsId\") ", + " SELECT a.\"id\", @tagsId::uuid " + ]; + if (lowestVersionHistory <= 1.129) { + results.Add(" FROM assets a "); + } else { + results.Add(" FROM public.asset a "); + } + results.AddRange([ + " WHERE a.\"type\" = 'IMAGE' ", + " AND a.\"ownerId\" = @ownerId ", + " AND a.\"deviceAssetId\" in ( ", + deviceAssetIds, + " ) ", + " AND a.\"id\" not in ( ", + " SELECT \"id\" " + ]); + if (lowestVersionHistory <= 1.129) { + results.Add(" FROM assets b "); + } else { + results.Add(" FROM public.asset b "); + } results.Add(" INNER "); - results.Add(" JOIN tag_asset t "); - results.Add(" ON b.\"id\" = t.\"assetsId\" "); - results.Add(" WHERE t.\"tagsId\" = @tagsId::uuid "); - results.Add(" AND b.\"deviceAssetId\" in ( "); - results.Add(deviceAssetIds); - results.Add(" ) "); - results.Add(" ) "); + if (lowestVersionHistory <= 1.129) { + results.Add(" JOIN tag_asset t "); + } else { + results.Add(" JOIN public.tag_asset t "); + } + results.AddRange([ + " ON b.\"id\" = t.\"assetsId\" ", + " WHERE t.\"tagsId\" = @tagsId::uuid ", + " AND b.\"deviceAssetId\" in ( ", + deviceAssetIds, + " ) ", + " ); " + ]); return string.Join(Environment.NewLine, results); } // cSpell:enable - internal static string GetArchivedTag() - { // cSpell:disable - List results = new(); - results.Add(" SELECT json_agg(t) "); - results.Add(" FROM tags t "); - results.Add($" WHERE t.\"value\" = @value "); - results.Add(" AND t.\"userId\" = @userId "); + internal static string GetArchivedTag(float lowestVersionHistory) { // cSpell:disable + List results = [ + " SELECT json_agg(t) " + ]; + if (lowestVersionHistory <= 1.129) { + results.Add(" FROM tags t "); + } else { + results.Add(" FROM public.tag t "); + } + results.AddRange([ + " WHERE t.\"value\" = @value ", + " AND t.\"userId\" = @userId; " + ]); return string.Join(Environment.NewLine, results); } // cSpell:enable