save-random-paths
sync-immich
This commit is contained in:
parent
590b1b87ca
commit
0e03b784a2
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
|
||||||
|
```
|
||||||
|
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@ -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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -15,3 +15,13 @@ Accept: application/json
|
|||||||
|
|
||||||
GET {{host}}/api/v1/assets/{{ownerId}}/random-paths/
|
GET {{host}}/api/v1/assets/{{ownerId}}/random-paths/
|
||||||
Accept: application/json
|
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
|
@ -27,4 +27,12 @@ public class AssetsController(AssetService assetService) : ControllerBase
|
|||||||
public IActionResult GetRandomPaths(Guid ownerId) =>
|
public IActionResult GetRandomPaths(Guid ownerId) =>
|
||||||
Ok(_AssetService.GetRandomPaths(ownerId));
|
Ok(_AssetService.GetRandomPaths(ownerId));
|
||||||
|
|
||||||
|
[HttpGet("{ownerId:guid}/save-random-paths")]
|
||||||
|
public IActionResult SaveRandomPaths(Guid ownerId) =>
|
||||||
|
Content(_AssetService.SaveRandomPaths(ownerId), _ContentType);
|
||||||
|
|
||||||
|
[HttpGet("{ownerId:guid}/sync-immich")]
|
||||||
|
public IActionResult SyncImmich(Guid ownerId) =>
|
||||||
|
Ok(_AssetService.SyncImmich(ownerId));
|
||||||
|
|
||||||
}
|
}
|
@ -3,8 +3,13 @@ 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[] WithOrigins,
|
||||||
string WorkingDirectoryName)
|
string WorkingDirectoryName)
|
||||||
{
|
{
|
||||||
|
@ -6,8 +6,13 @@ 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[]? WithOrigins { get; set; }
|
||||||
public string? WorkingDirectoryName { get; set; }
|
public string? WorkingDirectoryName { get; set; }
|
||||||
|
|
||||||
@ -37,12 +42,22 @@ 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?.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.WithOrigins,
|
||||||
appSettings.WorkingDirectoryName);
|
appSettings.WorkingDirectoryName);
|
||||||
return result;
|
return result;
|
||||||
|
@ -13,8 +13,11 @@ public static class WebApplicationExtensions
|
|||||||
{
|
{
|
||||||
_ = webApplication.UseCors(corsPolicyBuilder =>
|
_ = webApplication.UseCors(corsPolicyBuilder =>
|
||||||
corsPolicyBuilder.WithOrigins(appSettings.WithOrigins).AllowAnyHeader().AllowAnyMethod());
|
corsPolicyBuilder.WithOrigins(appSettings.WithOrigins).AllowAnyHeader().AllowAnyMethod());
|
||||||
|
if (appSettings.URLs.Contains("https", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
_ = webApplication.UseHttpsRedirection();
|
_ = webApplication.UseHttpsRedirection();
|
||||||
_ = webApplication.UseHsts();
|
_ = webApplication.UseHsts();
|
||||||
|
}
|
||||||
return webApplication;
|
return webApplication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using ImmichToSlideshow.Models;
|
using ImmichToSlideshow.Models;
|
||||||
using ImmichToSlideshow.Models.Immich;
|
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,9 +9,10 @@ using System.Text.Json;
|
|||||||
|
|
||||||
namespace ImmichToSlideshow.Services;
|
namespace ImmichToSlideshow.Services;
|
||||||
|
|
||||||
public class AssetService(AppSettings appSettings)
|
public class AssetService(ILogger<Program> logger, AppSettings appSettings)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
private readonly ILogger<Program> _Logger = logger;
|
||||||
private readonly AppSettings _AppSettings = appSettings;
|
private readonly AppSettings _AppSettings = appSettings;
|
||||||
|
|
||||||
private static string GetColumnsCommandText()
|
private static string GetColumnsCommandText()
|
||||||
@ -97,52 +99,56 @@ public class AssetService(AppSettings appSettings)
|
|||||||
|
|
||||||
private static StringBuilder GetForJsonPath(string connectionString, string commandText, NpgsqlParameter[] npgsqlParameters)
|
private static StringBuilder GetForJsonPath(string connectionString, string commandText, 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);
|
npgsqlCommand.Parameters.AddRange(npgsqlParameters);
|
||||||
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 string? GetColumns()
|
public string? GetColumns()
|
||||||
{
|
{
|
||||||
|
string result;
|
||||||
string commandText = GetColumnsCommandText();
|
string commandText = GetColumnsCommandText();
|
||||||
NpgsqlParameter[] npgsqlParameters = [];
|
NpgsqlParameter[] npgsqlParameters = [];
|
||||||
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
|
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
|
||||||
string json = stringBuilder.ToString();
|
result = stringBuilder.ToString();
|
||||||
if (json.Length == 1)
|
if (result.Length == 1)
|
||||||
File.WriteAllText(".vscode/jsonl/.jsonl", json);
|
File.WriteAllText(".vscode/jsonl/.jsonl", result);
|
||||||
return json;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? GetOwnerIds()
|
public string? GetOwnerIds()
|
||||||
{
|
{
|
||||||
|
string result;
|
||||||
string commandText = GetOwnerIdActiveImageCommandText();
|
string commandText = GetOwnerIdActiveImageCommandText();
|
||||||
NpgsqlParameter[] npgsqlParameters = [];
|
NpgsqlParameter[] npgsqlParameters = [];
|
||||||
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
|
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
|
||||||
string json = stringBuilder.ToString();
|
result = stringBuilder.ToString();
|
||||||
if (json.Length == 1)
|
if (result.Length == 1)
|
||||||
File.WriteAllText(".vscode/jsonl/.jsonl", json);
|
File.WriteAllText(".vscode/jsonl/.jsonl", result);
|
||||||
return json;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? GetAssets(Guid ownerId)
|
public string? GetAssets(Guid ownerId)
|
||||||
{
|
{
|
||||||
|
string result;
|
||||||
string commandText = GetAssetActiveImagePreviewNotDuplicateCommandText();
|
string commandText = GetAssetActiveImagePreviewNotDuplicateCommandText();
|
||||||
NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)];
|
NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)];
|
||||||
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
|
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
|
||||||
string json = stringBuilder.ToString();
|
result = stringBuilder.ToString();
|
||||||
if (json.Length == 1)
|
if (result.Length == 1)
|
||||||
File.WriteAllText(".vscode/jsonl/assets.jsonl", json);
|
File.WriteAllText(".vscode/jsonl/assets.jsonl", result);
|
||||||
return json;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyCollection<string>? GetRandomPaths(Guid ownerId)
|
public ReadOnlyCollection<string>? GetRandomPaths(Guid ownerId)
|
||||||
{
|
{
|
||||||
|
string[]? results;
|
||||||
string commandText = GetAssetActiveImagePreviewNotDuplicateCommandText();
|
string commandText = GetAssetActiveImagePreviewNotDuplicateCommandText();
|
||||||
NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)];
|
NpgsqlParameter[] npgsqlParameters = [new NpgsqlParameter(nameof(ownerId), ownerId)];
|
||||||
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
|
StringBuilder stringBuilder = GetForJsonPath(_AppSettings.ConnectionString, commandText, npgsqlParameters);
|
||||||
@ -150,8 +156,69 @@ public class AssetService(AppSettings appSettings)
|
|||||||
Random random = new();
|
Random random = new();
|
||||||
string ownerIdValue = ownerId.ToString();
|
string ownerIdValue = ownerId.ToString();
|
||||||
Asset[]? assets = JsonSerializer.Deserialize(json, AssetCollectionSourceGenerationContext.Default.AssetArray);
|
Asset[]? assets = JsonSerializer.Deserialize(json, AssetCollectionSourceGenerationContext.Default.AssetArray);
|
||||||
string[]? paths = assets is null ? null : (from l in assets orderby random.NextSingle() select l.Path.Split(ownerIdValue)[1][1..]).ToArray();
|
results = assets is null ? null : (from l in assets orderby random.NextSingle() select l.Path.Split(ownerIdValue)[1]).ToArray();
|
||||||
return paths?.AsReadOnly();
|
return results?.AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user