Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

scrypted: init at 0.126.0 #373883

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions maintainers/maintainer-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17008,6 +17008,13 @@
githubId = 4728903;
name = "Owen Lynch";
};
omares = {
email = "[email protected]";
github = "omares";
githubId = 1375307;
name = "Ota Mares";
keys = [ { fingerprint = "gFRzYaOzwdT2ipiB7GoOotds1SPhu7YcfB1ozj6Z/Ng"; } ];
};
omasanori = {
github = "omasanori";
githubId = 167209;
Expand Down
178 changes: 178 additions & 0 deletions nixos/modules/services/video/scrypted.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
{
config,
lib,
pkgs,
...
}:

let
cfg = config.services.scrypted;

python312Packages =
python-pkgs: with python-pkgs; [
pip
setuptools
wheel
debugpy
gst-python
];

python39Packages =
python-pkgs: with python-pkgs; [
pip
debugpy
];

python312 = pkgs.python312.withPackages python312Packages;
python39 = pkgs.python39.withPackages python39Packages;

gstPlugins = with pkgs.gst_all_1; [
gstreamer
gst-plugins-base
gst-plugins-good
gst-plugins-bad
gst-libav
gst-vaapi
];
in
{
options.services.scrypted = {
enable = lib.mkEnableOption "Scrypted home automation server";

package = lib.mkOption {
type = lib.types.package;
description = "The scrypted package to use";
};

openFirewall = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Open ports 11080 and 10443 in the firewall";
};

installPath = lib.mkOption {
type = lib.types.path;
default = "/var/lib/scrypted";
description = "Directory where scrypted data will be stored";
};

user = lib.mkOption {
type = lib.types.str;
default = "scrypted";
description = "User account under which scrypted runs";
};

group = lib.mkOption {
type = lib.types.str;
default = "scrypted";
description = "Group account under which scrypted runs";
};

environmentFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Environment file to load additional environment variables from";
};

extraEnvironment = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Additional environment variables for scrypted";
example = lib.literalExpression ''
{
SCRYPTED_NVR_VOLUME = "/var/lib/scrypted/nvr";
SCRYPTED_SECURE_PORT = 443;
SCRYPTED_INSECURE_PORT = 8080;
}
'';
};
};

config = lib.mkIf cfg.enable {
systemd.services.scrypted = {
description = "Scrypted home automation server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];

environment = lib.mkMerge [
{
SCRYPTED_CAN_RESTART = "true";
SCRYPTED_INSTALL_PATH = cfg.installPath;
SCRYPTED_VOLUME = "${cfg.installPath}/volume";
SCRYPTED_PYTHON_PATH = "${lib.getExe python312}";
SCRYPTED_PYTHON39_PATH = "${lib.getExe python39}";
SCRYPTED_PYTHON312_PATH = "${lib.getExe python312}";
SCRYPTED_FFMPEG_PATH = "${lib.getExe pkgs.ffmpeg}";

LD_LIBRARY_PATH = lib.makeLibraryPath [
pkgs.gcc-unwrapped.lib
pkgs.tensorflow-lite
];

GST_PLUGIN_PATH = lib.makeSearchPath "lib/gstreamer-1.0" gstPlugins;
}
cfg.extraEnvironment
];

path = [
python39
python312
pkgs.ffmpeg
pkgs.gcc-unwrapped.lib
pkgs.gobject-introspection
pkgs.tensorflow-lite
] ++ gstPlugins;

serviceConfig =
{
ExecStart = "${lib.getExe cfg.package}";
Restart = "always";
RestartSec = "3";

User = cfg.user;
Group = cfg.group;

StateDirectory = "scrypted";
StateDirectoryMode = "0750";

ProtectSystem = "strict";
ProtectHome = true;
WorkingDirectory = cfg.installPath;
ReadWritePaths = [ cfg.installPath ];
PrivateDevices = false;
PrivateTmp = true;
NoNewPrivileges = true;
RestrictRealtime = true;

RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_NETLINK"
];

}
// lib.optionalAttrs (cfg.environmentFile != null) {
EnvironmentFile = cfg.environmentFile;
};
};

users.users = lib.mkIf (cfg.user == "scrypted") {
${cfg.user} = {
isSystemUser = true;
group = cfg.group;
home = cfg.installPath;
createHome = true;
description = "Scrypted service user";
};
};

users.groups = lib.mkIf (cfg.group == "scrypted") { ${cfg.group} = { }; };

networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [
11080
10443
];
};
};
}
24 changes: 24 additions & 0 deletions pkgs/by-name/sc/scrypted/hooks.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
lib,
srcOnly,
makeSetupHook,
nodejs,
jq,
prefetch-npm-deps,
diffutils,
}:

{
customConfigHook = makeSetupHook {
name = "custom-npm-config-hook";
substitutions = {
nodeSrc = srcOnly nodejs;
nodeGyp = "${nodejs}/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js";
diff = "${diffutils}/bin/diff";
jq = "${jq}/bin/jq";
prefetchNpmDeps = "${prefetch-npm-deps}/bin/prefetch-npm-deps";
nodeVersion = nodejs.version;
nodeVersionMajor = lib.versions.major nodejs.version;
};
} ./npm-config-hook.sh;
}
126 changes: 126 additions & 0 deletions pkgs/by-name/sc/scrypted/npm-config-hook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# shellcheck shell=bash

