Need to fix date format
This commit is contained in:
93
src/ImmichToSlideshow/Controllers/AssetsController.cs
Normal file
93
src/ImmichToSlideshow/Controllers/AssetsController.cs
Normal 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
13
src/ImmichToSlideshow/Domain/Product.cs
Normal file
13
src/ImmichToSlideshow/Domain/Product.cs
Normal 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
|
||||
|
||||
}
|
14
src/ImmichToSlideshow/Domain/User.cs
Normal file
14
src/ImmichToSlideshow/Domain/User.cs
Normal 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
|
||||
|
||||
}
|
12
src/ImmichToSlideshow/ImmichToSlideshow.csproj
Normal file
12
src/ImmichToSlideshow/ImmichToSlideshow.csproj
Normal 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>
|
23
src/ImmichToSlideshow/Models/AppSettings.cs
Normal file
23
src/ImmichToSlideshow/Models/AppSettings.cs
Normal 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
|
||||
{
|
||||
}
|
2
src/ImmichToSlideshow/Models/Binder/.editorconfig
Normal file
2
src/ImmichToSlideshow/Models/Binder/.editorconfig
Normal file
@ -0,0 +1,2 @@
|
||||
[*.cs]
|
||||
csharp_preserve_single_line_statements = true
|
65
src/ImmichToSlideshow/Models/Binder/AppSettings.cs
Normal file
65
src/ImmichToSlideshow/Models/Binder/AppSettings.cs
Normal 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
|
||||
{
|
||||
}
|
62
src/ImmichToSlideshow/Models/Immich/Asset.cs
Normal file
62
src/ImmichToSlideshow/Models/Immich/Asset.cs
Normal 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
|
||||
{
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace ImmichToSlideshow.Persistence.Database;
|
||||
|
||||
public static class DbConstants
|
||||
{
|
||||
|
||||
public const string DefaultConnectionStringPath = "Database:ConnectionStrings:DefaultConnection";
|
||||
|
||||
}
|
22
src/ImmichToSlideshow/Program.cs
Normal file
22
src/ImmichToSlideshow/Program.cs
Normal 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();
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
123
src/ImmichToSlideshow/Services/AssetService.cs
Normal file
123
src/ImmichToSlideshow/Services/AssetService.cs
Normal 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());
|
||||
|
||||
}
|
8
src/ImmichToSlideshow/appsettings.Development.json
Normal file
8
src/ImmichToSlideshow/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
9
src/ImmichToSlideshow/appsettings.json
Normal file
9
src/ImmichToSlideshow/appsettings.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
Reference in New Issue
Block a user