Insert into asset tag instead of updating is archived field

Convert to Podman
This commit is contained in:
2025-06-15 18:10:52 -07:00
parent ac4e4c277d
commit ab90adee42
10 changed files with 330 additions and 135 deletions

View File

@ -1,6 +1,5 @@
using ImmichToSlideshow.Models;
using ImmichToSlideshow.Models.Immich;
using Microsoft.AspNetCore.Mvc;
using Npgsql;
using System.Collections.ObjectModel;
using System.Data;
@ -12,107 +11,12 @@ namespace ImmichToSlideshow.Services;
public class AssetService(ILogger<Program> logger, AppSettings appSettings)
{
#pragma warning disable CS9124
private readonly ILogger<Program> _Logger = logger;
private readonly AppSettings _AppSettings = appSettings;
#pragma warning restore CS9124
private static string GetColumnsCommandText()
{ // cSpell:disable
List<string> 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' ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
private static string GetOwnerIdActiveImageCommandText()
{ // cSpell:disable
List<string> 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 ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
private static string GetAssetActiveImagePreviewNotDuplicateCommandText()
{ // cSpell:disable
List<string> 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.\"isArchived\" = false ");
results.Add(" AND a.\"isExternal\" = true ");
results.Add(" AND a.\"isOffline\" = false ");
results.Add(" AND a.\"isVisible\" = true ");
results.Add(" AND a.\"ownerId\" = @ownerId ");
results.Add(" ) j ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
private static string SetAssetArchivedCommandText(string deviceAssetIds)
{ // cSpell:disable
List<string> results = new();
results.Add(" UPDATE assets ");
results.Add(" SET \"isArchived\" = true ");
results.Add(" WHERE \"isArchived\" = false ");
results.Add(" AND \"type\" = 'IMAGE' ");
results.Add(" AND \"ownerId\" = @ownerId ");
results.Add(" AND \"deviceAssetId\" in ( ");
results.Add(deviceAssetIds);
results.Add(" ) ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
private static int? ExecuteNonQuery(string connectionString, string commandText, NpgsqlParameter[] npgsqlParameters)
private static int? ExecuteNonQuery(string connectionString, string commandText, ReadOnlyCollection<NpgsqlParameter> npgsqlParameters)
{
int? result;
if (string.IsNullOrEmpty(connectionString))
@ -122,19 +26,19 @@ public class AssetService(ILogger<Program> logger, AppSettings appSettings)
using NpgsqlConnection npgsqlConnection = new(connectionString);
npgsqlConnection.Open();
using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection);
npgsqlCommand.Parameters.AddRange(npgsqlParameters);
npgsqlCommand.Parameters.AddRange(npgsqlParameters.ToArray());
result = npgsqlCommand.ExecuteNonQuery();
}
return result;
}
private static StringBuilder GetForJsonPath(string connectionString, string commandText, NpgsqlParameter[] npgsqlParameters)
private static StringBuilder GetForJsonPath(string connectionString, string commandText, ReadOnlyCollection<NpgsqlParameter> npgsqlParameters)
{
StringBuilder result = new();
using NpgsqlConnection npgsqlConnection = new(connectionString);
npgsqlConnection.Open();
using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection);
npgsqlCommand.Parameters.AddRange(npgsqlParameters);
npgsqlCommand.Parameters.AddRange(npgsqlParameters.ToArray());
NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader(CommandBehavior.SequentialAccess);
while (npgsqlDataReader.Read())
_ = result.Append(npgsqlDataReader.GetString(0));
@ -144,9 +48,9 @@ public class AssetService(ILogger<Program> logger, AppSettings appSettings)
public string? GetColumns()
{
string result;
string commandText = GetColumnsCommandText();
NpgsqlParameter[] npgsqlParameters = [];
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
string commandText = CommandText.GetColumns();
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
result = stringBuilder.ToString();
if (result.Length == 1)
File.WriteAllText(".vscode/jsonl/.jsonl", result);
@ -156,9 +60,9 @@ public class AssetService(ILogger<Program> logger, AppSettings appSettings)
public string? GetOwnerIds()
{
string result;
string commandText = GetOwnerIdActiveImageCommandText();
NpgsqlParameter[] npgsqlParameters = [];
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
string commandText = CommandText.GetOwnerIdActiveImage();
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
result = stringBuilder.ToString();
if (result.Length == 1)
File.WriteAllText(".vscode/jsonl/.jsonl", result);
@ -168,9 +72,9 @@ public class AssetService(ILogger<Program> logger, AppSettings appSettings)
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 commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_AppSettings.FilterTags);
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
result = stringBuilder.ToString();
if (result.Length == 1)
File.WriteAllText(".vscode/jsonl/assets.jsonl", result);
@ -180,9 +84,9 @@ public class AssetService(ILogger<Program> logger, AppSettings appSettings)
public ReadOnlyCollection<string>? GetRandomPaths(Guid ownerId)
{
string[]? results;
string commandText = GetAssetActiveImagePreviewNotDuplicateCommandText();
NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)];
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
string commandText = CommandText.GetAssetActiveImagePreviewNotDuplicate(_AppSettings.FilterTags);
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
string json = stringBuilder.ToString();
Random random = new();
string ownerIdValue = ownerId.ToString();
@ -191,6 +95,31 @@ public class AssetService(ILogger<Program> logger, AppSettings appSettings)
return results?.AsReadOnly();
}
public Tag[]? GetArchivedTag(AppSettings appSettings, Guid ownerId)
{
Tag[] results;
Guid userId = ownerId;
string value = appSettings.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 json = stringBuilder.ToString();
results = JsonSerializer.Deserialize(json, TagCollectionSourceGenerationContext.Default.TagArray);
return results;
}
public string? GetArchivedTag(Guid ownerId)
{
string result;
Tag[]? tags = GetArchivedTag(_AppSettings, ownerId);
result = tags is null || tags.Length != 1 ? null : tags[0].Id;
return result;
}
public string? SaveRandomPaths(Guid ownerId)
{
string? results;
@ -254,7 +183,7 @@ public class AssetService(ILogger<Program> logger, AppSettings appSettings)
public ReadOnlyCollection<int> SetArchiveImmich(Guid ownerId)
{
List<int> results;
ReadOnlyCollection<int>? results;
string checkDirectory = Path.Combine(_AppSettings.CurrentResultsDirectory, "B)Metadata", _AppSettings.CurrentCommit, "[]");
if (!Directory.Exists(checkDirectory))
results = null;
@ -271,18 +200,32 @@ public class AssetService(ILogger<Program> logger, AppSettings appSettings)
results = null;
else
{
string deviceAssetIds = $"'{string.Join($"',{Environment.NewLine}'", identifiers.Select(l => $"{l.PaddedId}{l.Extension}").ToArray())}'";
string commandText = SetAssetArchivedCommandText(deviceAssetIds);
NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)];
int? result = ExecuteNonQuery(_AppSettings.ConnectionString, commandText, npgsqlParameters);
if (result is null)
Tag[]? tags = GetArchivedTag(_AppSettings, ownerId);
if (tags is null || tags.Length != 1)
results = null;
else
results = [result.Value];
results = SetArchiveImmich(logger, appSettings, ownerId, identifiers.AsReadOnly(), tags.AsReadOnly());
}
}
}
return results?.AsReadOnly();
}
private static ReadOnlyCollection<int> SetArchiveImmich(ILogger<Program> logger, AppSettings appSettings, Guid ownerId, ReadOnlyCollection<Identifier> identifiers, ReadOnlyCollection<Tag> tags)
{
ReadOnlyCollection<int>? results;
string tagsId = tags[0].Id;
NpgsqlParameter[] npgsqlParameters =
[
new NpgsqlParameter(nameof(tagsId), tagsId),
new NpgsqlParameter(nameof(ownerId), ownerId),
];
string deviceAssetIds = Identifier.GetDeviceAssetIds(identifiers);
string commandText = CommandText.SetAssetArchived(deviceAssetIds);
logger.LogDebug(commandText.Replace($"@{nameof(tagsId)}", $"'{tagsId}'").Replace($"@{nameof(ownerId)}", $"'{ownerId}'".ToString()));
int? result = ExecuteNonQuery(appSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
results = result is null ? null : new([result.Value]);
return results?.AsReadOnly();
}
}

View File

@ -0,0 +1,130 @@
namespace ImmichToSlideshow.Services;
internal static class CommandText
{
internal static string GetColumns()
{ // cSpell:disable
List<string> 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' ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
internal static string GetOwnerIdActiveImage()
{ // cSpell:disable
List<string> 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 ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
internal static string GetAssetActiveImagePreviewNotDuplicate(string[] filterTags)
{ // cSpell:disable
List<string> 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 ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
internal static string SetAssetArchived(string deviceAssetIds)
{ // cSpell:disable
List<string> 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 ");
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(" ) ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
internal static string GetArchivedTag()
{ // cSpell:disable
List<string> 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 ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
}