diff --git a/.vscode/bash.md b/.vscode/bash.md index 07d4f87..ff8ef05 100644 --- a/.vscode/bash.md +++ b/.vscode/bash.md @@ -15,3 +15,11 @@ dotnet run --project src/ImmichToSlideshow ```bash 1731643960696 = 638672407606960000 = Thu Nov 14 2024 21:12:40 GMT-0700 (Mountain Standard Time) docker compose up --build ``` + +```bash 1732418534573 = 638680153345730000 = Sat Nov 23 2024 20:22:14 GMT-0700 (Mountain Standard Time) +# docker system prune +docker build . +docker login gitea.phares.duckdns.org:443 +docker tag fa136e5bb221 gitea.phares.duckdns.org:443/phares3757/immich-to-slideshow:latest +docker push gitea.phares.duckdns.org:443/phares3757/immich-to-slideshow:latest +``` diff --git a/.vscode/launch.json b/.vscode/launch.json index 4cf6e7f..c030b0a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,6 +28,24 @@ "request": "launch", "name": "node Launch Current Opened File", "program": "${file}" - } + }, + { + "name": "Docker", + "type": "coreclr", + "request": "attach", + "sourceFileMap": { + "/app": "${workspaceRoot}" + }, + "processId": "${command:pickRemoteProcess}", + "pipeTransport": { + "debuggerPath": "/root/vsdbg/vsdbg", + "pipeProgram": "docker", + "pipeCwd": "${workspaceRoot}", + "quoteArgs": false, + "pipeArgs": [ + "exec -i immich-to-slideshow-server" + ] + } + }, ] } \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index b5732b4..51082cc 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,6 @@ services: webapp: - container_name: immich-to-slideshow-api + container_name: immich-to-slideshow build: context: . dockerfile: Dockerfile diff --git a/requests/ImmichToSlideshow.http b/requests/ImmichToSlideshow.http index 5d86e9a..3df20d8 100644 --- a/requests/ImmichToSlideshow.http +++ b/requests/ImmichToSlideshow.http @@ -14,4 +14,14 @@ 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 \ No newline at end of file diff --git a/src/ImmichToSlideshow/Controllers/AssetsController.cs b/src/ImmichToSlideshow/Controllers/AssetsController.cs index 6305699..1db6a44 100644 --- a/src/ImmichToSlideshow/Controllers/AssetsController.cs +++ b/src/ImmichToSlideshow/Controllers/AssetsController.cs @@ -27,4 +27,12 @@ public class AssetsController(AssetService assetService) : ControllerBase public IActionResult GetRandomPaths(Guid ownerId) => Ok(_AssetService.GetRandomPaths(ownerId)); + [HttpGet("{ownerId:guid}/save-random-paths")] + public IActionResult SaveRandomPaths(Guid ownerId) => + Content(_AssetService.SaveRandomPaths(ownerId), _ContentType); + + [HttpGet("{ownerId:guid}/sync-immich")] + public IActionResult SyncImmich(Guid ownerId) => + Ok(_AssetService.SyncImmich(ownerId)); + } \ No newline at end of file diff --git a/src/ImmichToSlideshow/Models/AppSettings.cs b/src/ImmichToSlideshow/Models/AppSettings.cs index bee83a2..29d5d13 100644 --- a/src/ImmichToSlideshow/Models/AppSettings.cs +++ b/src/ImmichToSlideshow/Models/AppSettings.cs @@ -3,8 +3,13 @@ using System.Text.Json.Serialization; namespace ImmichToSlideshow.Models; -public record AppSettings(string Company, +public record AppSettings(int AddDays, + string Company, string ConnectionString, + string ImmichUploadDirectory, + string RandomResultsDirectory, + string SyncDirectory, + string URLs, string[] WithOrigins, string WorkingDirectoryName) { diff --git a/src/ImmichToSlideshow/Models/Binder/AppSettings.cs b/src/ImmichToSlideshow/Models/Binder/AppSettings.cs index a3859b1..0a65937 100644 --- a/src/ImmichToSlideshow/Models/Binder/AppSettings.cs +++ b/src/ImmichToSlideshow/Models/Binder/AppSettings.cs @@ -6,8 +6,13 @@ namespace ImmichToSlideshow.Models.Binder; public class AppSettings { + public int? AddDays { get; set; } public string? Company { get; set; } public string? ConnectionString { 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; } @@ -37,12 +42,22 @@ public class AppSettings private static Models.AppSettings Get(AppSettings? appSettings) { Models.AppSettings result; + if (appSettings?.AddDays is null) throw new NullReferenceException(nameof(appSettings.AddDays)); if (appSettings?.Company is null) throw new NullReferenceException(nameof(appSettings.Company)); if (appSettings?.ConnectionString is null) throw new NullReferenceException(nameof(appSettings.ConnectionString)); + 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.Company, + result = new(appSettings.AddDays.Value, + appSettings.Company, appSettings.ConnectionString, + appSettings.ImmichUploadDirectory, + appSettings.RandomResultsDirectory, + appSettings.SyncDirectory, + appSettings.URLs, appSettings.WithOrigins, appSettings.WorkingDirectoryName); return result; diff --git a/src/ImmichToSlideshow/RequestPipeline/WebApplicationExtensions.cs b/src/ImmichToSlideshow/RequestPipeline/WebApplicationExtensions.cs index b7ca600..87fe229 100644 --- a/src/ImmichToSlideshow/RequestPipeline/WebApplicationExtensions.cs +++ b/src/ImmichToSlideshow/RequestPipeline/WebApplicationExtensions.cs @@ -13,8 +13,11 @@ public static class WebApplicationExtensions { _ = webApplication.UseCors(corsPolicyBuilder => corsPolicyBuilder.WithOrigins(appSettings.WithOrigins).AllowAnyHeader().AllowAnyMethod()); - _ = webApplication.UseHttpsRedirection(); - _ = webApplication.UseHsts(); + if (appSettings.URLs.Contains("https", StringComparison.InvariantCultureIgnoreCase)) + { + _ = webApplication.UseHttpsRedirection(); + _ = webApplication.UseHsts(); + } return webApplication; } diff --git a/src/ImmichToSlideshow/Services/AssetService.cs b/src/ImmichToSlideshow/Services/AssetService.cs index 1679ee3..7ca5a19 100644 --- a/src/ImmichToSlideshow/Services/AssetService.cs +++ b/src/ImmichToSlideshow/Services/AssetService.cs @@ -1,5 +1,6 @@ using ImmichToSlideshow.Models; using ImmichToSlideshow.Models.Immich; +using Microsoft.AspNetCore.Mvc; using Npgsql; using System.Collections.ObjectModel; using System.Data; @@ -8,9 +9,10 @@ using System.Text.Json; namespace ImmichToSlideshow.Services; -public class AssetService(AppSettings appSettings) +public class AssetService(ILogger logger, AppSettings appSettings) { + private readonly ILogger _Logger = logger; private readonly AppSettings _AppSettings = appSettings; private static string GetColumnsCommandText() @@ -97,52 +99,56 @@ public class AssetService(AppSettings appSettings) private static StringBuilder GetForJsonPath(string connectionString, string commandText, NpgsqlParameter[] npgsqlParameters) { - StringBuilder stringBuilder = new(); + StringBuilder result = new(); using NpgsqlConnection npgsqlConnection = new(connectionString); npgsqlConnection.Open(); using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection); npgsqlCommand.Parameters.AddRange(npgsqlParameters); NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader(CommandBehavior.SequentialAccess); while (npgsqlDataReader.Read()) - _ = stringBuilder.Append(npgsqlDataReader.GetString(0)); - return stringBuilder; + _ = result.Append(npgsqlDataReader.GetString(0)); + return result; } public string? GetColumns() { + string result; string commandText = GetColumnsCommandText(); NpgsqlParameter[] npgsqlParameters = []; StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters); - string json = stringBuilder.ToString(); - if (json.Length == 1) - File.WriteAllText(".vscode/jsonl/.jsonl", json); - return json; + result = stringBuilder.ToString(); + if (result.Length == 1) + File.WriteAllText(".vscode/jsonl/.jsonl", result); + return result; } public string? GetOwnerIds() { + string result; string commandText = GetOwnerIdActiveImageCommandText(); NpgsqlParameter[] npgsqlParameters = []; StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters); - string json = stringBuilder.ToString(); - if (json.Length == 1) - File.WriteAllText(".vscode/jsonl/.jsonl", json); - return json; + result = stringBuilder.ToString(); + if (result.Length == 1) + File.WriteAllText(".vscode/jsonl/.jsonl", result); + return result; } public string? GetAssets(Guid ownerId) { + string result; string commandText = GetAssetActiveImagePreviewNotDuplicateCommandText(); NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)]; StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters); - string json = stringBuilder.ToString(); - if (json.Length == 1) - File.WriteAllText(".vscode/jsonl/assets.jsonl", json); - return json; + result = stringBuilder.ToString(); + if (result.Length == 1) + File.WriteAllText(".vscode/jsonl/assets.jsonl", result); + return result; } public ReadOnlyCollection? GetRandomPaths(Guid ownerId) { + string[]? results; string commandText = GetAssetActiveImagePreviewNotDuplicateCommandText(); NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)]; StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters); @@ -150,8 +156,69 @@ public class AssetService(AppSettings appSettings) Random random = new(); string ownerIdValue = ownerId.ToString(); Asset[]? assets = JsonSerializer.Deserialize(json, AssetCollectionSourceGenerationContext.Default.AssetArray); - string[]? paths = assets is null ? null : (from l in assets orderby random.NextSingle() select l.Path.Split(ownerIdValue)[1][1..]).ToArray(); - return paths?.AsReadOnly(); + results = assets is null ? null : (from l in assets orderby random.NextSingle() select l.Path.Split(ownerIdValue)[1]).ToArray(); + return results?.AsReadOnly(); + } + + 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)) + results = null; + else + { + _Logger.LogDebug("Writing <{FullName}>", fileInfo.FullName); + ReadOnlyCollection? paths = GetRandomPaths(ownerId); + if (paths is null) + results = null; + else + { + _Logger.LogInformation("{count} path(s)", paths.Count.ToString()); + results = JsonSerializer.Serialize(paths); + File.WriteAllText(fileInfo.FullName, results); + } + } + return results; + } + + private record Record(string Source, string Destination); + + 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); + 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); + string[] immichUploadCheck = immichUploadFiles.Select(l => l[immichUploadLength..]).ToArray(); + 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])); + records.Add(record); + } + _Logger.LogInformation("{count} file(s)", results.Count.ToString()); + for (int i = 0; i < records.Count; i++) + { + record = records[i]; + _Logger.LogDebug("Copying <{source}>", record.Source); + File.Copy(record.Source, record.Destination); + } + return results.AsReadOnly(); } } \ No newline at end of file