Compare commits
5 Commits
2391462500
...
main
Author | SHA1 | Date | |
---|---|---|---|
ab90adee42 | |||
ac4e4c277d | |||
0e03b784a2 | |||
590b1b87ca | |||
c8325aafca |
8
.vscode/bash.md
vendored
8
.vscode/bash.md
vendored
@ -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
22
.vscode/launch.json
vendored
@ -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
71
.vscode/settings.json
vendored
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
78
.vscode/tasks.json
vendored
78
.vscode/tasks.json
vendored
@ -88,6 +88,84 @@
|
|||||||
],
|
],
|
||||||
"problemMatcher": "$msCompile"
|
"problemMatcher": "$msCompile"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "podman-login",
|
||||||
|
"command": "podman",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"login",
|
||||||
|
"gitea.phares.duckdns.org:443"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "podman-compose-up-build",
|
||||||
|
"command": "podman",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"compose",
|
||||||
|
"up",
|
||||||
|
"--build"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "podman-build",
|
||||||
|
"command": "podman",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"-t",
|
||||||
|
"immich-to-slideshow",
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "podman-image-list",
|
||||||
|
"command": "podman",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"image",
|
||||||
|
"ls"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "podman-run",
|
||||||
|
"command": "podman",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"-p",
|
||||||
|
"5001:5001",
|
||||||
|
"--name",
|
||||||
|
"immich-to-slideshow_webapp",
|
||||||
|
"a3de856b5731"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "podman-tag",
|
||||||
|
"command": "podman",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"tag",
|
||||||
|
"asdf",
|
||||||
|
"gitea.phares.duckdns.org:443/phares3757/immich-to-slideshow:latest"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "podman-push",
|
||||||
|
"command": "podman",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"push",
|
||||||
|
"gitea.phares.duckdns.org:443/phares3757/immich-to-slideshow:latest"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "Publish AOT",
|
"label": "Publish AOT",
|
||||||
"command": "dotnet",
|
"command": "dotnet",
|
||||||
|
@ -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,14 +17,9 @@ 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
|
||||||
COPY --from=publish /app/publish .
|
COPY --from=publish /app/publish .
|
||||||
ENTRYPOINT [ "dotnet", "ImmichToSlideshow.dll" ]
|
ENTRYPOINT [ "dotnet", "ImmichToSlideshow.dll" ]
|
||||||
|
|
||||||
# docker build -t sprint-console-001 .
|
|
||||||
# docker images ls | grep -i 001
|
|
||||||
# docker run -p 5001:5001 --name sprint-console-api-001 sprint-console-001
|
|
||||||
# docker run -p 5001:5001 --name sprint-console-api-001 sprint-console-001
|
|
@ -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
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"rollForward": "latestMinor",
|
"rollForward": "latestMinor",
|
||||||
"version": "8.0.100"
|
"version": "9.0.100"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,39 @@
|
|||||||
@immich_to_slideshow_HostAddress = http://localhost:5243
|
GET {{host}}/api/v1/assets/columns/
|
||||||
|
Accept: application/json
|
||||||
GET {{immich_to_slideshow_HostAddress}}/weatherforecast/
|
|
||||||
|
###
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
GET {{host}}/api/v1/assets/{{ownerId}}/set-archive-immich/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
GET {{host}}/api/v1/assets/{{ownerId}}/archived-tag/
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -1 +0,0 @@
|
|||||||
GET www.google.com
|
|
@ -1,93 +1,46 @@
|
|||||||
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}/archived-tag")]
|
||||||
return CreatedAtAction(
|
public IActionResult GetArchivedTag(Guid ownerId) =>
|
||||||
actionName: nameof(Get),
|
Content(_AssetService.GetArchivedTag(ownerId), _ContentType);
|
||||||
routeValues: new { AssetId = asset.Id },
|
|
||||||
value: assetResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{assetId:guid}")]
|
[HttpGet("{ownerId:guid}/save-random-paths")]
|
||||||
public IActionResult Get(Guid assetId)
|
public IActionResult SaveRandomPaths(Guid ownerId) =>
|
||||||
{
|
Content(_AssetService.SaveRandomPaths(ownerId), _ContentType);
|
||||||
//get the asset
|
|
||||||
Asset? asset = _AssetService.Get(assetId);
|
|
||||||
|
|
||||||
// mapping to external representation
|
[HttpGet("{ownerId:guid}/sync-immich")]
|
||||||
AssetResponse? assetResponse = AssetResponse.FromDomain(asset);
|
public IActionResult SyncImmich(Guid ownerId) =>
|
||||||
|
Ok(_AssetService.SyncImmich(ownerId));
|
||||||
|
|
||||||
// return 200 ok response
|
[HttpGet("{ownerId:guid}/set-archive-immich")]
|
||||||
return assetResponse is null
|
public IActionResult SetArchiveImmich(Guid ownerId) =>
|
||||||
? Problem(statusCode: StatusCodes.Status404NotFound, detail: $"Asset not found {assetId}")
|
Ok(_AssetService.SetArchiveImmich(ownerId));
|
||||||
: 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();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -3,8 +3,18 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace ImmichToSlideshow.Models;
|
namespace ImmichToSlideshow.Models;
|
||||||
|
|
||||||
public record AppSettings(string Company,
|
public record AppSettings(int AddDays,
|
||||||
|
string ArchivedTag,
|
||||||
|
string Company,
|
||||||
string ConnectionString,
|
string ConnectionString,
|
||||||
|
string CurrentCommit,
|
||||||
|
string CurrentResultsDirectory,
|
||||||
|
string[] FilterTags,
|
||||||
|
string ImmichUploadDirectory,
|
||||||
|
string RandomResultsDirectory,
|
||||||
|
string SyncDirectory,
|
||||||
|
string URLs,
|
||||||
|
string[] WithOrigins,
|
||||||
string WorkingDirectoryName)
|
string WorkingDirectoryName)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -6,8 +6,18 @@ namespace ImmichToSlideshow.Models.Binder;
|
|||||||
public class AppSettings
|
public class AppSettings
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public int? AddDays { get; set; }
|
||||||
|
public string? ArchivedTag { get; set; }
|
||||||
public string? Company { get; set; }
|
public string? Company { get; set; }
|
||||||
public string? ConnectionString { 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 string? WorkingDirectoryName { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
@ -36,11 +46,31 @@ 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?.ArchivedTag is null) throw new NullReferenceException(nameof(appSettings.ArchivedTag));
|
||||||
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?.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));
|
if (appSettings?.WorkingDirectoryName is null) throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName));
|
||||||
result = new(appSettings.Company,
|
result = new(appSettings.AddDays.Value,
|
||||||
|
appSettings.ArchivedTag,
|
||||||
|
appSettings.Company,
|
||||||
appSettings.ConnectionString,
|
appSettings.ConnectionString,
|
||||||
|
appSettings.CurrentCommit,
|
||||||
|
appSettings.CurrentResultsDirectory,
|
||||||
|
appSettings.FilterTags,
|
||||||
|
appSettings.ImmichUploadDirectory,
|
||||||
|
appSettings.RandomResultsDirectory,
|
||||||
|
appSettings.SyncDirectory,
|
||||||
|
appSettings.URLs,
|
||||||
|
appSettings.WithOrigins,
|
||||||
appSettings.WorkingDirectoryName);
|
appSettings.WorkingDirectoryName);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
40
src/ImmichToSlideshow/Models/Identifier.cs
Normal file
40
src/ImmichToSlideshow/Models/Identifier.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ImmichToSlideshow.Models;
|
||||||
|
|
||||||
|
public sealed record Identifier(string[] DirectoryNames,
|
||||||
|
string Extension,
|
||||||
|
bool? HasDateTimeOriginal,
|
||||||
|
int Id,
|
||||||
|
long Length,
|
||||||
|
string PaddedId,
|
||||||
|
long Ticks)
|
||||||
|
{
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
string result = JsonSerializer.Serialize(this, IdentifierSourceGenerationContext.Default.Identifier);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetDeviceAssetIds(ReadOnlyCollection<Identifier> identifiers) =>
|
||||||
|
$"'{string.Join($"',{Environment.NewLine}'", (from l in identifiers select GetDeviceAssetId(l)).ToArray())}'";
|
||||||
|
|
||||||
|
internal static string GetDeviceAssetId(Identifier identifier) =>
|
||||||
|
$"{identifier.PaddedId}{identifier.Extension}";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(Identifier))]
|
||||||
|
public partial class IdentifierSourceGenerationContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(Identifier[]))]
|
||||||
|
public partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
@ -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)]
|
||||||
|
34
src/ImmichToSlideshow/Models/Immich/Tag.cs
Normal file
34
src/ImmichToSlideshow/Models/Immich/Tag.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace ImmichToSlideshow.Models.Immich;
|
||||||
|
|
||||||
|
public record Tag([property: JsonPropertyName("id")] string Id,
|
||||||
|
[property: JsonPropertyName("userId")] string UserId,
|
||||||
|
[property: JsonPropertyName("value")] string OwnerId,
|
||||||
|
// [property: JsonPropertyName("createdAt")] DateTime CreatedAt,
|
||||||
|
// [property: JsonPropertyName("updatedAt")] DateTime UpdatedAt,
|
||||||
|
// [property: JsonPropertyName("color")] char Color,
|
||||||
|
// [property: JsonPropertyName("parentId")] string ParentId,
|
||||||
|
[property: JsonPropertyName("updateId")] string UpdateId)
|
||||||
|
{
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
string result = JsonSerializer.Serialize(this, TagSourceGenerationContext.Default.Tag);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(Tag))]
|
||||||
|
internal partial class TagSourceGenerationContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||||
|
[JsonSerializable(typeof(Tag[]))]
|
||||||
|
internal partial class TagCollectionSourceGenerationContext : JsonSerializerContext
|
||||||
|
{
|
||||||
|
}
|
@ -2,21 +2,25 @@ using ImmichToSlideshow.DependencyInjection;
|
|||||||
using ImmichToSlideshow.Models;
|
using ImmichToSlideshow.Models;
|
||||||
using ImmichToSlideshow.RequestPipeline;
|
using ImmichToSlideshow.RequestPipeline;
|
||||||
|
|
||||||
WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args);
|
namespace ImmichToSlideshow;
|
||||||
|
|
||||||
|
public class Program
|
||||||
{
|
{
|
||||||
// configure services (DI)
|
|
||||||
_ = webApplicationBuilder.Services.AddServices();
|
public static void Main(string[] args)
|
||||||
_ = webApplicationBuilder.Services.AddControllers();
|
{
|
||||||
_ = webApplicationBuilder.Configuration.AddUserSecrets<Program>();
|
WebApplicationBuilder webApplicationBuilder = WebApplication.CreateBuilder(args);
|
||||||
AppSettings appSettings = ImmichToSlideshow.Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration);
|
_ = webApplicationBuilder.Configuration.AddUserSecrets<Program>();
|
||||||
_ = webApplicationBuilder.Services.AddSingleton(_ => appSettings);
|
AppSettings appSettings = Models.Binder.AppSettings.Get(webApplicationBuilder.Configuration);
|
||||||
|
_ = webApplicationBuilder.Services.AddControllers();
|
||||||
|
_ = webApplicationBuilder.Services.AddServices(appSettings);
|
||||||
|
WebApplication webApplication = webApplicationBuilder.Build();
|
||||||
|
ILogger<Program>? logger = webApplication.Services.GetRequiredService<ILogger<Program>>();
|
||||||
|
logger.LogInformation("Starting Web Application");
|
||||||
|
_ = webApplication.InitializeCorsAndHttps(appSettings);
|
||||||
|
_ = webApplication.MapControllers();
|
||||||
|
_ = webApplication.InitializeDatabase();
|
||||||
|
webApplication.Run();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
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();
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
using ImmichToSlideshow.Models.Immich;
|
|
||||||
using ImmichToSlideshow.Models;
|
using ImmichToSlideshow.Models;
|
||||||
|
using ImmichToSlideshow.Models.Immich;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
@ -8,54 +8,15 @@ 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;
|
#pragma warning disable CS9124
|
||||||
|
private readonly ILogger<Program> _Logger = logger;
|
||||||
|
private readonly AppSettings _AppSettings = appSettings;
|
||||||
|
#pragma warning restore CS9124
|
||||||
|
|
||||||
public AssetService(AppSettings appSettings) =>
|
private static int? ExecuteNonQuery(string connectionString, string commandText, ReadOnlyCollection<NpgsqlParameter> npgsqlParameters)
|
||||||
_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;
|
int? result;
|
||||||
if (string.IsNullOrEmpty(connectionString))
|
if (string.IsNullOrEmpty(connectionString))
|
||||||
@ -65,59 +26,206 @@ public class AssetService
|
|||||||
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);
|
||||||
|
npgsqlCommand.Parameters.AddRange(npgsqlParameters.ToArray());
|
||||||
result = npgsqlCommand.ExecuteNonQuery();
|
result = npgsqlCommand.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StringBuilder GetForJsonPath(string connectionString, string commandText)
|
private static StringBuilder GetForJsonPath(string connectionString, string commandText, ReadOnlyCollection<NpgsqlParameter> npgsqlParameters)
|
||||||
{
|
{
|
||||||
StringBuilder stringBuilder = new();
|
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);
|
||||||
|
npgsqlCommand.Parameters.AddRange(npgsqlParameters.ToArray());
|
||||||
NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader(CommandBehavior.SequentialAccess);
|
NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader(CommandBehavior.SequentialAccess);
|
||||||
while (npgsqlDataReader.Read())
|
while (npgsqlDataReader.Read())
|
||||||
_ = stringBuilder.Append(npgsqlDataReader.GetString(0));
|
_ = result.Append(npgsqlDataReader.GetString(0));
|
||||||
return stringBuilder;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyCollection<Asset>? Get()
|
public string? GetColumns()
|
||||||
{
|
{
|
||||||
string commandText = GetCommandText();
|
string result;
|
||||||
if (commandText.Length == 1)
|
NpgsqlParameter[] npgsqlParameters = [];
|
||||||
{
|
string commandText = CommandText.GetColumns();
|
||||||
int? result = ExecuteNonQuery(_AppSettings.ConnectionString, commandText);
|
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
|
||||||
if (result is null)
|
result = stringBuilder.ToString();
|
||||||
{ }
|
if (result.Length == 1)
|
||||||
}
|
File.WriteAllText(".vscode/jsonl/.jsonl", result);
|
||||||
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText);
|
return result;
|
||||||
if (commandText.Length == 1)
|
}
|
||||||
File.WriteAllText(".vscode/jsonl/.jsonl", stringBuilder.ToString());
|
|
||||||
string json = stringBuilder.ToString();
|
public string? GetOwnerIds()
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
NpgsqlParameter[] npgsqlParameters = [];
|
||||||
|
string commandText = CommandText.GetOwnerIdActiveImage();
|
||||||
|
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
|
||||||
|
result = stringBuilder.ToString();
|
||||||
|
if (result.Length == 1)
|
||||||
|
File.WriteAllText(".vscode/jsonl/.jsonl", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
result = stringBuilder.ToString();
|
||||||
|
if (result.Length == 1)
|
||||||
|
File.WriteAllText(".vscode/jsonl/assets.jsonl", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 Tag[]? GetArchivedTag(AppSettings appSettings, 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,
|
Tag[] results;
|
||||||
if (asset is null)
|
Guid userId = ownerId;
|
||||||
throw new ArgumentNullException(nameof(asset));
|
string value = appSettings.ArchivedTag;
|
||||||
|
NpgsqlParameter[] npgsqlParameters =
|
||||||
// User user = _UsersRepository.Find(x => x.Id == userId)
|
[
|
||||||
// ?? throw new InvalidOperationException();
|
new NpgsqlParameter(nameof(value), value),
|
||||||
|
new NpgsqlParameter(nameof(userId), userId),
|
||||||
// user.AddAsset(asset);
|
];
|
||||||
_AssetsRepository.Add(asset);
|
string commandText = CommandText.GetArchivedTag();
|
||||||
|
StringBuilder stringBuilder = GetForJsonPath(appSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
|
||||||
|
string json = stringBuilder.ToString();
|
||||||
|
results = JsonSerializer.Deserialize(json, TagCollectionSourceGenerationContext.Default.TagArray);
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Asset? Get(Guid assetId) =>
|
public string? GetArchivedTag(Guid ownerId)
|
||||||
_AssetsRepository.Find(l => l.Id == assetId.ToString());
|
{
|
||||||
|
string result;
|
||||||
|
Tag[]? tags = GetArchivedTag(_AppSettings, ownerId);
|
||||||
|
result = tags is null || tags.Length != 1 ? null : tags[0].Id;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? SaveRandomPaths(Guid ownerId)
|
||||||
|
{
|
||||||
|
string? results;
|
||||||
|
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))
|
||||||
|
results = null;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Record(string Source, string Destination);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyCollection<int> SetArchiveImmich(Guid ownerId)
|
||||||
|
{
|
||||||
|
ReadOnlyCollection<int>? results;
|
||||||
|
string checkDirectory = Path.Combine(_AppSettings.CurrentResultsDirectory, "B)Metadata", _AppSettings.CurrentCommit, "[]");
|
||||||
|
if (!Directory.Exists(checkDirectory))
|
||||||
|
results = null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string checkFile = Path.Combine(checkDirectory, "!9.json");
|
||||||
|
if (!File.Exists(checkFile))
|
||||||
|
results = null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string json = File.ReadAllText(checkFile);
|
||||||
|
Identifier[]? identifiers = JsonSerializer.Deserialize<Identifier[]>(json);
|
||||||
|
if (identifiers is null || identifiers.Length == 0)
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results?.AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ReadOnlyCollection<int> SetArchiveImmich(ILogger<Program> logger, AppSettings appSettings, Guid ownerId, ReadOnlyCollection<Identifier> identifiers, ReadOnlyCollection<Tag> tags)
|
||||||
|
{
|
||||||
|
ReadOnlyCollection<int>? results;
|
||||||
|
string tagsId = tags[0].Id;
|
||||||
|
NpgsqlParameter[] npgsqlParameters =
|
||||||
|
[
|
||||||
|
new NpgsqlParameter(nameof(tagsId), tagsId),
|
||||||
|
new NpgsqlParameter(nameof(ownerId), ownerId),
|
||||||
|
];
|
||||||
|
string deviceAssetIds = Identifier.GetDeviceAssetIds(identifiers);
|
||||||
|
string commandText = CommandText.SetAssetArchived(deviceAssetIds);
|
||||||
|
logger.LogDebug(commandText.Replace($"@{nameof(tagsId)}", $"'{tagsId}'").Replace($"@{nameof(ownerId)}", $"'{ownerId}'".ToString()));
|
||||||
|
int? result = ExecuteNonQuery(appSettings.ConnectionString, commandText, npgsqlParameters.AsReadOnly());
|
||||||
|
results = result is null ? null : new([result.Value]);
|
||||||
|
return results?.AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
130
src/ImmichToSlideshow/Services/CommandText.cs
Normal file
130
src/ImmichToSlideshow/Services/CommandText.cs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
namespace ImmichToSlideshow.Services;
|
||||||
|
|
||||||
|
internal static class CommandText
|
||||||
|
{
|
||||||
|
|
||||||
|
internal static string GetColumns()
|
||||||
|
{ // cSpell:disable
|
||||||
|
List<string> results = new();
|
||||||
|
// results.Add(" SELECT COALESCE(SUM(checksum_failures), 0) ");
|
||||||
|
// results.Add(" FROM pg_stat_database ");
|
||||||
|
// results.Add(" SELECT json_agg(t) ");
|
||||||
|
// results.Add(" FROM information_schema.tables t ");
|
||||||
|
// results.Add(" WHERE table_schema= 'public' ");
|
||||||
|
// results.Add(" AND table_type= 'BASE TABLE' ");
|
||||||
|
results.Add(" SELECT json_agg(c) ");
|
||||||
|
results.Add(" FROM information_schema.columns c ");
|
||||||
|
// results.Add(" WHERE table_name = 'assets' ");
|
||||||
|
// results.Add(" WHERE table_name = 'libraries' ");
|
||||||
|
results.Add(" WHERE table_name = 'asset_files' ");
|
||||||
|
return string.Join(Environment.NewLine, results);
|
||||||
|
} // cSpell:enable
|
||||||
|
|
||||||
|
internal static string GetOwnerIdActiveImage()
|
||||||
|
{ // cSpell:disable
|
||||||
|
List<string> results = new();
|
||||||
|
results.Add(" SELECT json_agg(j) ");
|
||||||
|
results.Add(" FROM ( ");
|
||||||
|
results.Add(" SELECT a.\"ownerId\" ");
|
||||||
|
results.Add(" FROM assets a ");
|
||||||
|
results.Add(" WHERE a.\"status\" = 'active' ");
|
||||||
|
results.Add(" AND a.\"type\" = 'IMAGE' ");
|
||||||
|
results.Add(" GROUP");
|
||||||
|
results.Add(" BY a.\"ownerId\" ");
|
||||||
|
results.Add(" ) j ");
|
||||||
|
return string.Join(Environment.NewLine, results);
|
||||||
|
} // cSpell:enable
|
||||||
|
|
||||||
|
internal static string GetAssetActiveImagePreviewNotDuplicate(string[] filterTags)
|
||||||
|
{ // cSpell:disable
|
||||||
|
List<string> results = new();
|
||||||
|
results.Add(" SELECT json_agg(j) ");
|
||||||
|
results.Add(" FROM ( ");
|
||||||
|
results.Add(" SELECT a.\"id\" ");
|
||||||
|
results.Add(" , a.\"deviceAssetId\" ");
|
||||||
|
// results.Add(" , a.\"ownerId\" ");
|
||||||
|
// results.Add(" , a.\"deviceId\" ");
|
||||||
|
// results.Add(" , a.\"type\" ");
|
||||||
|
results.Add(" , a.\"originalPath\" ");
|
||||||
|
// results.Add(" , a.\"fileCreatedAt\" ");
|
||||||
|
// results.Add(" , a.\"fileModifiedAt\" ");
|
||||||
|
// results.Add(" , a.\"isFavorite\" ");
|
||||||
|
// results.Add(" , a.\"duration\" ");
|
||||||
|
// results.Add(" , a.\"encodedVideoPath\" ");
|
||||||
|
// results.Add(" , a.\"checksum\" ");
|
||||||
|
// results.Add(" , a.\"isVisible\" ");
|
||||||
|
// results.Add(" , a.\"livePhotoVideoId\" ");
|
||||||
|
// results.Add(" , a.\"updatedAt\" ");
|
||||||
|
// results.Add(" , a.\"createdAt\" ");
|
||||||
|
// results.Add(" , a.\"isArchived\" ");
|
||||||
|
results.Add(" , a.\"originalFileName\" ");
|
||||||
|
// results.Add(" , a.\"sidecarPath\" ");
|
||||||
|
// results.Add(" , a.\"thumbhash\" ");
|
||||||
|
// results.Add(" , a.\"isOffline\" ");
|
||||||
|
// results.Add(" , a.\"libraryId\" ");
|
||||||
|
// results.Add(" , a.\"isExternal\" ");
|
||||||
|
// results.Add(" , a.\"deletedAt\" ");
|
||||||
|
// results.Add(" , a.\"localDateTime\" ");
|
||||||
|
// results.Add(" , a.\"stackId\" ");
|
||||||
|
results.Add(" , a.\"duplicateId\" ");
|
||||||
|
// results.Add(" , a.\"status\" ");
|
||||||
|
results.Add(" , f.\"path\" ");
|
||||||
|
results.Add(" FROM assets a ");
|
||||||
|
results.Add(" INNER ");
|
||||||
|
results.Add(" JOIN asset_files f ");
|
||||||
|
results.Add(" ON a.\"id\" = f.\"assetId\" ");
|
||||||
|
results.Add(" WHERE a.\"status\" = 'active' ");
|
||||||
|
results.Add(" AND a.\"type\" = 'IMAGE' ");
|
||||||
|
results.Add(" AND f.\"type\" = 'preview' ");
|
||||||
|
results.Add(" AND a.\"duplicateId\" is null ");
|
||||||
|
results.Add(" AND a.\"id\" not in ( ");
|
||||||
|
results.Add(" SELECT \"assetsId\" ");
|
||||||
|
results.Add(" FROM tag_asset g ");
|
||||||
|
results.Add(" JOIN tags t ");
|
||||||
|
results.Add(" ON g.\"tagsId\" = t.\"id\" ");
|
||||||
|
results.Add($" WHERE t.\"value\" in ('{string.Join("','", filterTags)}') ");
|
||||||
|
results.Add(" ) ");
|
||||||
|
results.Add(" AND a.\"isExternal\" = true ");
|
||||||
|
results.Add(" AND a.\"isOffline\" = false ");
|
||||||
|
results.Add(" AND a.\"ownerId\" = @ownerId ");
|
||||||
|
results.Add(" ) j ");
|
||||||
|
return string.Join(Environment.NewLine, results);
|
||||||
|
} // cSpell:enable
|
||||||
|
|
||||||
|
internal static string SetAssetArchived(string deviceAssetIds)
|
||||||
|
{ // cSpell:disable
|
||||||
|
List<string> results = new();
|
||||||
|
results.Add(" INSERT INTO tag_asset ");
|
||||||
|
results.Add(" (\"assetsId\", \"tagsId\") ");
|
||||||
|
results.Add(" SELECT a.\"id\", @tagsId::uuid ");
|
||||||
|
results.Add(" FROM assets a ");
|
||||||
|
results.Add(" WHERE a.\"type\" = 'IMAGE' ");
|
||||||
|
results.Add(" AND a.\"ownerId\" = @ownerId ");
|
||||||
|
results.Add(" AND a.\"deviceAssetId\" in ( ");
|
||||||
|
results.Add(deviceAssetIds);
|
||||||
|
results.Add(" ) ");
|
||||||
|
results.Add(" AND a.\"id\" not in ( ");
|
||||||
|
results.Add(" SELECT \"id\" ");
|
||||||
|
results.Add(" FROM assets b ");
|
||||||
|
results.Add(" INNER ");
|
||||||
|
results.Add(" JOIN tag_asset t ");
|
||||||
|
results.Add(" ON b.\"id\" = t.\"assetsId\" ");
|
||||||
|
results.Add(" WHERE t.\"tagsId\" = @tagsId::uuid ");
|
||||||
|
results.Add(" AND b.\"deviceAssetId\" in ( ");
|
||||||
|
results.Add(deviceAssetIds);
|
||||||
|
results.Add(" ) ");
|
||||||
|
results.Add(" ) ");
|
||||||
|
return string.Join(Environment.NewLine, results);
|
||||||
|
} // cSpell:enable
|
||||||
|
|
||||||
|
internal static string GetArchivedTag()
|
||||||
|
{ // cSpell:disable
|
||||||
|
List<string> results = new();
|
||||||
|
results.Add(" SELECT json_agg(t) ");
|
||||||
|
results.Add(" FROM tags t ");
|
||||||
|
results.Add($" WHERE t.\"value\" = @value ");
|
||||||
|
results.Add(" AND t.\"userId\" = @userId ");
|
||||||
|
return string.Join(Environment.NewLine, results);
|
||||||
|
} // cSpell:enable
|
||||||
|
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
Reference in New Issue
Block a user