immichframe: init at 1.0.29.0, nixos/immichframe: init module (#463563)
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
|
||||
- [knot-resolver](https://www.knot-resolver.cz/) in version 6. Available as `services.knot-resolver`. A module for knot-resolver 5 was already available as `services.kresd`.
|
||||
|
||||
- [ImmichFrame](https://immichframe.dev/), display your photos from Immich as a digital photo frame. Available as `services.immichframe`.
|
||||
|
||||
- [udp-over-tcp](https://github.com/mullvad/udp-over-tcp), a tunnel for proxying UDP traffic over a TCP stream. Available as `services.udp-over-tcp`.
|
||||
|
||||
## Backward Incompatibilities {#sec-release-26.05-incompatibilities}
|
||||
|
||||
@@ -1637,6 +1637,7 @@
|
||||
./services/web-apps/immich-kiosk.nix
|
||||
./services/web-apps/immich-public-proxy.nix
|
||||
./services/web-apps/immich.nix
|
||||
./services/web-apps/immichframe.nix
|
||||
./services/web-apps/invidious.nix
|
||||
./services/web-apps/isso.nix
|
||||
./services/web-apps/jirafeau.nix
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.immichframe;
|
||||
format = pkgs.formats.json { };
|
||||
inherit (lib)
|
||||
types
|
||||
mkIf
|
||||
mkOption
|
||||
mkEnableOption
|
||||
;
|
||||
in
|
||||
{
|
||||
options.services.immichframe = {
|
||||
enable = mkEnableOption "ImmichFrame";
|
||||
package = lib.mkPackageOption pkgs "immichframe" { };
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 3000;
|
||||
description = "The port that ImmichFrame will listen on.";
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
Accounts = mkOption {
|
||||
type = types.listOf (
|
||||
types.submodule {
|
||||
freeformType = format.type;
|
||||
options = {
|
||||
ImmichServerUrl = mkOption {
|
||||
type = types.str;
|
||||
example = "http://photos.example.com";
|
||||
description = "The URL of your Immich server.";
|
||||
};
|
||||
ApiKey = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
API key to talk to the Immich server.
|
||||
Warning: it will be world-readable in /nix/store.
|
||||
Consider using {option}`ApiKeyFile` instead.
|
||||
|
||||
See
|
||||
<https://immichframe.online/docs/getting-started/configuration#api-key-permissions>
|
||||
for details on what permissions this key needs.
|
||||
'';
|
||||
};
|
||||
ApiKeyFile = mkOption {
|
||||
type = types.nullOr types.externalPath;
|
||||
default = null;
|
||||
description = ''
|
||||
File containing an API key to talk to the Immich server.
|
||||
|
||||
See
|
||||
<https://immichframe.online/docs/getting-started/configuration#api-key-permissions>
|
||||
for details on what permissions this key needs.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
description = ''
|
||||
Accounts configuration, multiple are permitted. See
|
||||
<https://immichframe.online/docs/getting-started/configuration>.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
description = ''
|
||||
Configuration for ImmichFrame. See
|
||||
<https://immichframe.online/docs/getting-started/configuration> for
|
||||
options and defaults.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions = lib.imap0 (i: account: {
|
||||
assertion = lib.xor (account.ApiKey == null) (account.ApiKeyFile == null);
|
||||
message = "Exactly one of {option}`services.immichframe.settings.Accounts[${toString i}].ApiKey` and {option}`services.immichframe.settings.Accounts[${toString i}].ApiKeyFile` must be specified";
|
||||
}) cfg.settings.Accounts;
|
||||
|
||||
systemd.services.immichframe =
|
||||
let
|
||||
accountsWithApiKeyFiles = lib.filter (account: account.ApiKeyFile != null) cfg.settings.Accounts;
|
||||
apiKeyFileToId = lib.listToAttrs (
|
||||
lib.imap0 (
|
||||
index: account: lib.nameValuePair account.ApiKeyFile "api-key-${toString index}"
|
||||
) accountsWithApiKeyFiles
|
||||
);
|
||||
settingsPatchWithCredentialPaths = {
|
||||
Accounts = map (
|
||||
account:
|
||||
account
|
||||
// (
|
||||
if account.ApiKeyFile != null then
|
||||
{
|
||||
ApiKeyFile = "/run/credentials/${config.systemd.services.immichframe.name}/${
|
||||
apiKeyFileToId.${account.ApiKeyFile}
|
||||
}";
|
||||
}
|
||||
else
|
||||
{ }
|
||||
)
|
||||
) cfg.settings.Accounts;
|
||||
};
|
||||
settingsWithFixedSecretPaths = lib.recursiveUpdate cfg.settings settingsPatchWithCredentialPaths;
|
||||
in
|
||||
{
|
||||
description = "Display your photos from Immich as a digital photo frame";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
IMMICHFRAME_CONFIG_PATH = pkgs.runCommand "Config" { } ''
|
||||
mkdir $out
|
||||
ln -s ${format.generate "Settings.json" settingsWithFixedSecretPaths} $out/Settings.json
|
||||
'';
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = "${lib.getExe cfg.package} --urls=http://localhost:${toString cfg.port}";
|
||||
LoadCredential = lib.concatMapAttrsStringSep ":" (
|
||||
apiKeyFile: id: "${id}:${apiKeyFile}"
|
||||
) apiKeyFileToId;
|
||||
DynamicUser = true;
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 3;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ jfly ];
|
||||
}
|
||||
@@ -756,6 +756,7 @@ in
|
||||
immich-public-proxy = runTest ./web-apps/immich-public-proxy.nix;
|
||||
immich-vectorchord-migration = runTest ./web-apps/immich-vectorchord-migration.nix;
|
||||
immich-vectorchord-reindex = runTest ./web-apps/immich-vectorchord-reindex.nix;
|
||||
immichframe = runTest ./web-apps/immichframe.nix;
|
||||
incron = runTest ./incron.nix;
|
||||
incus = import ./incus {
|
||||
inherit runTestOn;
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
let
|
||||
apiKeyFile = "/tmp/immich-api.key";
|
||||
customInterval = 5;
|
||||
in
|
||||
{
|
||||
name = "immichframe";
|
||||
|
||||
enableOCR = true;
|
||||
|
||||
nodes.machine =
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [ ../common/x11.nix ];
|
||||
|
||||
# When setting this to 2500 I got "Kernel panic - not syncing: Out of
|
||||
# memory: compulsory panic_on_oom is enabled".
|
||||
virtualisation.memorySize = 3000;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
imagemagick
|
||||
immich-cli
|
||||
firefox
|
||||
xdotool
|
||||
];
|
||||
|
||||
fonts.packages = [ pkgs.liberation_ttf ];
|
||||
|
||||
services.immich = {
|
||||
enable = true;
|
||||
port = 2283;
|
||||
openFirewall = true;
|
||||
# Disable a bunch of features that aren't needed for this test.
|
||||
machine-learning.enable = false;
|
||||
settings = {
|
||||
backup.database.enabled = false;
|
||||
machineLearning.enabled = false;
|
||||
map.enabled = false;
|
||||
reverseGeocoding.enabled = false;
|
||||
metadata.faces.import = false;
|
||||
newVersionCheck.enabled = false;
|
||||
notifications.smtp.enabled = false;
|
||||
};
|
||||
};
|
||||
|
||||
services.immichframe = {
|
||||
enable = true;
|
||||
port = 8002;
|
||||
settings = {
|
||||
General.Interval = customInterval;
|
||||
Accounts = [
|
||||
{
|
||||
ImmichServerUrl = "http://localhost:${toString config.services.immich.port}";
|
||||
ApiKeyFile = apiKeyFile;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = /* python */ ''
|
||||
import json
|
||||
import tempfile
|
||||
from shlex import quote
|
||||
|
||||
custom_interval = ${toString customInterval}
|
||||
|
||||
machine.wait_for_unit("immich-server.service")
|
||||
machine.wait_for_open_port(2283)
|
||||
|
||||
# Log in to Immich and create an access key.
|
||||
machine.succeed("""
|
||||
curl --no-progress-meter -f --json '{ "email": "test@example.com", "name": "Admin", "password": "admin" }' http://localhost:2283/api/auth/admin-sign-up
|
||||
""")
|
||||
res = machine.succeed("""
|
||||
curl --no-progress-meter -f --json '{ "email": "test@example.com", "password": "admin" }' http://localhost:2283/api/auth/login
|
||||
""")
|
||||
token = json.loads(res)['accessToken']
|
||||
res = machine.succeed("""
|
||||
curl --no-progress-meter -f -H 'Cookie: immich_access_token=%s' --json '{ "name": "API Key", "permissions": ["all"] }' http://localhost:2283/api/api-keys
|
||||
""" % token)
|
||||
key = json.loads(res)['secret']
|
||||
machine.succeed(f"immich login http://localhost:2283/api {key}")
|
||||
res = machine.succeed("immich server-info")
|
||||
print(res)
|
||||
|
||||
# Create the API key so ImmichFrame can start.
|
||||
with tempfile.NamedTemporaryFile("w", delete_on_close=False) as f:
|
||||
f.write(key)
|
||||
f.close()
|
||||
machine.copy_from_host(f.name, ${builtins.toJSON apiKeyFile})
|
||||
|
||||
# We finally have an api key! Make sure ImmichFrame starts up.
|
||||
machine.wait_for_unit("immichframe.service")
|
||||
machine.wait_for_open_port(8002)
|
||||
|
||||
# Check ImmichFrame's configuration.
|
||||
res = machine.succeed("curl --no-progress-meter -f http://localhost:8002/api/Config")
|
||||
frame_config = json.loads(res)
|
||||
assert frame_config['interval'] == custom_interval, frame_config
|
||||
|
||||
# At this point, ImmichFrame should not find any images to serve.
|
||||
res = machine.succeed("curl -f http://localhost:8002/api/Asset")
|
||||
assets = json.loads(res)
|
||||
assert len(assets) == 0, assets
|
||||
|
||||
# Repeat to make it easier for OCR to pick up given overlays
|
||||
image_text = '\\n'.join(['reproduce this moment', 'with NixOS tests <3'] * 6)
|
||||
|
||||
# These settings make it display fine given potential cropping.
|
||||
common_args = f"-gravity center -font Liberation-Mono -pointsize 50 -annotate 0 {quote(image_text)}"
|
||||
|
||||
# Upload some images to a new album.
|
||||
album_title = '✨ Reproducible Moments ✨'
|
||||
machine.succeed(f"magick -size 1920x1080 canvas:white -fill black {common_args} /tmp/white.png")
|
||||
machine.succeed(f"immich upload -A {quote(album_title)} /tmp/white.png")
|
||||
machine.succeed(f"magick -size 1920x1080 canvas:black -fill white {common_args} /tmp/black.png")
|
||||
machine.succeed(f"immich upload -A {quote(album_title)} /tmp/black.png")
|
||||
res = machine.succeed("immich server-info")
|
||||
print(res)
|
||||
|
||||
# ImmichFrame should now find some assets.
|
||||
# Note: we restart ImmichFrame because it has some caching that prevents it
|
||||
# from immediately seeing the newly uploaded photos.
|
||||
machine.succeed("systemctl restart immichframe.service")
|
||||
machine.wait_for_open_port(8002)
|
||||
res = machine.succeed("curl --no-progress-meter -f http://localhost:8002/api/Asset")
|
||||
assets = json.loads(res)
|
||||
assert len(assets) == 2, assets
|
||||
|
||||
# Wait for a photo to be displayed.
|
||||
machine.wait_for_x()
|
||||
machine.execute("xterm -e 'firefox --kiosk http://localhost:8002' >&2 &")
|
||||
machine.wait_for_window("immichFrame")
|
||||
_, active_window = machine.execute("xdotool getactivewindow")
|
||||
machine.succeed(f"xdotool windowsize {quote(active_window.strip())} 100% 100%")
|
||||
machine.wait_for_text('reproduce this moment')
|
||||
machine.wait_for_text('with NixOS tests')
|
||||
machine.screenshot("screen")
|
||||
'';
|
||||
}
|
||||
Generated
+467
@@ -0,0 +1,467 @@
|
||||
[
|
||||
{
|
||||
"pname": "AwesomeAssertions",
|
||||
"version": "9.2.0",
|
||||
"hash": "sha256-s5j46b6KchKwjQgiXvCYUZLk5Xtk4sb1wSh25f8ShFc="
|
||||
},
|
||||
{
|
||||
"pname": "BloomFilter.NetCore",
|
||||
"version": "2.5.3",
|
||||
"hash": "sha256-UfH3JZgUh8zj34+E1DLZdKo9Dyqb/Ek8qmg4kx5N818="
|
||||
},
|
||||
{
|
||||
"pname": "Castle.Core",
|
||||
"version": "5.1.1",
|
||||
"hash": "sha256-oVkQB+ON7S6Q27OhXrTLaxTL0kWB58HZaFFuiw4iTrE="
|
||||
},
|
||||
{
|
||||
"pname": "coverlet.collector",
|
||||
"version": "6.0.4",
|
||||
"hash": "sha256-ieiUl7G5pVKQ4V6rxhEe0ehep0/u1RBD3EAI63AQTI0="
|
||||
},
|
||||
{
|
||||
"pname": "Ical.Net",
|
||||
"version": "4.3.1",
|
||||
"hash": "sha256-jpohvCiUBe61A3O+BJwCqsPW+3jUt4cKeCqZb/0cw0M="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.ApplicationInsights",
|
||||
"version": "2.23.0",
|
||||
"hash": "sha256-5sf3bg7CZZjHseK+F3foOchEhmVeioePxMZVvS6Rjb0="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.AspNetCore.Mvc.Testing",
|
||||
"version": "8.0.20",
|
||||
"hash": "sha256-jSWZfbewG7LqrqVQ4wmwj4K6OPGslm0wt4D5wK2TlFc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.AspNetCore.SpaProxy",
|
||||
"version": "8.0.20",
|
||||
"hash": "sha256-kjvVZQBbE6fjKdbZsg+BNCRHh3d5fvxNNFaCF5fxQSI="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.AspNetCore.TestHost",
|
||||
"version": "8.0.20",
|
||||
"hash": "sha256-M/MTiN0nNQ8eKQn9r8TnIXYWxOYgBHNG/PZtvnInmDw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeCoverage",
|
||||
"version": "17.14.1",
|
||||
"hash": "sha256-f8QytG8GvRoP47rO2KEmnDLxIpyesaq26TFjDdW40Gs="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.ApiDescription.Client",
|
||||
"version": "8.0.20",
|
||||
"hash": "sha256-BcgOnv5xXl42ZocyzNrgSiUA6DbGoseTCV8RDi/n8Eo="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.ApiDescription.Server",
|
||||
"version": "6.0.5",
|
||||
"hash": "sha256-RJjBWz+UHxkQE2s7CeGYdTZ218mCufrxl0eBykZdIt4="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Caching.Abstractions",
|
||||
"version": "9.0.5",
|
||||
"hash": "sha256-TUsEWQLao2UCQi/Lqqlc5wnjMCI52tJ1A6NlODdRLOU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Caching.Memory",
|
||||
"version": "9.0.5",
|
||||
"hash": "sha256-tCNn5RSGZ+J3gd68cDGaHofB3ZTcvOlLXeY/4TG5INU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-9BPsASlxrV8ilmMCjdb3TiUcm5vFZxkBnAI/fNBSEyA="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Abstractions",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-Evg+Ynj2QUa6Gz+zqF+bUyfGD0HI5A2fHmxZEXbn3HA="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Abstractions",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-4eBpDkf7MJozTZnOwQvwcfgRKQGcNXe0K/kF+h5Rl8o="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Binder",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-7NZcKkiXWSuhhVcA/fXHPY/62aGUyMsRdiHm91cWC5Y="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Binder",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-GanfInGzzoN2bKeNwON8/Hnamr6l7RTpYLA49CNXD9Q="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Binder",
|
||||
"version": "8.0.2",
|
||||
"hash": "sha256-aGB0VuoC34YadAEqrwoaXLc5qla55pswDV2xLSmR7SE="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.CommandLine",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-fmPC/o8S+weTtQJWykpnGHm6AKVU21xYE/CaHYU7zgg="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.EnvironmentVariables",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-+bjFZvqCsMf2FRM2olqx/fub+QwfM1kBhjGVOT5HC48="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.FileExtensions",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-iRA8L7BX/fe5LHCVOhzBSk30GfshP7V2Qj2nxpEvStA="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.Json",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-J8EK/yhsfTpeSUY8F81ZTBV9APHiPUliN7d+n2OX9Ig="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Configuration.UserSecrets",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-yGvWfwBhyFudcIv96pKWaQ1MIMOiv5LHSCn+9J7Doz0="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyInjection",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-O9g0jWS+jfGoT3yqKwZYJGL+jGSIeSbwmvomKDC3hTU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-SZke0jNKIqJvvukdta+MgIlGsrP2EdPkkS8lfLg7Ju4="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-75KzEGWjbRELczJpCiJub+ltNUMMbz5A/1KQU+5dgP8="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
|
||||
"version": "8.0.2",
|
||||
"hash": "sha256-UfLfEQAkXxDaVPC7foE/J3FVEXd31Pu6uQIhTic3JgY="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyInjection.Abstractions",
|
||||
"version": "9.0.5",
|
||||
"hash": "sha256-JSGmzV9CdZaRIBDbnUXCMlrIk2oVnrQSCL79xoNmjeY="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.DependencyModel",
|
||||
"version": "8.0.2",
|
||||
"hash": "sha256-PyuO/MyCR9JtYqpA1l/nXGh+WLKCq34QuAXN9qNza9Q="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Diagnostics",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-CraHNCaVlMiYx6ff9afT6U7RC/MoOCXM3pn2KrXkiLc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Diagnostics.Abstractions",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-d5DVXhA8qJFY9YbhZjsTqs5w5kDuxF5v+GD/WZR1QL0="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.FileProviders.Abstractions",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-uQSXmt47X2HGoVniavjLICbPtD2ReQOYQMgy3l0xuMU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.FileProviders.Physical",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-29y5ZRQ1ZgzVOxHktYxyiH40kVgm5un2yTGdvuSWnRc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.FileSystemGlobbing",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-+Oz41JR5jdcJlCJOSpQIL5OMBNi+1Hl2d0JUHfES7sU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Hosting",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-FFLo6em0N2vaWg6//vaQhxoOgT9LLH5Y2KWkCeX5xQ4="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Hosting.Abstractions",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-/bIVL9uvBQhV/KQmjA1ZjR74sMfaAlBb15sVXsGDEVA="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Http",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-ScPwhBvD3Jd4S0E7JQ18+DqY3PtQvdFLbkohUBbFd3o="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-vkfVw4tQEg86Xg18v6QO0Qb4Ysz0Njx57d1XcNuj6IU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Abstractions",
|
||||
"version": "6.0.1",
|
||||
"hash": "sha256-v3FWpuKXlBIW5NwqQx0Ffb6y58RlevIyO/byqeLphJ8="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Abstractions",
|
||||
"version": "8.0.2",
|
||||
"hash": "sha256-cHpe8X2BgYa5DzulZfq24rg8O2K5Lmq2OiLhoyAVgJc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Abstractions",
|
||||
"version": "9.0.5",
|
||||
"hash": "sha256-ILcK06TEbVwxUZ3PCpGkjg69H6PGwTXnN512JJ8F9KI="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Configuration",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-E2JbJG2EXlv2HUWLi17kIkAL6RC9rC2E18C3gAyOuaE="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Console",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-2thhF1JbDNj3Bx2fcH7O26uHGNeMd9MYah6N60lIpIU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.Debug",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-gKFqBg5lbjy5VBEcAuoQ/SsXAxvrYdBYOu9dV60eJKg="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.EventLog",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-1UkEOwl3Op2b3jTvpI10hHxIe9FqeVVy+VB1tZp6Lc8="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Logging.EventSource",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-EINT/PgfB4Dvf+1JBzL1plPT35ezT7kyS8y/XMMgYxA="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Options",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-DxnEgGiCXpkrxFkxXtOXqwaiAtoIjA8VSSWCcsW0FwE="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Options",
|
||||
"version": "8.0.2",
|
||||
"hash": "sha256-AjcldddddtN/9aH9pg7ClEZycWtFHLi9IPe1GGhNQys="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Options",
|
||||
"version": "9.0.5",
|
||||
"hash": "sha256-iz/ox4wpQBX0Qk97qRYlmmJPuuoV2urN03uGxAkp/SM="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Options.ConfigurationExtensions",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-au0Y13cGk/dQFKuvSA5NnP/++bErTk0oOTlgmHdI2Mw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Options.ConfigurationExtensions",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-A5Bbzw1kiNkgirk5x8kyxwg9lLTcSngojeD+ocpG1RI="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Primitives",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-AgvysszpQ11AiTBJFkvSy8JnwIWTj15Pfek7T7ThUc4="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Primitives",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-FU8qj3DR8bDdc1c+WeGZx/PCZeqqndweZM9epcpXjSo="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Extensions.Primitives",
|
||||
"version": "9.0.5",
|
||||
"hash": "sha256-3ctJMOwnEsBSNqcrh77IItX2wtOmjM/b8OTXJUZ/P0o="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NET.Test.Sdk",
|
||||
"version": "17.14.1",
|
||||
"hash": "sha256-mZUzDFvFp7x1nKrcnRd0hhbNu5g8EQYt8SKnRgdhT/A="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.OpenApi",
|
||||
"version": "1.6.23",
|
||||
"hash": "sha256-YD2oxM/tlNpK5xUeHF85xdqcpBzHioUSyRjpN2A7KcY="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Extensions.Telemetry",
|
||||
"version": "1.9.0",
|
||||
"hash": "sha256-JT91ThKLEyoRS/8ZJqZwlSTT7ofC2QhNqPFI3pYmMaw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Extensions.TrxReport.Abstractions",
|
||||
"version": "1.9.0",
|
||||
"hash": "sha256-oscZOEKw7gM6eRdDrOS3x+CwqIvXWRmfmi0ugCxBRw0="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Extensions.VSTestBridge",
|
||||
"version": "1.9.0",
|
||||
"hash": "sha256-CadXLWD093sUDaWhnppzD9LvpxSRqqt93ZEOFiIAPyw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Platform",
|
||||
"version": "1.9.0",
|
||||
"hash": "sha256-6nzjoYbJOh7v/GB7d+TDuM0l/xglCshFX6KWjg7+cFI="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Platform.MSBuild",
|
||||
"version": "1.9.0",
|
||||
"hash": "sha256-/bileP4b+9RZp8yjgS6eynXwc2mohyyzf6p/0LZJd8I="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.TestPlatform.AdapterUtilities",
|
||||
"version": "17.13.0",
|
||||
"hash": "sha256-Vr+3Tad/h/nk7f/5HMExn3HvCGFCarehFAzJSfCBaOc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.TestPlatform.ObjectModel",
|
||||
"version": "17.13.0",
|
||||
"hash": "sha256-6S0fjfj8vA+h6dJVNwLi6oZhYDO/I/6hBZaq2VTW+Uk="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.TestPlatform.ObjectModel",
|
||||
"version": "17.14.1",
|
||||
"hash": "sha256-QMf6O+w0IT+16Mrzo7wn+N20f3L1/mDhs/qjmEo1rYs="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.TestPlatform.TestHost",
|
||||
"version": "17.14.1",
|
||||
"hash": "sha256-1cxHWcvHRD7orQ3EEEPPxVGEkTpxom1/zoICC9SInJs="
|
||||
},
|
||||
{
|
||||
"pname": "Moq",
|
||||
"version": "4.20.72",
|
||||
"hash": "sha256-+uAc/6xtzij9YnmZrhZwc+4vUgx6cppZsWQli3CGQ8o="
|
||||
},
|
||||
{
|
||||
"pname": "Newtonsoft.Json",
|
||||
"version": "13.0.3",
|
||||
"hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc="
|
||||
},
|
||||
{
|
||||
"pname": "Newtonsoft.Json",
|
||||
"version": "13.0.4",
|
||||
"hash": "sha256-8JCB1FdAW681qXP6DFDWvycu1oPyVoxaYgpJ2pUvZSk="
|
||||
},
|
||||
{
|
||||
"pname": "NodaTime",
|
||||
"version": "3.2.0",
|
||||
"hash": "sha256-kt59MWEzmMFBgpw5tOPlcYwZKe74WkA9N5ggrLS3tUM="
|
||||
},
|
||||
{
|
||||
"pname": "NSwag.ApiDescription.Client",
|
||||
"version": "13.20.0",
|
||||
"hash": "sha256-r/BtE46yFdM9MuoaVYNhO7vvdBICDsM8ROXZldxFIeY="
|
||||
},
|
||||
{
|
||||
"pname": "NSwag.MSBuild",
|
||||
"version": "13.20.0",
|
||||
"hash": "sha256-HarUF6peOsd12saco8+Na4TnOEh+Gl2MJelSfnBVRhQ="
|
||||
},
|
||||
{
|
||||
"pname": "NUnit",
|
||||
"version": "4.4.0",
|
||||
"hash": "sha256-5geF5QOF+X/WkuCEgkPVKH4AdKx4U0olpU07S8+G3nU="
|
||||
},
|
||||
{
|
||||
"pname": "NUnit.Analyzers",
|
||||
"version": "4.10.0",
|
||||
"hash": "sha256-wkThcI2ratJoCnstOIj/jB5muGQbdDPFbRK1r7PHEBU="
|
||||
},
|
||||
{
|
||||
"pname": "NUnit3TestAdapter",
|
||||
"version": "5.2.0",
|
||||
"hash": "sha256-ybTutL4VkX/fq61mS+O3Ruh+adic4fpv+MKgQ0IZvGg="
|
||||
},
|
||||
{
|
||||
"pname": "OpenWeatherMap.API",
|
||||
"version": "2.1.2",
|
||||
"hash": "sha256-rosFFZzqPKNPuItlxGjeeCv+jqDzVvyiKy4vxmUFP5A="
|
||||
},
|
||||
{
|
||||
"pname": "Swashbuckle.AspNetCore",
|
||||
"version": "8.1.4",
|
||||
"hash": "sha256-gZVM5/6HMBAuuQI2uXdoAUJOG8BkGuh/3ZuWlGME8vk="
|
||||
},
|
||||
{
|
||||
"pname": "Swashbuckle.AspNetCore.Swagger",
|
||||
"version": "8.1.4",
|
||||
"hash": "sha256-6iqC/F581my20C+/3ORTM6uVUj+M095cRQyCIAo9AyA="
|
||||
},
|
||||
{
|
||||
"pname": "Swashbuckle.AspNetCore.SwaggerGen",
|
||||
"version": "8.1.4",
|
||||
"hash": "sha256-m0ixgc45HCX2xrvrnhy3WYU/r/basjat535n4rN+vOY="
|
||||
},
|
||||
{
|
||||
"pname": "Swashbuckle.AspNetCore.SwaggerUI",
|
||||
"version": "8.1.4",
|
||||
"hash": "sha256-zkVzBxkxGh5WyhFEOiZknez9GBeBKXTH2ySf2/BnSV4="
|
||||
},
|
||||
{
|
||||
"pname": "System.Collections.Immutable",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-F7OVjKNwpqbUh8lTidbqJWYi476nsq9n+6k0+QVRo3w="
|
||||
},
|
||||
{
|
||||
"pname": "System.Diagnostics.DiagnosticSource",
|
||||
"version": "5.0.0",
|
||||
"hash": "sha256-6mW3N6FvcdNH/pB58pl+pFSCGWgyaP4hfVtC/SMWDV4="
|
||||
},
|
||||
{
|
||||
"pname": "System.Diagnostics.DiagnosticSource",
|
||||
"version": "9.0.5",
|
||||
"hash": "sha256-fB8970CWgKWI1UwAaDa9D2yaMUiQ74ULGU7GMsr2u/o="
|
||||
},
|
||||
{
|
||||
"pname": "System.Diagnostics.EventLog",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-zUXIQtAFKbiUMKCrXzO4mOTD5EUphZzghBYKXprowSM="
|
||||
},
|
||||
{
|
||||
"pname": "System.Diagnostics.EventLog",
|
||||
"version": "8.0.1",
|
||||
"hash": "sha256-zvqd72pwgcGoa1nH3ZT1C0mP9k53vFLJ69r5MCQ1saA="
|
||||
},
|
||||
{
|
||||
"pname": "System.IO.Pipelines",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-LdpB1s4vQzsOODaxiKstLks57X9DTD5D6cPx8DE1wwE="
|
||||
},
|
||||
{
|
||||
"pname": "System.Reflection.Metadata",
|
||||
"version": "1.6.0",
|
||||
"hash": "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E="
|
||||
},
|
||||
{
|
||||
"pname": "System.Reflection.Metadata",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-dQGC30JauIDWNWXMrSNOJncVa1umR1sijazYwUDdSIE="
|
||||
},
|
||||
{
|
||||
"pname": "System.Runtime.CompilerServices.Unsafe",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I="
|
||||
},
|
||||
{
|
||||
"pname": "ThumbHash",
|
||||
"version": "2.1.1",
|
||||
"hash": "sha256-pLlNMjUvCHZ2cJi2nco1ikFq/plIklA3x8Cgcag3x44="
|
||||
},
|
||||
{
|
||||
"pname": "UnitsNet",
|
||||
"version": "5.29.0",
|
||||
"hash": "sha256-LdHK42YM1X3Hq3VWWh9slhgEpUsQa2kM335bAitpT+A="
|
||||
},
|
||||
{
|
||||
"pname": "YamlDotNet",
|
||||
"version": "16.3.0",
|
||||
"hash": "sha256-4Gi8wSQ8Rsi/3+LyegJr//A83nxn2fN8LN1wvSSp39Q="
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,86 @@
|
||||
{
|
||||
buildDotnetModule,
|
||||
buildNpmPackage,
|
||||
dotnet-sdk,
|
||||
fetchFromGitHub,
|
||||
fetchpatch,
|
||||
lib,
|
||||
nixosTests,
|
||||
writeShellApplication,
|
||||
}:
|
||||
|
||||
let
|
||||
version = "1.0.29.0";
|
||||
src = fetchFromGitHub {
|
||||
owner = "immichFrame";
|
||||
repo = "immichFrame";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-YFh+/QWYYtQzBVJUyUuhhKqi9/5waWVX+lw/tov++ws=";
|
||||
};
|
||||
|
||||
publishApi = buildDotnetModule {
|
||||
pname = "immichframe";
|
||||
inherit version src;
|
||||
projectFile = "ImmichFrame.WebApi/ImmichFrame.WebApi.csproj";
|
||||
nugetDeps = ./deps.json;
|
||||
dotnet-runtime = dotnet-sdk.aspnetcore;
|
||||
|
||||
patches = [
|
||||
# This not-yet-released commit has landed upstream. It adds a
|
||||
# `IMMICHFRAME_CONFIG_PATH` environment variable for a "configurable"
|
||||
# config path.
|
||||
(fetchpatch {
|
||||
name = "Configurable config path";
|
||||
url = "https://github.com/immichFrame/ImmichFrame/commit/f6680f23bcf107ce27372dfb37809c0f92ebb2f2.patch";
|
||||
hash = "sha256-dQnspQEKixQgBpCvNxrYL51z5wg5BhdN0uTuaXgKQZU=";
|
||||
})
|
||||
# This patch adds an `ApiKeyFile` option, which makes it possible to
|
||||
# configure ImmichFrame without leaking secrets into your configuration.
|
||||
# See [upstream PR](https://github.com/immichFrame/ImmichFrame/pull/511)
|
||||
(fetchpatch {
|
||||
name = "Add a `ApiKeyFile` option";
|
||||
url = "https://github.com/immichFrame/ImmichFrame/commit/f5bb164170460b1020bfe6bce8e8abb3315e32e3.diff";
|
||||
hash = "sha256-F3BVIxcu8Hm6wbWmzVnfgm6XvqdBw4IiS61CDQiMRVg=";
|
||||
})
|
||||
];
|
||||
|
||||
meta.mainProgram = "ImmichFrame.WebApi";
|
||||
};
|
||||
|
||||
frontend = buildNpmPackage {
|
||||
pname = "immichframe-frontend";
|
||||
inherit version src;
|
||||
|
||||
sourceRoot = "${src.name}/immichFrame.Web";
|
||||
|
||||
npmBuildScript = "build";
|
||||
npmDepsHash = "sha256-eOv3DlmHaI6hVCYTBzCtLWKD72/RM/KjCUDVUgb9jcg=";
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir $out
|
||||
cp -r build/ $out/wwwroot
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
in
|
||||
writeShellApplication {
|
||||
name = "ImmichFrame";
|
||||
|
||||
text = ''
|
||||
cd ${frontend}
|
||||
exec ${lib.getExe publishApi} "$@"
|
||||
'';
|
||||
|
||||
passthru.tests = { inherit (nixosTests) immichframe; };
|
||||
|
||||
meta = {
|
||||
description = "Display your photos from Immich as a digital photo frame";
|
||||
homepage = "https://immichframe.dev";
|
||||
license = lib.licenses.gpl3Only;
|
||||
maintainers = with lib.maintainers; [ jfly ];
|
||||
platforms = lib.platforms.all;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user