Need to fix date format

This commit is contained in:
2024-11-18 21:08:13 -07:00
commit 2391462500
32 changed files with 1502 additions and 0 deletions

View File

@ -0,0 +1,93 @@
using ImmichToSlideshow.Models.Immich;
using ImmichToSlideshow.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.ObjectModel;
namespace ImmichToSlideshow.Controllers;
[ApiController]
[Route("[controller]")]
public class AssetsController(AssetService assetService) : ControllerBase
{
private readonly AssetService _AssetService = assetService;
[HttpGet()]
public IActionResult Get()
{
ReadOnlyCollection<Asset> assets = _AssetService.Get();
AssetResponse?[] assetResponses = AssetResponse.FromDomain(assets);
return Ok(assetResponses);
}
[HttpPost]
public IActionResult Create(CreateAssetRequest request)
{
// mapping to internal representation
Asset asset = request.ToDomain();
// invoke the use case
_AssetService.Create(asset);
// mapping to external representation
AssetResponse assetResponse = AssetResponse.FromDomain(asset);
// return 201 created response
return CreatedAtAction(
actionName: nameof(Get),
routeValues: new { AssetId = asset.Id },
value: assetResponse);
}
[HttpGet("{assetId:guid}")]
public IActionResult Get(Guid assetId)
{
//get the asset
Asset? asset = _AssetService.Get(assetId);
// mapping to external representation
AssetResponse? assetResponse = AssetResponse.FromDomain(asset);
// return 200 ok response
return assetResponse is null
? Problem(statusCode: StatusCodes.Status404NotFound, detail: $"Asset not found {assetId}")
: Ok(assetResponse);
}
public record CreateAssetRequest(string Id,
string DeviceAssetId,
string OwnerId,
string OriginalFileName,
string Path)
{
public Asset ToDomain() =>
Asset.Get(id: Id,
deviceAssetId: DeviceAssetId,
ownerId: OwnerId,
originalFileName: OriginalFileName,
path: Path);
}
public record AssetResponse(string Id,
string DeviceAssetId,
string OwnerId,
string OriginalFileName,
string Path)
{
public static AssetResponse? FromDomain(Asset? asset) =>
asset is null ? null : new AssetResponse(
Id: asset.Id,
DeviceAssetId: asset.DeviceAssetId,
OwnerId: asset.OwnerId,
OriginalFileName: asset.OriginalFileName,
Path: asset.Path);
public static AssetResponse?[] FromDomain(IEnumerable<Asset> assets) =>
assets.Select(FromDomain).ToArray();
}
}

View File

@ -0,0 +1,14 @@
using ImmichToSlideshow.Services;
namespace ImmichToSlideshow.DependencyInjection;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddServices(this IServiceCollection services)
{
_ = services.AddScoped<AssetService>();
return services;
}
}

View File

@ -0,0 +1,13 @@
namespace ImmichToSlideshow.Domain;
public class Product
{
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
}

View File

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

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<UserSecretsId>cc24ad7a-1d95-4c47-a3ea-0d8475ab06da</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Npgsql" Version="9.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,23 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ImmichToSlideshow.Models;
public record AppSettings(string Company,
string ConnectionString,
string WorkingDirectoryName)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

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

View File

@ -0,0 +1,65 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ImmichToSlideshow.Models.Binder;
public class AppSettings
{
public string? Company { get; set; }
public string? ConnectionString { 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?.Company is null) throw new NullReferenceException(nameof(appSettings.Company));
if (appSettings?.ConnectionString is null) throw new NullReferenceException(nameof(appSettings.ConnectionString));
if (appSettings?.WorkingDirectoryName is null) throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName));
result = new(appSettings.Company,
appSettings.ConnectionString,
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,62 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ImmichToSlideshow.Models.Immich;
public record Asset([property: JsonPropertyName("id")] string Id,
[property: JsonPropertyName("deviceAssetId")] string DeviceAssetId,
[property: JsonPropertyName("ownerId")] string OwnerId,
[property: JsonPropertyName("deviceId")] string DeviceId,
[property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("originalPath")] string OriginalPath,
[property: JsonPropertyName("fileCreatedAt")] DateTime FileCreatedAt,
[property: JsonPropertyName("fileModifiedAt")] DateTime FileModifiedAt,
[property: JsonPropertyName("isFavorite")] bool IsFavorite,
[property: JsonPropertyName("duration")] string Duration,
[property: JsonPropertyName("encodedVideoPath")] string EncodedVideoPath,
[property: JsonPropertyName("checksum")] string Checksum,
[property: JsonPropertyName("isVisible")] bool IsVisible,
[property: JsonPropertyName("livePhotoVideoId")] object LivePhotoVideoId,
[property: JsonPropertyName("updatedAt")] DateTime UpdatedAt,
[property: JsonPropertyName("createdAt")] DateTime CreatedAt,
[property: JsonPropertyName("isArchived")] bool IsArchived,
[property: JsonPropertyName("originalFileName")] string OriginalFileName,
[property: JsonPropertyName("sidecarPath")] object SidecarPath,
[property: JsonPropertyName("thumbhash")] string Thumbhash,
[property: JsonPropertyName("isOffline")] bool IsOffline,
[property: JsonPropertyName("libraryId")] string LibraryId,
[property: JsonPropertyName("isExternal")] bool IsExternal,
[property: JsonPropertyName("deletedAt")] object DeletedAt,
[property: JsonPropertyName("localDateTime")] DateTime LocalDateTime,
[property: JsonPropertyName("stackId")] object StackId,
[property: JsonPropertyName("duplicateId")] string DuplicateId,
[property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("path")] string Path)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, AssetSourceGenerationContext.Default.Asset);
return result;
}
public static Asset Get(string id,
string deviceAssetId,
string ownerId,
string originalFileName,
string path) =>
throw new Exception();
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Asset))]
internal partial class AssetSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Asset[]))]
internal partial class AssetCollectionSourceGenerationContext : JsonSerializerContext
{
}