npmConfigHook() {
echo "Executing npmConfigHook"

# Use npm patches in the nodejs package
export NIX_NODEJS_BUILDNPMPACKAGE=1
export prefetchNpmDeps="@prefetchNpmDeps@"

if [ -n "${npmRoot-}" ]; then
pushd "$npmRoot"
fi

echo "Configuring npm"

export HOME="$TMPDIR"
export npm_config_nodedir="@nodeSrc@"
export npm_config_node_gyp="@nodeGyp@"

if [ -z "${npmDeps-}" ]; then
echo
echo "ERROR: no dependencies were specified"
echo 'Hint: set `npmDeps` if using these hooks individually. If this is happening with `buildNpmPackage`, please open an issue.'
echo

exit 1
fi

local -r cacheLockfile="$npmDeps/package-lock.json"
local -r srcLockfile="$PWD/package-lock.json"

echo "Validating consistency between $srcLockfile and $cacheLockfile"

if ! @diff@ "$srcLockfile" "$cacheLockfile"; then
# If the diff failed, first double-check that the file exists, so we can
# give a friendlier error msg.
if ! [ -e "$srcLockfile" ]; then
echo
echo "ERROR: Missing package-lock.json from src. Expected to find it at: $srcLockfile"
echo "Hint: You can copy a vendored package-lock.json file via postPatch."
echo

exit 1
fi

if ! [ -e "$cacheLockfile" ]; then
echo
echo "ERROR: Missing lockfile from cache. Expected to find it at: $cacheLockfile"
echo

exit 1
fi

echo
echo "ERROR: npmDepsHash is out of date"
echo
echo "The package-lock.json in src is not the same as the in $npmDeps."
echo
echo "To fix the issue:"
echo '1. Use `lib.fakeHash` as the npmDepsHash value'
echo "2. Build the derivation and wait for it to fail with a hash mismatch"
echo "3. Copy the 'got: sha256-' value back into the npmDepsHash field"
echo

exit 1
fi

export CACHE_MAP_PATH="$TMP/MEOW"
@prefetchNpmDeps@ --map-cache

@prefetchNpmDeps@ --fixup-lockfile "$srcLockfile"

local cachePath

if [ -z "${makeCacheWritable-}" ]; then
cachePath="$npmDeps"
else
echo "Making cache writable"
cp -r "$npmDeps" "$TMPDIR/cache"
chmod -R 700 "$TMPDIR/cache"
cachePath="$TMPDIR/cache"
fi

npm config set cache "$cachePath"
npm config set offline true
npm config set progress false

echo "Installing dependencies"

if ! npm ci --ignore-scripts $npmInstallFlags "${npmInstallFlagsArray[@]}" $npmFlags "${npmFlagsArray[@]}"; then
echo
echo "ERROR: npm failed to install dependencies"
echo
echo "Here are a few things you can try, depending on the error:"
echo '1. Set `makeCacheWritable = true`'
echo " Note that this won't help if npm is complaining about not being able to write to the logs directory -- look above that for the actual error."
echo '2. Set `npmFlags = [ "--legacy-peer-deps" ]`'
echo

exit 1
fi

patchShebangs node_modules

# npm rebuild currently fails due to issues with node-addon-api.
# See https://github.com/NixOS/nixpkgs/issues/372605.
# The inability to build the @scrypted/node-pty npm package prevents scrypted's
# terminal feature from functioning, while all other features remain available.
# Although I don't view this as a downside, as terminal access through a Node
# app could be considered a security risk.

# npm rebuild $npmRebuildFlags "${npmRebuildFlagsArray[@]}" $npmFlags "${npmFlagsArray[@]}"

patchShebangs node_modules

rm "$CACHE_MAP_PATH"
unset CACHE_MAP_PATH

if [ -n "${npmRoot-}" ]; then
popd
fi

echo "Finished npmConfigHook"
}

postPatchHooks+=(npmConfigHook)
53 changes: 53 additions & 0 deletions pkgs/by-name/sc/scrypted/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
lib,
buildNpmPackage,
fetchFromGitHub,
nodejs_20,
callPackage,
nix-update-script,
}:
let
npmHooks = callPackage ./hooks.nix { };
nodejs = nodejs_20;
in
(buildNpmPackage.override { inherit nodejs; }) rec {
pname = "scrypted";
version = "0.127.1";

src = fetchFromGitHub {
owner = "koush";
repo = "scrypted";
tag = "v${version}";
hash = "sha256-I/EFvv2JClX96mTZsYx/1lYI+N0CzmJSKrgN42VtYqU=";
};

npmDepsHash = "sha256-M6go8LEIB2W6/Alqj1LAQVj99eNbeuXBCYlueL5xcLk=";

# A custom npm hook is required to skip the npm rebuild phase
npmConfigHook = npmHooks.customConfigHook;

sourceRoot = "${src.name}/server";

nativeBuildInputs = [ nodejs ];

makeWrapperArgs = [ "--set NODE_ENV production" ];

postInstall = ''
cp ${
lib.escapeShellArg (builtins.toFile "install.json" (builtins.toJSON { inherit version; }))
} $out/install.json
'';

passthru.updateScript = nix-update-script { };

meta = {
description = ''
Scrypted is a high performance home video integration platform and NVR with smart detections
'';
mainProgram = "scrypted-serve";
homepage = "https://github.com/koush/scrypted";
license = lib.licenses.mit;
platforms = lib.platforms.all;
maintainers = with lib.maintainers; [ omares ];
};
}