Compare commits

...

3 Commits

Author SHA1 Message Date
0e03b784a2 save-random-paths
sync-immich
2024-11-23 23:46:24 -07:00
590b1b87ca InitializeCorsAndHttps 2024-11-23 13:46:53 -07:00
c8325aafca Ready to test pulling random assets 2024-11-23 11:53:11 -07:00
20 changed files with 421 additions and 235 deletions

8
.vscode/bash.md vendored
View File

@ -15,3 +15,11 @@ dotnet run --project src/ImmichToSlideshow
```bash 1731643960696 = 638672407606960000 = Thu Nov 14 2024 21:12:40 GMT-0700 (Mountain Standard Time) ```bash 1731643960696 = 638672407606960000 = Thu Nov 14 2024 21:12:40 GMT-0700 (Mountain Standard Time)
docker compose up --build docker compose up --build
``` ```
```bash 1732418534573 = 638680153345730000 = Sat Nov 23 2024 20:22:14 GMT-0700 (Mountain Standard Time)
# docker system prune
docker build .
docker login gitea.phares.duckdns.org:443
docker tag fa136e5bb221 gitea.phares.duckdns.org:443/phares3757/immich-to-slideshow:latest
docker push gitea.phares.duckdns.org:443/phares3757/immich-to-slideshow:latest
```

22
.vscode/launch.json vendored
View File

@ -9,7 +9,7 @@
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
"program": "${workspaceFolder}/src/ImmichToSlideshow/bin/Debug/net8.0/ImmichToSlideshow.dll", "program": "${workspaceFolder}/src/ImmichToSlideshow/bin/Debug/net9.0/ImmichToSlideshow.dll",
"args": [ "args": [
"s", "s",
"test" "test"
@ -28,6 +28,24 @@
"request": "launch", "request": "launch",
"name": "node Launch Current Opened File", "name": "node Launch Current Opened File",
"program": "${file}" "program": "${file}"
} },
{
"name": "Docker",
"type": "coreclr",
"request": "attach",
"sourceFileMap": {
"/app": "${workspaceRoot}"
},
"processId": "${command:pickRemoteProcess}",
"pipeTransport": {
"debuggerPath": "/root/vsdbg/vsdbg",
"pipeProgram": "docker",
"pipeCwd": "${workspaceRoot}",
"quoteArgs": false,
"pipeArgs": [
"exec -i immich-to-slideshow-server"
]
}
},
] ]
} }

71
.vscode/settings.json vendored
View File

@ -11,15 +11,69 @@
}, },
"cSpell.words": [ "cSpell.words": [
"accessibilities", "accessibilities",
"ackages",
"Acks", "Acks",
"aspnet", "aspnet",
"ASPNETCORE", "ASPNETCORE",
"binlog",
"buildhelp",
"cachefile",
"CAXXXX", "CAXXXX",
"checkin",
"codegen",
"coveragexml",
"csdef",
"dbmdl",
"dbproj",
"DENITED", "DENITED",
"dlldata",
"docstates",
"ebug",
"elease",
"eleases",
"esult",
"Fractors",
"gitea",
"Immich", "Immich",
"Infineon", "Infineon",
"Installshield",
"iobj",
"ipch",
"ipdb",
"jmconfig",
"mfractor",
"Npgsql", "Npgsql",
"Thumbhash" "Nsight",
"ntvs",
"NUNIT",
"nupkg",
"nvuser",
"opendb",
"opensdf",
"paket",
"Paket",
"pidb",
"psess",
"PTVS",
"publishproj",
"publishsettings",
"pycache",
"rptproj",
"rsuser",
"schemaview",
"Silverlight",
"svclog",
"Telerik's",
"Thumbhash",
"ublish",
"uild",
"userosscache",
"userprefs",
"vspscc",
"vspx",
"vssscc",
"wwwroot",
"xpress"
], ],
"rest-client.environmentVariables": { "rest-client.environmentVariables": {
"$shared": { "$shared": {
@ -27,8 +81,19 @@
"reviewId": "asdfasdf" "reviewId": "asdfasdf"
}, },
"dev": { "dev": {
"host": "http://localhost:5003", "ownerId": "5f0b1052-466d-44de-a554-226d7256850d",
"token": "ey..dev" "host": "https://localhost:5003",
"token": "ey..dev",
},
"affirm": {
"ownerId": "5f0b1052-466d-44de-a554-226d7256850d",
"host": "https://immich-to-slideshow.affirm.duckdns.org/",
"token": "ey..affirm"
},
"phares": {
"ownerId": "5f0b1052-466d-44de-a554-226d7256850d",
"host": "https://immich-to-slideshow.phares.duckdns.org/",
"token": "ey..phares"
} }
} }
} }

