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
This commit is contained in:
2025-07-27 16:03:47 -07:00
parent ab90adee42
commit c4d42d79a0
31 changed files with 644 additions and 458 deletions

View File

@ -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));
}

View File

@ -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<AssetService>();
_ = services.AddSingleton(_ => appSettings);
return services;

View File

@ -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<ImageTag>? Get(string tag, string? tagsPath, string? imageTagsPath, string? imagesPath) {
List<ImageTag>? results;
ReadOnlyCollection<Tags>? tags = Tags.GetTags(tagsPath);
if (tags is null || tags.Count == 0) {
results = null;
} else {
ReadOnlyCollection<int> tagIds = GetTagIds(tag, tags);
if (tagIds.Count == 0) {
results = null;
} else {
ReadOnlyCollection<ImageTags>? imageTags = ImageTags.GetImageTags(imageTagsPath);
if (imageTags is null || imageTags.Count == 0) {
results = null;
} else {
ReadOnlyCollection<int> imageIds = GetImageIds(imageTags, tagIds);
if (imageIds.Count == 0) {
results = null;
} else {
results = [];
long id;
ImageTag imageTag;
ReadOnlyCollection<Images>? 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<int> GetTagIds(string tag, ReadOnlyCollection<Tags> tags) {
List<int> results = [];
foreach (Tags t in tags) {
if (t.Name != tag) {
continue;
}
results.Add(t.Id);
}
return results.AsReadOnly();
}
private static ReadOnlyCollection<int> GetImageIds(ReadOnlyCollection<ImageTags> imageTags, ReadOnlyCollection<int> tagIds) {
List<int> results = [];
foreach (ImageTags i in imageTags) {
if (!tagIds.Contains(i.TagId)) {
continue;
}
results.Add(i.ImageId);
}
return results.AsReadOnly();
}
}

View File

@ -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
}
public record Product(Guid Id,
string Name,
string Category,
string SubCategory);

View File

@ -1,14 +1,11 @@
namespace ImmichToSlideshow.Domain;
public class User
{
// Business concerns
public Guid Id { get; init; } = Guid.NewGuid();
public List<Product> Products { get; init; } = [];
public record User(Guid Id,
List<Product> Products) {
internal void AddProduct(Product product) =>
Products.Add(product);
// Business concerns
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
@ -7,6 +7,7 @@
<UserSecretsId>cc24ad7a-1d95-4c47-a3ea-0d8475ab06da</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Npgsql" Version="9.0.0" />
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="System.Text.Json" Version="9.0.7" />
</ItemGroup>
</Project>

View File

@ -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>();
string? company = configurationRoot.GetSection(nameof(Company)).Get<string>();
Settings? settings = configurationRoot.GetSection(nameof(Settings)).Get<Settings>();
string[]? withOrigins = configurationRoot.GetSection(nameof(WithOrigins)).Get<string[]>();
#pragma warning restore IL3050, IL2026
if (company is null || settings is null || urls is null || withOrigins is null) {
List<string> 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 {
}

View File

@ -1,2 +0,0 @@
[*.cs]
csharp_preserve_single_line_statements = true

View File

@ -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<string> 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<AppSettings>();
#pragma warning restore IL3050, IL2026
PreVerify(configurationRoot, appSettings);
result = Get(appSettings);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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<ImageTags>? GetImageTags(string? path) {
ImageTags[]? results;
if (string.IsNullOrEmpty(path) || !File.Exists(path)) {
results = null;
} else {
string json = File.ReadAllText(path);
results = JsonSerializer.Deserialize<ImageTags[]>(json);
}
return results?.AsReadOnly();
}
}

View File

@ -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<Images>? GetImages(string? path) {
Images[]? results;
if (string.IsNullOrEmpty(path) || !File.Exists(path)) {
results = null;
} else {
string json = File.ReadAllText(path);
results = JsonSerializer.Deserialize<Images[]>(json);
}
return results?.AsReadOnly();
}
}

View File

@ -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);

View File

@ -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<Tags>? GetTags(string? path) {
Tags[]? results;
if (string.IsNullOrEmpty(path) || !File.Exists(path)) {
results = null;
} else {
string json = File.ReadAllText(path);
results = JsonSerializer.Deserialize<Tags[]>(json);
}
return results?.AsReadOnly();
}
}

View File

@ -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 {
}

View File

@ -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<Identifier> identifiers) =>
$"'{string.Join($"',{Environment.NewLine}'", (from l in identifiers select GetDeviceAssetId(l)).ToArray())}'";
internal static string GetDeviceAssetIds(ReadOnlyCollection<ImageTag> 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 {
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -1,7 +1,6 @@
namespace ImmichToSlideshow.Persistence.Database;
public static class DbConstants
{
public static class DbConstants {
public const string DefaultConnectionStringPath = "Database:ConnectionStrings:DefaultConnection";

View File

@ -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<Program>();
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();

View File

@ -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();
}

View File

@ -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<Program> logger, AppSettings appSettings)
{
public class AssetService(ILogger<Program> logger, AppSettings appSettings) {
#pragma warning disable CS9124
private readonly ILogger<Program> _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<NpgsqlParameter> npgsqlParameters)
{
private static int? ExecuteNonQuery(string connectionString, string commandText, ReadOnlyCollection<NpgsqlParameter> 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<Program> logger, AppSettings appSettings)
return result;
}
private static StringBuilder GetForJsonPath(string connectionString, string commandText, ReadOnlyCollection<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.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<string>? GetRandomPaths(Guid ownerId)
{
public ReadOnlyCollection<string>? 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<Program> 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<string>? 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<Program> logger, AppSettings appSettings)
private record Record(string Source, string Destination);
public ReadOnlyCollection<string> SyncImmich(Guid ownerId)
{
public ReadOnlyCollection<string> SyncImmich(Guid ownerId) {
List<string> results = [];
Record record;
List<Record> 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<Program> logger, AppSettings appSettings)
return results.AsReadOnly();
}
public ReadOnlyCollection<int> SetArchiveImmich(Guid ownerId)
{
public ReadOnlyCollection<int>? SetArchiveImmich(Guid ownerId) {
ReadOnlyCollection<int>? 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<Identifier[]>(json);
if (identifiers is null || identifiers.Length == 0) {
results = null;
else
{
string json = File.ReadAllText(checkFile);
Identifier[]? identifiers = JsonSerializer.Deserialize<Identifier[]>(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<int> SetArchiveImmich(ILogger<Program> logger, AppSettings appSettings, Guid ownerId, ReadOnlyCollection<Identifier> identifiers, ReadOnlyCollection<Tag> tags)
{
private static ReadOnlyCollection<int>? SetArchiveImmich(ILogger<Program> logger, Settings settings, Guid ownerId, string tagsId, string deviceAssetIds) {
ReadOnlyCollection<int>? 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<int>? SetArchiveImmich(ILogger<Program> logger, Settings settings, Guid ownerId, ReadOnlyCollection<Identifier> identifiers, ReadOnlyCollection<Tag> tags) {
ReadOnlyCollection<int>? results;
string tagsId = tags[0].Id;
string deviceAssetIds = Identifier.GetDeviceAssetIds(identifiers);
results = SetArchiveImmich(logger, settings, ownerId, tagsId, deviceAssetIds);
return results?.AsReadOnly();
}
public ReadOnlyCollection<int>? SetDigiKam4ArchiveImmich(Guid ownerId) {
ReadOnlyCollection<int>? results;
ReadOnlyCollection<ImageTag>? 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<int>? SetArchiveImmich(ILogger<Program> logger, Settings settings, Guid ownerId, ReadOnlyCollection<ImageTag> imageTags, ReadOnlyCollection<Tag> tags) {
ReadOnlyCollection<int>? results;
string tagsId = tags[0].Id;
string deviceAssetIds = Identifier.GetDeviceAssetIds(imageTags);
results = SetArchiveImmich(logger, settings, ownerId, tagsId, deviceAssetIds);
return results?.AsReadOnly();
}
}

View File

@ -1,129 +1,176 @@
namespace ImmichToSlideshow.Services;
internal static class CommandText
{
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' ");
internal static string GetColumns() { // cSpell:disable
List<string> 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<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 ");
internal static string GetOwnerIdActiveImage(float lowestVersionHistory) { // cSpell:disable
List<string> 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<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 ");
internal static string GetAssetActiveImagePreviewNotDuplicate(float lowestVersionHistory, string[] filterTags) { // cSpell:disable
List<string> 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<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 ");
internal static string SetAssetArchived(float lowestVersionHistory, string deviceAssetIds) { // cSpell:disable
List<string> 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<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 ");
internal static string GetArchivedTag(float lowestVersionHistory) { // cSpell:disable
List<string> 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