navidrome: introduce plugin infrastructure

This commit is contained in:
Chris Moultrie
2026-04-15 09:07:08 -04:00
parent 14d0c3f17b
commit 68e6fb9603
6 changed files with 160 additions and 3 deletions
+60 -2
View File
@@ -7,13 +7,18 @@
let
inherit (lib)
literalExpression
mkDefault
mkEnableOption
mkPackageOption
mkOption
maintainers
;
inherit (lib.types)
addCheck
bool
listOf
package
port
str
submodule
@@ -29,6 +34,36 @@ in
package = mkPackageOption pkgs "navidrome" { };
plugins = mkOption {
type = listOf (
addCheck package (p: p.isNavidromePlugin or false)
// {
name = "navidrome plugin";
description = "package that is a navidrome plugin";
}
);
default = [ ];
description = "List of Navidrome plugins";
example = literalExpression ''
with pkgs.navidromePlugins; [
];
'';
};
finalPackage = mkOption {
type = package;
readOnly = true;
default = cfg.package.override {
inherit (cfg) plugins;
};
defaultText = literalExpression ''
config.services.navidrome.package.override {
inherit (config.services.navidrome) plugins;
}
'';
description = "The final navidrome package including all selected plugins.";
};
settings = mkOption {
type = submodule {
freeformType = settingsFormat.type;
@@ -51,6 +86,29 @@ in
description = "Enable anonymous usage data collection, see <https://www.navidrome.org/docs/getting-started/insights/> for details.";
type = bool;
};
Plugins = {
Enabled = mkOption {
default = (builtins.length cfg.plugins) != 0;
defaultText = literalExpression "builtins.length \"\${config.services.navidrome.plugins != 0}\"";
description = ''
Enable plugin support in navidrome.
This is automatically enabled if {option}`services.navidrome.plugins` is used.
'';
};
Folder = mkOption {
default = "${cfg.finalPackage}/share/plugins";
description = ''
The folder containing navidrome plugins.
This directory is automatically created from plugins defined in {option}`services.navidrome.plugins`.
'';
readOnly = true;
type = str;
defaultText = literalExpression "\"\${config.services.navidrome.finalPackage}/share/plugins\"";
};
};
};
};
default = { };
@@ -78,7 +136,7 @@ in
description = "Whether to open the TCP port in the firewall";
};
environmentFile = lib.mkOption {
environmentFile = mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Environment file, used to set any secret ND_* environment variables.";
@@ -114,7 +172,7 @@ in
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = ''
${getExe cfg.package} --configfile ${settingsFormat.generate "navidrome.json" cfg.settings}
${getExe cfg.finalPackage} --configfile ${settingsFormat.generate "navidrome.json" cfg.settings}
'';
EnvironmentFile = lib.mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
User = cfg.user;
+3 -1
View File
@@ -5,7 +5,9 @@
nodes.machine =
{ ... }:
{
services.navidrome.enable = true;
services.navidrome = {
enable = true;
};
};
testScript = ''
+11
View File
@@ -16,6 +16,7 @@
nix-update-script,
ffmpegSupport ? true,
versionCheckHook,
plugins ? [ ],
}:
buildGoModule (finalAttrs: {
@@ -46,6 +47,8 @@ buildGoModule (finalAttrs: {
pkg-config
];
runtimeInputs = plugins;
overrideModAttrs = oldAttrs: {
nativeBuildInputs = lib.filter (drv: drv != npmHooks.npmConfigHook) oldAttrs.nativeBuildInputs;
preBuild = null;
@@ -77,6 +80,13 @@ buildGoModule (finalAttrs: {
make buildjs
'';
postInstall = ''
mkdir -p $out/share/plugins/
${lib.concatMapStringsSep "\n" (plugin: ''
ln -s ${plugin}/share/${plugin.pname}.ndp $out/share/plugins/
'') plugins}
'';
tags = [
"netgo"
"sqlite_fts5"
@@ -91,6 +101,7 @@ buildGoModule (finalAttrs: {
'';
passthru = {
inherit plugins;
tests.navidrome = nixosTests.navidrome;
updateScript = nix-update-script { };
};
@@ -0,0 +1,8 @@
# Packaging guidelines
## Basics
Each navidrome plugin is a `.zip` file named `${pname}.ndp`. Inside this zip is
two files named exactly:
- `manifest.json` which defines permissions and configuration
- `plugin.wasm` which is the output of the plugin build
@@ -0,0 +1,67 @@
{
buildGoModule,
lib,
navidrome,
zip,
}:
{
pname,
src,
version,
vendorHash,
meta,
...
}@args:
buildGoModule (
finalAttrs:
{
inherit
pname
version
src
vendorHash
;
env = {
CGO_ENABLED = "0";
}
// (args.env or { });
postBuild = ''
GOOS=wasip1 \
GOARCH=wasm \
go build \
-buildmode=c-shared \
-o $GOPATH/bin/plugin.wasm .
'';
postInstall = ''
mkdir $out/share
pushd $(mktemp -d)
cp $GOPATH/bin/plugin.wasm .
cp ${finalAttrs.src}/manifest.json .
${lib.getExe zip} \
$out/share/${finalAttrs.pname}.ndp \
plugin.wasm \
manifest.json
popd
rm -r $out/bin
'';
passthru = {
isNavidromePlugin = true;
}
// args.passthru or { };
meta = {
inherit (navidrome.meta) platforms maintainers;
}
// args.meta or { };
}
// removeAttrs args [
"meta"
"passthru"
]
)
+11
View File
@@ -2686,6 +2686,17 @@ with pkgs;
nanoemoji = with python3Packages; toPythonApplication nanoemoji;
buildNavidromePlugin = callPackage ../by-name/na/navidrome/plugins/build-plugin.nix { };
navidromePlugins = recurseIntoAttrs (
lib.makeExtensible (
self:
lib.packagesFromDirectoryRecursive {
inherit callPackage;
directory = ../by-name/na/navidrome/plugins;
}
)
);
netdata = callPackage ../tools/system/netdata {
protobuf = protobuf_21;
};