11
.vscode/tasks.json vendored
View File

@ -104,6 +104,17 @@
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
},
{
"label": "docker compose up --build",
"command": "docker",
"type": "process",
"args": [
"compose",
"up",
"--build"
],
"problemMatcher": "$msCompile"
} }
] ]
} }

View File

@ -1,5 +1,5 @@
# Stage 1: Build Stage # Stage 1: Build Stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src WORKDIR /src
@ -17,7 +17,7 @@ FROM build AS publish
RUN dotnet publish 'ImmichToSlideshow.csproj' -c Release -o /app/publish RUN dotnet publish 'ImmichToSlideshow.csproj' -c Release -o /app/publish
# Stage 3: Run Stage # Stage 3: Run Stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0 FROM mcr.microsoft.com/dotnet/aspnet:9.0
ENV ASPNETCORE_HTTP_PORTS=5001 ENV ASPNETCORE_HTTP_PORTS=5001
EXPOSE 5001 EXPOSE 5001
WORKDIR /app WORKDIR /app

View File

@ -1,6 +1,6 @@
services: services:
webapp: webapp:
container_name: immich-to-slideshow-api container_name: immich-to-slideshow
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile

View File

@ -1,6 +1,6 @@
{ {
"sdk": { "sdk": {
"rollForward": "latestMinor", "rollForward": "latestMinor",
"version": "8.0.100" "version": "9.0.100"
} }
} }

View File

@ -1,6 +1,27 @@
@immich_to_slideshow_HostAddress = http://localhost:5243 GET {{host}}/api/v1/assets/columns/
GET {{immich_to_slideshow_HostAddress}}/weatherforecast/
Accept: application/json Accept: application/json
### ###
GET {{host}}/api/v1/assets/owner-ids/
Accept: application/json
###
GET {{host}}/api/v1/assets/{{ownerId}}
Accept: application/json
###
GET {{host}}/api/v1/assets/{{ownerId}}/random-paths/
Accept: application/json
###
GET {{host}}/api/v1/assets/{{ownerId}}/save-random-paths/
Accept: application/json
###
GET {{host}}/api/v1/assets/{{ownerId}}/sync-immich/
Accept: application/json

View File

@ -1 +0,0 @@
GET www.google.com

View File

