using ImmichToSlideshow.Domain; using ImmichToSlideshow.Models; using ImmichToSlideshow.Models.Immich; using Npgsql; using System.Collections.ObjectModel; using System.Data; using System.Text; using System.Text.Json; namespace ImmichToSlideshow.Services; public class AssetService(ILogger logger, AppSettings appSettings) { #pragma warning disable CS9124 private readonly ILogger _Logger = logger; private readonly Settings _Settings = appSettings.Settings; #pragma warning restore CS9124 private static int? ExecuteNonQuery(string connectionString, string commandText, ReadOnlyCollection npgsqlParameters) { int? result; if (string.IsNullOrEmpty(connectionString)) { result = null; } else { using NpgsqlConnection npgsqlConnection = new(connectionString); npgsqlConnection.Open(); using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection); npgsqlCommand.Parameters.AddRange(npgsqlParameters.ToArray()); result = npgsqlCommand.ExecuteNonQuery(); } return result; } 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()) { _ = result.Append(npgsqlDataReader.GetString(0)); } return result; } public string? GetColumns() { string result; NpgsqlParameter[] npgsqlParameters = []; string commandText = CommandText.GetColumns(); StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); result = stringBuilder.ToString(); if (result.Length == 1) { File.WriteAllText(".vscode/jsonl/.jsonl", result); } return result; } public string? GetOwnerIds() { string result; NpgsqlParameter[] npgsqlParameters = []; string commandText = CommandText.GetOwnerIdActiveImage(_Settings.LowestVersionHistory); StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); result = stringBuilder.ToString(); if (result.Length == 1) { File.WriteAllText(".vscode/jsonl/.jsonl", result); } return result; } 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.AsReadOnly(), people); StringBuilder stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); result = stringBuilder.ToString(); if (result.Length == 1) { File.WriteAllText(".vscode/jsonl/assets.jsonl", result); } return result; } public ReadOnlyCollection? GetRandomPaths(Guid ownerId, string? monthDay) { string[]? results; string commandText; Random random = new(); List people = []; StringBuilder stringBuilder; if (!string.IsNullOrEmpty(monthDay)) { foreach (KeyValuePair keyValuePair in _Settings.People) { if (!keyValuePair.Key.Contains(monthDay)) { continue; } people.Add($"People/{keyValuePair.Value.Trim('/')}"); } } try { NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)]; commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_Settings.LowestVersionHistory, _Settings.FilterTags.AsReadOnly(), people.AsReadOnly()); stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); } catch (Exception) { people.Clear(); NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)]; commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_Settings.LowestVersionHistory, _Settings.FilterTags.AsReadOnly(), people.AsReadOnly()); stringBuilder = GetForJsonPath(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); } string json = stringBuilder.ToString(); string ownerIdValue = ownerId.ToString(); Asset[]? assets = JsonSerializer.Deserialize(json, AssetCollectionSourceGenerationContext.Default.AssetArray); results = assets is null ? null : (from l in assets orderby random.NextSingle() select l.Path.Split(ownerIdValue)[1]).ToArray(); return results?.AsReadOnly(); } public Tag[]? GetArchivedTag(Settings settings, Guid ownerId) { Tag[]? results; Guid userId = ownerId; string value = settings.ArchivedTag; NpgsqlParameter[] npgsqlParameters = [ new NpgsqlParameter(nameof(value), value), new NpgsqlParameter(nameof(userId), userId), ]; 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(_Settings, ownerId); result = tags is null || tags.Length != 1 ? null : tags[0].Id; return result; } public string? SaveRandomPaths(Guid ownerId, string? monthDay) { string? results = null; FileInfo fileInfo; DateTime dateTime = DateTime.Now; ReadOnlyCollection? paths; bool? check = monthDay is null ? null : monthDay == "00-00"; if (Directory.Exists(_Settings.RandomResultsDirectory)) { _ = Directory.CreateDirectory(_Settings.RandomResultsDirectory); } for (int i = 0; i < 366; i++) { if (check is null || check.Value) { monthDay = i == 0 && check is not null && check.Value ? "02-29" : dateTime.AddDays(i).ToString("MM-dd"); } fileInfo = new(Path.Combine(_Settings.RandomResultsDirectory, $"{monthDay}.json")); if (fileInfo.Exists && fileInfo.CreationTime > dateTime.AddDays(_Settings.AddDays)) { results = null; } else { _Logger.LogDebug("Writing <{FullName}>", fileInfo.FullName); paths = GetRandomPaths(ownerId, monthDay); if (paths is null) { results = null; } else { _Logger.LogInformation("{count} path(s)", paths.Count.ToString()); results = JsonSerializer.Serialize(paths); File.WriteAllText(fileInfo.FullName, results); } } if (check is null || !check.Value) { break; } } return results; } private record Record(string Source, string Destination); public ReadOnlyCollection SyncImmich(Guid ownerId) { List results = []; Record record; List records = []; 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(_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])) { continue; } results.Add(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++) { record = records[i]; _Logger.LogDebug("Copying <{source}>", record.Source); File.Copy(record.Source, record.Destination); } return results.AsReadOnly(); } public ReadOnlyCollection? SetArchiveImmich(Guid ownerId) { ReadOnlyCollection? results; if (!File.Exists(_Settings.NotNinePath)) { results = null; } else { string json = File.ReadAllText(_Settings.NotNinePath); Identifier[]? identifiers = JsonSerializer.Deserialize(json); if (identifiers is null || identifiers.Length == 0) { results = null; } else { Tag[]? tags = GetArchivedTag(_Settings, ownerId); if (tags is null || tags.Length != 1) { results = null; } else { results = SetArchiveImmich(logger, _Settings, ownerId, identifiers.AsReadOnly(), tags.AsReadOnly()); } } } return results?.AsReadOnly(); } private static ReadOnlyCollection? SetArchiveImmich(ILogger logger, Settings settings, Guid ownerId, string tagsId, string deviceAssetIds) { ReadOnlyCollection? results; NpgsqlParameter[] npgsqlParameters = [ new NpgsqlParameter(nameof(tagsId), tagsId), new NpgsqlParameter(nameof(ownerId), ownerId), ]; string commandText = CommandText.SetAssetArchived(settings.LowestVersionHistory, deviceAssetIds); logger.LogDebug(commandText.Replace($"@{nameof(tagsId)}", $"'{tagsId}'").Replace($"@{nameof(ownerId)}", $"'{ownerId}'".ToString())); 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(); } public ReadOnlyCollection? UpdateAssetsSetLocalDateTimeForThreeAndSeven() { ReadOnlyCollection? results; NpgsqlParameter[] npgsqlParameters = []; string commandText = CommandText.UpdateAssetsSetLocalDateTimeForThreeAndSeven(_Settings.LowestVersionHistory); int? result = ExecuteNonQuery(_Settings.ConnectionString, commandText, npgsqlParameters.AsReadOnly()); results = result is null ? null : new([result.Value]); return results?.AsReadOnly(); } }