View File

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

View File

@ -0,0 +1,22 @@
using ImmichToSlideshow.DependencyInjection;
using ImmichToSlideshow.Models;
using ImmichToSlideshow.RequestPipeline;
WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args);
{
// configure services (DI)
_ = webApplicationBuilder.Services.AddServices();
_ = webApplicationBuilder.Services.AddControllers();
_ = webApplicationBuilder.Configuration.AddUserSecrets<Program>();
AppSettings appSettings = ImmichToSlideshow.Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration);
_ = webApplicationBuilder.Services.AddSingleton(_ => appSettings);
}
WebApplication webApplication = webApplicationBuilder.Build();
{
// configure request pipeline
_ = webApplication.MapControllers();
_ = webApplication.InitializeDatabase();
}
ILogger<Program>? logger = webApplication.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Starting Web Application");
webApplication.Run();

View File

@ -0,0 +1,12 @@
namespace ImmichToSlideshow.RequestPipeline;
public static class WebApplicationExtensions
{
public static WebApplication InitializeDatabase(this WebApplication application)
{
// DBInitializer.Initialize(application.Configuration[DbConstants.DefaultConnectionStringPath]!);
return application;
}
}

View File

@ -0,0 +1,123 @@
using ImmichToSlideshow.Models.Immich;
using ImmichToSlideshow.Models;
using Npgsql;
using System.Collections.ObjectModel;
using System.Data;
using System.Text;
using System.Text.Json;
namespace ImmichToSlideshow.Services;
public class AssetService
{
private readonly AppSettings _AppSettings;
public AssetService(AppSettings appSettings) =>
_AppSettings = appSettings;
private static readonly List<Asset> _AssetsRepository = [];
private static string GetCommandText()
{ // 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' ");
results.Add(" SELECT json_agg(j) ");
results.Add(" FROM ( ");
results.Add(" SELECT a.* ");
results.Add(" , f.\"path\" ");
results.Add(" FROM assets a ");
// results.Add(" FROM asset_files f ");
results.Add(" INNER ");
results.Add(" JOIN asset_files f ");
results.Add(" ON a.\"id\" = f.\"assetId\" ");
results.Add(" AND f.\"type\" = 'preview' ");
results.Add(" WHERE a.\"status\" = 'active' ");
// results.Add(" WHERE f.\"assetId\" = '4c1933ce-f5b3-4348-bcc3-978f99823d70' ");
results.Add(" AND a.\"isExternal\" = true ");
results.Add(" AND a.\"isOffline\" = false ");
results.Add(" AND a.\"isVisible\" = true ");
// results.Add(" AND a.\"id\" = '4c1933ce-f5b3-4348-bcc3-978f99823d70' ");
// results.Add(" AND a.\"originalFileName\"");
// results.Add(" LIKE '%still%' ");
// results.Add(" AND a.\"originalFileName\" = '979270910999.jpg' ");
results.Add(" ) j ");
return string.Join(Environment.NewLine, results);
} // cSpell:enable
private static int? ExecuteNonQuery(string connectionString, string commandText)
{
int? result;
if (string.IsNullOrEmpty(connectionString))
result = null;
else
{
using NpgsqlConnection npgsqlConnection = new(connectionString);
npgsqlConnection.Open();
using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection);
result = npgsqlCommand.ExecuteNonQuery();
}
return result;
}
private static StringBuilder GetForJsonPath(string connectionString, string commandText)
{
StringBuilder stringBuilder = new();
using NpgsqlConnection npgsqlConnection = new(connectionString);
npgsqlConnection.Open();
using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection);
NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader(CommandBehavior.SequentialAccess);
while (npgsqlDataReader.Read())
_ = stringBuilder.Append(npgsqlDataReader.GetString(0));
return stringBuilder;
}
public ReadOnlyCollection<Asset>? Get()
{
string commandText = GetCommandText();
if (commandText.Length == 1)
{
int? result = ExecuteNonQuery(_AppSettings.ConnectionString, commandText);
if (result is null)
{ }
}
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText);
if (commandText.Length == 1)
File.WriteAllText(".vscode/jsonl/.jsonl", stringBuilder.ToString());
string json = stringBuilder.ToString();
Asset[]? assets = JsonSerializer.Deserialize(json, AssetCollectionSourceGenerationContext.Default.AssetArray);
return assets?.AsReadOnly();
}
// 1. fetch user
// 1. fetch asset
// 1. check wether the user reached the
// 1. update the user
// 1. save the asset
public void Create(Asset asset)
{
// Guid userId,
if (asset is null)
throw new ArgumentNullException(nameof(asset));
// User user = _UsersRepository.Find(x => x.Id == userId)
// ?? throw new InvalidOperationException();
// user.AddAsset(asset);
_AssetsRepository.Add(asset);
}
public Asset? Get(Guid assetId) =>
_AssetsRepository.Find(l => l.Id == assetId.ToString());
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}