@ -1,93 +1,38 @@
using ImmichToSlideshow.Models.Immich;
using ImmichToSlideshow.Services; using ImmichToSlideshow.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Collections.ObjectModel;
namespace ImmichToSlideshow.Controllers; namespace ImmichToSlideshow.Controllers;
[ApiController] [ApiController]
[Route("[controller]")] [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; private readonly AssetService _AssetService = assetService;
[HttpGet()] [HttpGet("columns")]
public IActionResult Get() public IActionResult GetColumns() =>
{ Content(_AssetService.GetColumns(), _ContentType);
ReadOnlyCollection<Asset> assets = _AssetService.Get();
AssetResponse?[] assetResponses = AssetResponse.FromDomain(assets);
return Ok(assetResponses);
}
[HttpPost] [HttpGet("owner-ids")]
public IActionResult Create(CreateAssetRequest request) public IActionResult GetOwnerIds() =>
{ Content(_AssetService.GetOwnerIds(), _ContentType);
// mapping to internal representation
Asset asset = request.ToDomain();
// invoke the use case [HttpGet("{ownerId:guid}")]
_AssetService.Create(asset); public IActionResult Get(Guid ownerId) =>
Content(_AssetService.GetAssets(ownerId), _ContentType);
// mapping to external representation [HttpGet("{ownerId:guid}/random-paths")]
AssetResponse assetResponse = AssetResponse.FromDomain(asset); public IActionResult GetRandomPaths(Guid ownerId) =>
Ok(_AssetService.GetRandomPaths(ownerId));
// return 201 created response [HttpGet("{ownerId:guid}/save-random-paths")]
return CreatedAtAction( public IActionResult SaveRandomPaths(Guid ownerId) =>
actionName: nameof(Get), Content(_AssetService.SaveRandomPaths(ownerId), _ContentType);
routeValues: new { AssetId = asset.Id },
value: assetResponse);
}
[HttpGet("{assetId:guid}")] [HttpGet("{ownerId:guid}/sync-immich")]
public IActionResult Get(Guid assetId) public IActionResult SyncImmich(Guid ownerId) =>
{ Ok(_AssetService.SyncImmich(ownerId));
//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

@ -1,3 +1,4 @@
using ImmichToSlideshow.Models;
using ImmichToSlideshow.Services; using ImmichToSlideshow.Services;
namespace ImmichToSlideshow.DependencyInjection; namespace ImmichToSlideshow.DependencyInjection;
@ -5,9 +6,10 @@ namespace ImmichToSlideshow.DependencyInjection;
public static class ServiceCollectionExtensions public static class ServiceCollectionExtensions
{ {
public static IServiceCollection AddServices(this IServiceCollection services) public static IServiceCollection AddServices(this IServiceCollection services, AppSettings appSettings)
{ {
_ = services.AddScoped<AssetService>(); _ = services.AddScoped<AssetService>();
_ = services.AddSingleton(_ => appSettings);
return services; return services;
} }

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<UserSecretsId>cc24ad7a-1d95-4c47-a3ea-0d8475ab06da</UserSecretsId> <UserSecretsId>cc24ad7a-1d95-4c47-a3ea-0d8475ab06da</UserSecretsId>
</PropertyGroup> </PropertyGroup>

View File

@ -3,8 +3,14 @@ using System.Text.Json.Serialization;
namespace ImmichToSlideshow.Models; namespace ImmichToSlideshow.Models;
public record AppSettings(string Company, public record AppSettings(int AddDays,
string Company,
string ConnectionString, string ConnectionString,
string ImmichUploadDirectory,
string RandomResultsDirectory,
string SyncDirectory,
string URLs,
string[] WithOrigins,
string WorkingDirectoryName) string WorkingDirectoryName)
{ {

View File

@ -6,8 +6,14 @@ namespace ImmichToSlideshow.Models.Binder;
public class AppSettings public class AppSettings
{ {
public int? AddDays { get; set; }
public string? Company { get; set; } public string? Company { get; set; }
public string? ConnectionString { get; set; } public string? ConnectionString { 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 string? WorkingDirectoryName { get; set; }
public override string ToString() public override string ToString()
@ -36,11 +42,23 @@ public class AppSettings
private static Models.AppSettings Get(AppSettings? appSettings) private static Models.AppSettings Get(AppSettings? appSettings)
{ {
Models.AppSettings result; Models.AppSettings result;
if (appSettings?.AddDays is null) throw new NullReferenceException(nameof(appSettings.AddDays));
if (appSettings?.Company is null) throw new NullReferenceException(nameof(appSettings.Company)); if (appSettings?.Company is null) throw new NullReferenceException(nameof(appSettings.Company));
if (appSettings?.ConnectionString is null) throw new NullReferenceException(nameof(appSettings.ConnectionString)); if (appSettings?.ConnectionString is null) throw new NullReferenceException(nameof(appSettings.ConnectionString));
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)); if (appSettings?.WorkingDirectoryName is null) throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName));
result = new(appSettings.Company, result = new(appSettings.AddDays.Value,
appSettings.Company,
appSettings.ConnectionString, appSettings.ConnectionString,
appSettings.ImmichUploadDirectory,
appSettings.RandomResultsDirectory,
appSettings.SyncDirectory,
appSettings.URLs,
appSettings.WithOrigins,
appSettings.WorkingDirectoryName); appSettings.WorkingDirectoryName);
return result; return result;
} }

View File

@ -5,32 +5,32 @@ namespace ImmichToSlideshow.Models.Immich;
public record Asset([property: JsonPropertyName("id")] string Id, public record Asset([property: JsonPropertyName("id")] string Id,
[property: JsonPropertyName("deviceAssetId")] string DeviceAssetId, [property: JsonPropertyName("deviceAssetId")] string DeviceAssetId,
[property: JsonPropertyName("ownerId")] string OwnerId, // [property: JsonPropertyName("ownerId")] string OwnerId,
[property: JsonPropertyName("deviceId")] string DeviceId, // [property: JsonPropertyName("deviceId")] string DeviceId,
[property: JsonPropertyName("type")] string Type, // [property: JsonPropertyName("type")] string Type,
[property: JsonPropertyName("originalPath")] string OriginalPath, [property: JsonPropertyName("originalPath")] string OriginalPath,
[property: JsonPropertyName("fileCreatedAt")] DateTime FileCreatedAt, // [property: JsonPropertyName("fileCreatedAt")] DateTime FileCreatedAt,
[property: JsonPropertyName("fileModifiedAt")] DateTime FileModifiedAt, // [property: JsonPropertyName("fileModifiedAt")] DateTime FileModifiedAt,
[property: JsonPropertyName("isFavorite")] bool IsFavorite, // [property: JsonPropertyName("isFavorite")] bool IsFavorite,
[property: JsonPropertyName("duration")] string Duration, // [property: JsonPropertyName("duration")] string? Duration,
[property: JsonPropertyName("encodedVideoPath")] string EncodedVideoPath, // [property: JsonPropertyName("encodedVideoPath")] string EncodedVideoPath,
[property: JsonPropertyName("checksum")] string Checksum, // [property: JsonPropertyName("checksum")] string Checksum,
[property: JsonPropertyName("isVisible")] bool IsVisible, // [property: JsonPropertyName("isVisible")] bool IsVisible,
[property: JsonPropertyName("livePhotoVideoId")] object LivePhotoVideoId, // [property: JsonPropertyName("livePhotoVideoId")] object? LivePhotoVideoId,
[property: JsonPropertyName("updatedAt")] DateTime UpdatedAt, // [property: JsonPropertyName("updatedAt")] DateTime UpdatedAt,
[property: JsonPropertyName("createdAt")] DateTime CreatedAt, // [property: JsonPropertyName("createdAt")] DateTime CreatedAt,
[property: JsonPropertyName("isArchived")] bool IsArchived, // [property: JsonPropertyName("isArchived")] bool IsArchived,
[property: JsonPropertyName("originalFileName")] string OriginalFileName, [property: JsonPropertyName("originalFileName")] string OriginalFileName,
[property: JsonPropertyName("sidecarPath")] object SidecarPath, // [property: JsonPropertyName("sidecarPath")] object? SidecarPath,
[property: JsonPropertyName("thumbhash")] string Thumbhash, // [property: JsonPropertyName("thumbhash")] string Thumbhash,
[property: JsonPropertyName("isOffline")] bool IsOffline, // [property: JsonPropertyName("isOffline")] bool IsOffline,
[property: JsonPropertyName("libraryId")] string LibraryId, // [property: JsonPropertyName("libraryId")] string LibraryId,
[property: JsonPropertyName("isExternal")] bool IsExternal, // [property: JsonPropertyName("isExternal")] bool IsExternal,
[property: JsonPropertyName("deletedAt")] object DeletedAt, // [property: JsonPropertyName("deletedAt")] object? DeletedAt,
[property: JsonPropertyName("localDateTime")] DateTime LocalDateTime, // [property: JsonPropertyName("localDateTime")] DateTime LocalDateTime,
[property: JsonPropertyName("stackId")] object StackId, // [property: JsonPropertyName("stackId")]? object? StackId,
[property: JsonPropertyName("duplicateId")] string DuplicateId, [property: JsonPropertyName("duplicateId")] string? DuplicateId,
[property: JsonPropertyName("status")] string Status, // [property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("path")] string Path) [property: JsonPropertyName("path")] string Path)
{ {
@ -40,13 +40,6 @@ public record Asset([property: JsonPropertyName("id")] string Id,
return result; return result;
} }
public static Asset Get(string id,
string deviceAssetId,
string ownerId,
string originalFileName,
string path) =>
throw new Exception();
} }
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]

View File

@ -2,21 +2,25 @@ using ImmichToSlideshow.DependencyInjection;
using ImmichToSlideshow.Models; using ImmichToSlideshow.Models;
using ImmichToSlideshow.RequestPipeline; using ImmichToSlideshow.RequestPipeline;
namespace ImmichToSlideshow;
public class Program
{
public static void Main(string[] args)
{
WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args); WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args);
{
// configure services (DI)
_ = webApplicationBuilder.Services.AddServices();
_ = webApplicationBuilder.Services.AddControllers();
_ = webApplicationBuilder.Configuration.AddUserSecrets<Program>(); _ = webApplicationBuilder.Configuration.AddUserSecrets<Program>();
AppSettings appSettings = ImmichToSlideshow.Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration); AppSettings appSettings = Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration);
_ = webApplicationBuilder.Services.AddSingleton(_ => appSettings); _ = webApplicationBuilder.Services.AddControllers();
} _ = webApplicationBuilder.Services.AddServices(appSettings);
WebApplication webApplication = webApplicationBuilder.Build(); WebApplication webApplication = webApplicationBuilder.Build();
{
// configure request pipeline
_ = webApplication.MapControllers();
_ = webApplication.InitializeDatabase();
}
ILogger<Program>? logger = webApplication.Services.GetRequiredService<ILogger<Program>>(); ILogger<Program>? logger = webApplication.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Starting Web Application"); logger.LogInformation("Starting Web Application");
_ = webApplication.InitializeCorsAndHttps(appSettings);
_ = webApplication.MapControllers();
_ = webApplication.InitializeDatabase();
webApplication.Run(); webApplication.Run();
}
}

View File

@ -1,12 +1,24 @@
using ImmichToSlideshow.Models;
namespace ImmichToSlideshow.RequestPipeline; namespace ImmichToSlideshow.RequestPipeline;
public static class WebApplicationExtensions public static class WebApplicationExtensions
{ {
public static WebApplication InitializeDatabase(this WebApplication application) public static WebApplication InitializeDatabase(this WebApplication webApplication) =>
{
// DBInitializer.Initialize(application.Configuration[DbConstants.DefaultConnectionStringPath]!); // DBInitializer.Initialize(application.Configuration[DbConstants.DefaultConnectionStringPath]!);
return application; webApplication;
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))
{
_ = webApplication.UseHttpsRedirection();
_ = webApplication.UseHsts();
}
return webApplication;
} }
} }

