diff --git a/.editorconfig b/.editorconfig index cd5d265..39e3dbb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -28,7 +28,7 @@ csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_open_brace = none csharp_new_line_between_query_expression_clauses = true -csharp_prefer_braces = false +csharp_prefer_braces = true csharp_prefer_qualified_reference = true:error csharp_prefer_simple_default_expression = true:warning csharp_prefer_simple_using_statement = true:warning diff --git a/.vscode/launch.json b/.vscode/launch.json index c030b0a..23e3866 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": ".NET Core Launch (console)", "type": "coreclr", "request": "launch", - "preLaunchTask": "build", + "preLaunchTask": "Build", "program": "${workspaceFolder}/src/ImmichToSlideshow/bin/Debug/net9.0/ImmichToSlideshow.dll", "args": [ "s", diff --git a/.vscode/settings.json b/.vscode/settings.json index 5a34a0a..3568a0b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,100 +1,107 @@ { - "[markdown]": { - "editor.wordWrap": "off" + "files.associations": { + "*.ffs_gui": "xml", + "*.hurl": "http", + "*.org": "ini", + "*.net": "ini", + "default": "ini" + }, + "[markdown]": { + "editor.wordWrap": "off" + }, + "files.exclude": { + "**/.git": false, + "**/node_modules": true + }, + "files.watcherExclude": { + "**/node_modules": true + }, + "cSpell.words": [ + "accessibilities", + "ackages", + "Acks", + "aspnet", + "ASPNETCORE", + "binlog", + "buildhelp", + "cachefile", + "CAXXXX", + "checkin", + "codegen", + "coveragexml", + "csdef", + "dbmdl", + "dbproj", + "DENITED", + "Digi", + "dlldata", + "docstates", + "ebug", + "elease", + "eleases", + "esult", + "Fractors", + "gitea", + "Immich", + "Infineon", + "Installshield", + "iobj", + "ipch", + "ipdb", + "jmconfig", + "mfractor", + "Npgsql", + "Nsight", + "ntvs", + "NUNIT", + "nupkg", + "nvuser", + "opendb", + "opensdf", + "paket", + "Paket", + "pidb", + "psess", + "PTVS", + "publishproj", + "publishsettings", + "pycache", + "rptproj", + "rsuser", + "schemaview", + "Silverlight", + "svclog", + "Telerik's", + "Thumbhash", + "ublish", + "uild", + "userosscache", + "userprefs", + "vspscc", + "vspx", + "vssscc", + "wwwroot", + "xpress" + ], + "rest-client.environmentVariables": { + "$shared": { + "productId": "asdfasdf", + "reviewId": "asdfasdf" }, - "files.exclude": { - "**/.git": false, - "**/node_modules": true + "dev": { + "ownerId": "5f0b1052-466d-44de-a554-226d7256850d", + "host": "https://localhost:5003", + "token": "ey..dev", }, - "files.watcherExclude": { - "**/node_modules": true + "affirm": { + "ownerId": "5f0b1052-466d-44de-a554-226d7256850d", + "host": "https://immich-to-slideshow.affirm.duckdns.org/", + "token": "ey..affirm" }, - "cSpell.words": [ - "accessibilities", - "ackages", - "Acks", - "aspnet", - "ASPNETCORE", - "binlog", - "buildhelp", - "cachefile", - "CAXXXX", - "checkin", - "codegen", - "coveragexml", - "csdef", - "dbmdl", - "dbproj", - "DENITED", - "Digi", - "dlldata", - "docstates", - "ebug", - "elease", - "eleases", - "esult", - "Fractors", - "gitea", - "Immich", - "Infineon", - "Installshield", - "iobj", - "ipch", - "ipdb", - "jmconfig", - "mfractor", - "Npgsql", - "Nsight", - "ntvs", - "NUNIT", - "nupkg", - "nvuser", - "opendb", - "opensdf", - "paket", - "Paket", - "pidb", - "psess", - "PTVS", - "publishproj", - "publishsettings", - "pycache", - "rptproj", - "rsuser", - "schemaview", - "Silverlight", - "svclog", - "Telerik's", - "Thumbhash", - "ublish", - "uild", - "userosscache", - "userprefs", - "vspscc", - "vspx", - "vssscc", - "wwwroot", - "xpress" - ], - "rest-client.environmentVariables": { - "$shared": { - "productId": "asdfasdf", - "reviewId": "asdfasdf" - }, - "dev": { - "ownerId": "5f0b1052-466d-44de-a554-226d7256850d", - "host": "https://localhost:5003", - "token": "ey..dev", - }, - "affirm": { - "ownerId": "5f0b1052-466d-44de-a554-226d7256850d", - "host": "https://immich-to-slideshow.affirm.duckdns.org/", - "token": "ey..affirm" - }, - "phares": { - "ownerId": "5f0b1052-466d-44de-a554-226d7256850d", - "host": "https://immich-to-slideshow.phares.duckdns.org/", - "token": "ey..phares" - } + "phares": { + "ownerId": "5f0b1052-466d-44de-a554-226d7256850d", + "host": "https://immich-to-slideshow.phares.duckdns.org/", + "token": "ey..phares" } + } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f335709..eefb928 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -43,7 +43,7 @@ "problemMatcher": "$msCompile" }, { - "label": "Format-Whitespaces", + "label": "Format Whitespaces", "command": "dotnet", "type": "process", "args": [ @@ -53,7 +53,7 @@ "problemMatcher": "$msCompile" }, { - "label": "build", + "label": "Build", "command": "dotnet", "type": "process", "args": [ @@ -65,7 +65,7 @@ "problemMatcher": "$msCompile" }, { - "label": "publish", + "label": "Publish", "command": "dotnet", "type": "process", "args": [ @@ -77,7 +77,7 @@ "problemMatcher": "$msCompile" }, { - "label": "watch", + "label": "Watch", "command": "dotnet", "type": "process", "args": [ @@ -89,7 +89,7 @@ "problemMatcher": "$msCompile" }, { - "label": "podman-login", + "label": "Podman Login", "command": "podman", "type": "process", "args": [ @@ -99,7 +99,7 @@ "problemMatcher": "$msCompile" }, { - "label": "podman-compose-up-build", + "label": "Podman Compose Up Build", "command": "podman", "type": "process", "args": [ @@ -110,7 +110,7 @@ "problemMatcher": "$msCompile" }, { - "label": "podman-build", + "label": "Podman Build", "command": "podman", "type": "process", "args": [ @@ -122,7 +122,7 @@ "problemMatcher": "$msCompile" }, { - "label": "podman-image-list", + "label": "Podman Image List", "command": "podman", "type": "process", "args": [ @@ -132,7 +132,7 @@ "problemMatcher": "$msCompile" }, { - "label": "podman-run", + "label": "Podman Run", "command": "podman", "type": "process", "args": [ @@ -146,7 +146,7 @@ "problemMatcher": "$msCompile" }, { - "label": "podman-tag", + "label": "Podman Tag", "command": "podman", "type": "process", "args": [ @@ -157,7 +157,7 @@ "problemMatcher": "$msCompile" }, { - "label": "podman-push", + "label": "Podman Push", "command": "podman", "type": "process", "args": [ diff --git a/requests/ImmichToSlideshow.http b/requests/ImmichToSlideshow.http deleted file mode 100644 index 8099e47..0000000 --- a/requests/ImmichToSlideshow.http +++ /dev/null @@ -1,46 +0,0 @@ -@host = http://0.0.0.0:5001 - -GET {{host}}/api/v1/assets/columns/ -Accept: application/json - -### - -GET {{host}}/api/v1/assets/owner-ids/ -Accept: application/json - -### - -GET {{host}}/api/v1/assets/{{ownerId}} -Accept: application/json - -### - -GET {{host}}/api/v1/assets/{{ownerId}}/random-paths/ -Accept: application/json - -### - -GET {{host}}/api/v1/assets/{{ownerId}}/save-random-paths/ -Accept: application/json - -### - -GET {{host}}/api/v1/assets/{{ownerId}}/sync-immich/ -Accept: application/json - -### - -GET {{host}}/api/v1/assets/{{ownerId}}/set-archive-immich/ -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/requests/ImmichToSlideshow.hurl b/requests/ImmichToSlideshow.hurl new file mode 100644 index 0000000..8ed2c06 --- /dev/null +++ b/requests/ImmichToSlideshow.hurl @@ -0,0 +1,80 @@ +GET http://127.0.0.1:5001/api/v1/assets/columns/ +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### + +GET http://127.0.0.1:5001/api/v1/assets/owner-ids/ +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### + +GET http://127.0.0.1:5001/api/v1/assets/5f0b1052-466d-44de-a554-226d7256850d +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### + +GET http://127.0.0.1:5001/api/v1/assets/5f0b1052-466d-44de-a554-226d7256850d/random-paths/ +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### + +GET http://127.0.0.1:5001/api/v1/assets/5f0b1052-466d-44de-a554-226d7256850d/save-random-paths/ +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### + +GET http://127.0.0.1:5001/api/v1/assets/5f0b1052-466d-44de-a554-226d7256850d/sync-immich/ +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### + +GET http://127.0.0.1:5001/api/v1/assets/5f0b1052-466d-44de-a554-226d7256850d/set-archive-immich/ +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### + +GET http://127.0.0.1:5001/api/v1/assets/5f0b1052-466d-44de-a554-226d7256850d/set-digi-kam-4-archive-immich/ +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### + +GET http://127.0.0.1:5001/api/v1/assets/5f0b1052-466d-44de-a554-226d7256850d/archived-tag/ +Accept: application/json + +HTTP 200 +[Asserts] +header "Content-Type" == "application/json" + +### diff --git a/src/ImmichToSlideshow/Controllers/AssetsController.cs b/src/ImmichToSlideshow/Controllers/AssetsController.cs index 55fd4d0..1b9ceda 100644 --- a/src/ImmichToSlideshow/Controllers/AssetsController.cs +++ b/src/ImmichToSlideshow/Controllers/AssetsController.cs @@ -25,7 +25,7 @@ public class AssetsController(AssetService assetService) : ControllerBase { [HttpGet("{ownerId:guid}/random-paths")] public IActionResult GetRandomPaths(Guid ownerId) => - Ok(_AssetService.GetRandomPaths(ownerId)); + Ok(_AssetService.GetRandomPaths(ownerId, tomorrow: null)); [HttpGet("{ownerId:guid}/archived-tag")] public IActionResult GetArchivedTag(Guid ownerId) => diff --git a/src/ImmichToSlideshow/DependencyInjection/ServiceCollectionExtensions.cs b/src/ImmichToSlideshow/DependencyInjection/ServiceCollectionExtensions.cs index 89bcdb9..931aa89 100644 --- a/src/ImmichToSlideshow/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/ImmichToSlideshow/DependencyInjection/ServiceCollectionExtensions.cs @@ -6,6 +6,7 @@ namespace ImmichToSlideshow.DependencyInjection; public static class ServiceCollectionExtensions { public static IServiceCollection AddServices(this IServiceCollection services, AppSettings appSettings) { + _ = services.AddControllers(); _ = services.AddScoped(); _ = services.AddSingleton(_ => appSettings); return services; diff --git a/src/ImmichToSlideshow/Models/AppSettings.cs b/src/ImmichToSlideshow/Models/AppSettings.cs index e316a2f..a030012 100644 --- a/src/ImmichToSlideshow/Models/AppSettings.cs +++ b/src/ImmichToSlideshow/Models/AppSettings.cs @@ -11,28 +11,45 @@ public record AppSettings(string Company, Settings Settings, string URLs, string } private static void Verify(AppSettings appSettings) { - if (appSettings?.Company is null) + if (appSettings?.Company is null) { throw new NullReferenceException(nameof(Company)); - if (appSettings?.URLs is null) + } + if (appSettings?.URLs is null) { throw new NullReferenceException(nameof(URLs)); - if (appSettings?.WithOrigins is null) + } + if (appSettings?.WithOrigins is null) { throw new NullReferenceException(nameof(WithOrigins)); - if (appSettings?.Settings?.AddDays is null) + } + if (appSettings?.Settings?.AddDays is null) { throw new NullReferenceException(nameof(Settings.AddDays)); - if (appSettings?.Settings?.ArchivedTag is null) + } + if (appSettings?.Settings?.BirthdayFormat is null) { + throw new NullReferenceException(nameof(Settings.BirthdayFormat)); + } + if (appSettings?.Settings?.ArchivedTag is null) { throw new NullReferenceException(nameof(Settings.ArchivedTag)); - if (appSettings?.Settings?.ConnectionString is null) + } + if (appSettings?.Settings?.ConnectionString is null) { throw new NullReferenceException(nameof(Settings.ConnectionString)); - if (appSettings?.Settings?.DigiKam4 is null) + } + if (appSettings?.Settings?.DigiKam4 is null) { throw new NullReferenceException(nameof(Settings.DigiKam4)); - if (appSettings?.Settings?.FilterTags is null) + } + if (appSettings?.Settings?.FilterTags is null) { throw new NullReferenceException(nameof(Settings.FilterTags)); - if (appSettings?.Settings?.ImmichUploadDirectory is null) + } + if (appSettings?.Settings?.ImmichUploadDirectory is null) { throw new NullReferenceException(nameof(Settings.ImmichUploadDirectory)); - if (appSettings?.Settings?.RandomResultsDirectory is null) + } + if (appSettings?.Settings?.People is null) { + throw new NullReferenceException(nameof(Settings.People)); + } + if (appSettings?.Settings?.RandomResultsDirectory is null) { throw new NullReferenceException(nameof(Settings.RandomResultsDirectory)); - if (appSettings?.Settings?.SyncDirectory is null) + } + if (appSettings?.Settings?.SyncDirectory is null) { throw new NullReferenceException(nameof(Settings.SyncDirectory)); + } } public static AppSettings Get(IConfigurationRoot configurationRoot) { @@ -46,10 +63,12 @@ public record AppSettings(string Company, Settings Settings, string URLs, string 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) + if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) { continue; - if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) + } + 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())}"); diff --git a/src/ImmichToSlideshow/Models/Settings.cs b/src/ImmichToSlideshow/Models/Settings.cs index 9333ed5..977c1c7 100644 --- a/src/ImmichToSlideshow/Models/Settings.cs +++ b/src/ImmichToSlideshow/Models/Settings.cs @@ -5,12 +5,14 @@ namespace ImmichToSlideshow.Models; public record Settings(int AddDays, string ArchivedTag, + string BirthdayFormat, string ConnectionString, DigiKam4? DigiKam4, string[] FilterTags, string ImmichUploadDirectory, float LowestVersionHistory, string NotNinePath, + Dictionary People, string RandomResultsDirectory, string SyncDirectory) { diff --git a/src/ImmichToSlideshow/Program.cs b/src/ImmichToSlideshow/Program.cs index 52358ca..f326f8c 100644 --- a/src/ImmichToSlideshow/Program.cs +++ b/src/ImmichToSlideshow/Program.cs @@ -10,7 +10,6 @@ public class Program { WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args); _ = webApplicationBuilder.Configuration.AddUserSecrets(); AppSettings appSettings = AppSettings.Get(webApplicationBuilder.Configuration); - _ = webApplicationBuilder.Services.AddControllers(); _ = webApplicationBuilder.Services.AddServices(appSettings); WebApplication webApplication = webApplicationBuilder.Build(); ILogger? logger = webApplication.Services.GetRequiredService>(); diff --git a/src/ImmichToSlideshow/Services/AssetService.cs b/src/ImmichToSlideshow/Services/AssetService.cs index 9cd047e..cf6b790 100644 --- a/src/ImmichToSlideshow/Services/AssetService.cs +++ b/src/ImmichToSlideshow/Services/AssetService.cs @@ -69,8 +69,9 @@ public class AssetService(ILogger logger, AppSettings appSettings) { public string? GetAssets(Guid ownerId) { string result; + ReadOnlyCollection people = Array.Empty().AsReadOnly(); NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)]; - string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_Settings.LowestVersionHistory, _Settings.FilterTags); + string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_Settings.LowestVersionHistory, _Settings.FilterTags.AsReadOnly(), people); StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); result = stringBuilder.ToString(); if (result.Length == 1) { @@ -79,10 +80,19 @@ public class AssetService(ILogger logger, AppSettings appSettings) { return result; } - public ReadOnlyCollection? GetRandomPaths(Guid ownerId) { + public ReadOnlyCollection? GetRandomPaths(Guid ownerId, string? tomorrow) { string[]? results; NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)]; - string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_Settings.LowestVersionHistory, _Settings.FilterTags); + List people = []; + if (!string.IsNullOrEmpty(tomorrow)) { + foreach (KeyValuePair keyValuePair in _Settings.People) { + if (!keyValuePair.Key.Contains(tomorrow)) { + continue; + } + people.Add($"People/{keyValuePair.Value.Trim('/')}"); + } + } + string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_Settings.LowestVersionHistory, _Settings.FilterTags.AsReadOnly(), people.AsReadOnly()); StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); string json = stringBuilder.ToString(); Random random = new(); @@ -126,7 +136,7 @@ public class AssetService(ILogger logger, AppSettings appSettings) { results = null; } else { _Logger.LogDebug("Writing <{FullName}>", fileInfo.FullName); - ReadOnlyCollection? paths = GetRandomPaths(ownerId); + ReadOnlyCollection? paths = GetRandomPaths(ownerId, tomorrow); if (paths is null) { results = null; } else { diff --git a/src/ImmichToSlideshow/Services/CommandText.cs b/src/ImmichToSlideshow/Services/CommandText.cs index af84293..5131668 100644 --- a/src/ImmichToSlideshow/Services/CommandText.cs +++ b/src/ImmichToSlideshow/Services/CommandText.cs @@ -1,3 +1,5 @@ +using System.Collections.ObjectModel; + namespace ImmichToSlideshow.Services; internal static class CommandText { @@ -40,7 +42,7 @@ internal static class CommandText { return string.Join(Environment.NewLine, results); } // cSpell:enable - internal static string GetAssetActiveImagePreviewNotDuplicate(float lowestVersionHistory, string[] filterTags) { // cSpell:disable + internal static string GetAssetActiveImagePreviewNotDuplicate(float lowestVersionHistory, ReadOnlyCollection filterTags, ReadOnlyCollection people) { // cSpell:disable List results = [ " SELECT json_agg(j) ", " FROM ( ", @@ -79,7 +81,7 @@ internal static class CommandText { } else { results.Add(" FROM public.asset a "); } - results.AddRange(" INNER "); + results.Add(" INNER "); if (lowestVersionHistory <= 1.129) { results.Add(" JOIN asset_files f "); } else { @@ -107,7 +109,28 @@ internal static class CommandText { results.AddRange([ " ON g.\"tagsId\" = t.\"id\" ", $" WHERE t.\"value\" in ('{string.Join("','", filterTags)}') ", - " ) ", + " ) "]); + if (people.Count > 0) { + results.AddRange([ + " AND a.\"id\" 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("','", people)}') ", + " ) "]); + } + results.AddRange([ " AND a.\"isExternal\" = true ", " AND a.\"isOffline\" = false ", " AND a.\"ownerId\" = @ownerId ",