View File

@ -1,5 +1,6 @@
using ImmichToSlideshow.Models.Immich;
using ImmichToSlideshow.Models; using ImmichToSlideshow.Models;
using ImmichToSlideshow.Models.Immich;
using Microsoft.AspNetCore.Mvc;
using Npgsql; using Npgsql;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Data; using System.Data;
@ -8,17 +9,13 @@ using System.Text.Json;
namespace ImmichToSlideshow.Services; namespace ImmichToSlideshow.Services;
public class AssetService public class AssetService(ILogger<Program> logger, AppSettings appSettings)
{ {
private readonly AppSettings _AppSettings; private readonly ILogger<Program> _Logger = logger;
private readonly AppSettings _AppSettings = appSettings;
public AssetService(AppSettings appSettings) => private static string GetColumnsCommandText()
_AppSettings = appSettings;
private static readonly List<Asset> _AssetsRepository = [];
private static string GetCommandText()
{ // cSpell:disable { // cSpell:disable
List<string> results = new(); List<string> results = new();
// results.Add(" SELECT COALESCE(SUM(checksum_failures), 0) "); // results.Add(" SELECT COALESCE(SUM(checksum_failures), 0) ");
@ -27,97 +24,201 @@ public class AssetService
// results.Add(" FROM information_schema.tables t "); // results.Add(" FROM information_schema.tables t ");
// results.Add(" WHERE table_schema='public' "); // results.Add(" WHERE table_schema='public' ");
// results.Add(" AND table_type='BASE TABLE' "); // results.Add(" AND table_type='BASE TABLE' ");
// results.Add(" SELECT json_agg(c) "); results.Add(" SELECT json_agg(c) ");
// results.Add(" FROM information_schema.columns c "); results.Add(" FROM information_schema.columns c ");
// results.Add(" WHERE table_name ='assets' "); // results.Add(" WHERE table_name ='assets' ");
// results.Add(" WHERE table_name ='libraries' "); // results.Add(" WHERE table_name ='libraries' ");
// results.Add(" WHERE table_name ='asset_files' "); 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(" SELECT json_agg(j) ");
results.Add(" FROM ( "); results.Add(" FROM ( ");
results.Add(" SELECT a.* "); results.Add(" SELECT a.\"ownerId\" ");
results.Add(" , f.\"path\" ");
results.Add(" FROM assets a "); 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 a.\"status\" = 'active' ");
// results.Add(" WHERE f.\"assetId\" = '4c1933ce-f5b3-4348-bcc3-978f99823d70' "); results.Add(" AND a.\"type\" = 'IMAGE' ");
results.Add(" AND a.\"isExternal\" = true "); results.Add(" GROUP");
results.Add(" AND a.\"isOffline\" = false "); results.Add(" BY a.\"ownerId\" ");
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 "); results.Add(" ) j ");
return string.Join(Environment.NewLine, results); return string.Join(Environment.NewLine, results);
} // cSpell:enable } // cSpell:enable
private static int? ExecuteNonQuery(string connectionString, string commandText) private static string GetAssetActiveImagePreviewNotDuplicateCommandText()
{ { // cSpell:disable
int? result; List<string> results = new();
if (string.IsNullOrEmpty(connectionString)) results.Add(" SELECT json_agg(j) ");
result = null; results.Add(" FROM ( ");
else 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.\"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 StringBuilder GetForJsonPath(string connectionString, string commandText, NpgsqlParameter[] npgsqlParameters)
{ {
StringBuilder result = new();
using NpgsqlConnection npgsqlConnection = new(connectionString); using NpgsqlConnection npgsqlConnection = new(connectionString);
npgsqlConnection.Open(); npgsqlConnection.Open();
using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection); using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection);
result = npgsqlCommand.ExecuteNonQuery(); npgsqlCommand.Parameters.AddRange(npgsqlParameters);
} NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader(CommandBehavior.SequentialAccess);
while (npgsqlDataReader.Read())
_ = result.Append(npgsqlDataReader.GetString(0));
return result; return result;
} }
private static StringBuilder GetForJsonPath(string connectionString, string commandText) public string? GetColumns()
{ {
StringBuilder stringBuilder = new(); string result;
using NpgsqlConnection npgsqlConnection = new(connectionString); string commandText = GetColumnsCommandText();
npgsqlConnection.Open(); NpgsqlParameter[] npgsqlParameters = [];
using NpgsqlCommand npgsqlCommand = new(commandText, npgsqlConnection); StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader(CommandBehavior.SequentialAccess); result = stringBuilder.ToString();
while (npgsqlDataReader.Read()) if (result.Length == 1)
_ = stringBuilder.Append(npgsqlDataReader.GetString(0)); File.WriteAllText(".vscode/jsonl/.jsonl", result);
return stringBuilder; return result;
} }
public ReadOnlyCollection<Asset>? Get() public string? GetOwnerIds()
{ {
string commandText = GetCommandText(); string result;
if (commandText.Length == 1) string commandText = GetOwnerIdActiveImageCommandText();
{ NpgsqlParameter[] npgsqlParameters = [];
int? result = ExecuteNonQuery(_AppSettings.ConnectionString, commandText); StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
if (result is null) result = stringBuilder.ToString();
{ } if (result.Length == 1)
File.WriteAllText(".vscode/jsonl/.jsonl", result);
return result;
} }
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText);
if (commandText.Length == 1) public string? GetAssets(Guid ownerId)
File.WriteAllText(".vscode/jsonl/.jsonl", stringBuilder.ToString()); {
string result;
string commandText = GetAssetActiveImagePreviewNotDuplicateCommandText();
NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)];
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
result = stringBuilder.ToString();
if (result.Length == 1)
File.WriteAllText(".vscode/jsonl/assets.jsonl", result);
return result;
}
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 json = stringBuilder.ToString(); string json = stringBuilder.ToString();
Random random = new();
string ownerIdValue = ownerId.ToString();
Asset[]? assets = JsonSerializer.Deserialize(json, AssetCollectionSourceGenerationContext.Default.AssetArray); Asset[]? assets = JsonSerializer.Deserialize(json, AssetCollectionSourceGenerationContext.Default.AssetArray);
return assets?.AsReadOnly(); results = assets is null ? null : (from l in assets orderby random.NextSingle() select l.Path.Split(ownerIdValue)[1]).ToArray();
return results?.AsReadOnly();
} }
// 1. fetch user public string? SaveRandomPaths(Guid ownerId)
// 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, string? results;
if (asset is null) DateTime dateTime = DateTime.Now;
throw new ArgumentNullException(nameof(asset)); string tomorrow = dateTime.AddDays(1).ToString("MM-dd");
if (Directory.Exists(_AppSettings.RandomResultsDirectory))
// User user = _UsersRepository.Find(x => x.Id == userId) _ = Directory.CreateDirectory(_AppSettings.RandomResultsDirectory);
// ?? throw new InvalidOperationException(); FileInfo fileInfo = new(Path.Combine(_AppSettings.RandomResultsDirectory, $"{tomorrow}.json"));
if (fileInfo.Exists && fileInfo.CreationTime > dateTime.AddDays(_AppSettings.AddDays))
// user.AddAsset(asset); results = null;
_AssetsRepository.Add(asset); else
{
_Logger.LogDebug("Writing <{FullName}>", fileInfo.FullName);
ReadOnlyCollection<string>? paths = GetRandomPaths(ownerId);
if (paths is null)
results = null;
else
{
_Logger.LogInformation("{count} path(s)", paths.Count.ToString());
results = JsonSerializer.Serialize(paths);
File.WriteAllText(fileInfo.FullName, results);
}
}
return results;
} }
public Asset? Get(Guid assetId) => private record Record(string Source, string Destination);
_AssetsRepository.Find(l => l.Id == assetId.ToString());
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);
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);
string[] immichUploadCheck = immichUploadFiles.Select(l => l[immichUploadLength..]).ToArray();
for (int i = 0; i < immichUploadFiles.Length; i++)
{
if (syncCheck.Contains(immichUploadCheck[i]))
continue;
results.Add(immichUploadCheck[i]);
record = new(immichUploadFiles[i], string.Concat(_AppSettings.SyncDirectory, immichUploadCheck[i]));
records.Add(record);
}
_Logger.LogInformation("{count} file(s)", results.Count.ToString());
for (int i = 0; i < records.Count; i++)
{
record = records[i];
_Logger.LogDebug("Copying <{source}>", record.Source);
File.Copy(record.Source, record.Destination);
}
return results.AsReadOnly();
}
} }

View File

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

View File

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