Merge pull request #55936 from tobim/modules/snapserver
nixos/snapserver: init
This commit is contained in:
commit
502a4263a3
@ -182,6 +182,7 @@
|
||||
./services/audio/mpd.nix
|
||||
./services/audio/mopidy.nix
|
||||
./services/audio/slimserver.nix
|
||||
./services/audio/snapserver.nix
|
||||
./services/audio/squeezelite.nix
|
||||
./services/audio/ympd.nix
|
||||
./services/backup/bacula.nix
|
||||
|
217
nixos/modules/services/audio/snapserver.nix
Normal file
217
nixos/modules/services/audio/snapserver.nix
Normal file
@ -0,0 +1,217 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
package = "snapcast";
|
||||
name = "snapserver";
|
||||
|
||||
cfg = config.services.snapserver;
|
||||
|
||||
# Using types.nullOr to inherit upstream defaults.
|
||||
sampleFormat = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Default sample format.
|
||||
'';
|
||||
example = "48000:16:2";
|
||||
};
|
||||
|
||||
codec = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Default audio compression method.
|
||||
'';
|
||||
example = "flac";
|
||||
};
|
||||
|
||||
streamToOption = name: opt:
|
||||
let
|
||||
os = val:
|
||||
optionalString (val != null) "${val}";
|
||||
os' = prefixx: val:
|
||||
optionalString (val != null) (prefixx + "${val}");
|
||||
flatten = key: value:
|
||||
"&${key}=${value}";
|
||||
in
|
||||
"-s ${opt.type}://" + os opt.location + "?" + os' "name=" name
|
||||
+ concatStrings (mapAttrsToList flatten opt.query);
|
||||
|
||||
optionalNull = val: ret:
|
||||
optional (val != null) ret;
|
||||
|
||||
optionString = concatStringsSep " " (mapAttrsToList streamToOption cfg.streams
|
||||
++ ["-p ${toString cfg.port}"]
|
||||
++ ["--controlPort ${toString cfg.controlPort}"]
|
||||
++ optionalNull cfg.sampleFormat "--sampleFormat ${cfg.sampleFormat}"
|
||||
++ optionalNull cfg.codec "-c ${cfg.codec}"
|
||||
++ optionalNull cfg.streamBuffer "--streamBuffer ${cfg.streamBuffer}"
|
||||
++ optionalNull cfg.buffer "-b ${cfg.buffer}"
|
||||
++ optional cfg.sendToMuted "--sendToMuted");
|
||||
|
||||
in {
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.snapserver = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable snapserver.
|
||||
'';
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 1704;
|
||||
description = ''
|
||||
The port that snapclients can connect to.
|
||||
'';
|
||||
};
|
||||
|
||||
controlPort = mkOption {
|
||||
type = types.port;
|
||||
default = 1705;
|
||||
description = ''
|
||||
The port for control connections (JSON-RPC).
|
||||
'';
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to automatically open the specified ports in the firewall.
|
||||
'';
|
||||
};
|
||||
|
||||
inherit sampleFormat;
|
||||
inherit codec;
|
||||
|
||||
streams = mkOption {
|
||||
type = with types; attrsOf (submodule {
|
||||
options = {
|
||||
location = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
The location of the pipe.
|
||||
'';
|
||||
};
|
||||
type = mkOption {
|
||||
type = types.enum [ "pipe" "file" "process" "spotify" "airplay" ];
|
||||
default = "pipe";
|
||||
description = ''
|
||||
The type of input stream.
|
||||
'';
|
||||
};
|
||||
query = mkOption {
|
||||
type = attrsOf str;
|
||||
default = {};
|
||||
description = ''
|
||||
Key-value pairs that convey additional parameters about a stream.
|
||||
'';
|
||||
example = literalExample ''
|
||||
# for type == "pipe":
|
||||
{
|
||||
mode = "listen";
|
||||
};
|
||||
# for type == "process":
|
||||
{
|
||||
params = "--param1 --param2";
|
||||
logStderr = "true";
|
||||
};
|
||||
'';
|
||||
};
|
||||
inherit sampleFormat;
|
||||
inherit codec;
|
||||
};
|
||||
});
|
||||
default = { default = {}; };
|
||||
description = ''
|
||||
The definition for an input source.
|
||||
'';
|
||||
example = literalExample ''
|
||||
{
|
||||
mpd = {
|
||||
type = "pipe";
|
||||
location = "/run/snapserver/mpd";
|
||||
sampleFormat = "48000:16:2";
|
||||
codec = "pcm";
|
||||
};
|
||||
};
|
||||
'';
|
||||
};
|
||||
|
||||
streamBuffer = mkOption {
|
||||
type = with types; nullOr int;
|
||||
default = null;
|
||||
description = ''
|
||||
Stream read (input) buffer in ms.
|
||||
'';
|
||||
example = 20;
|
||||
};
|
||||
|
||||
buffer = mkOption {
|
||||
type = with types; nullOr int;
|
||||
default = null;
|
||||
description = ''
|
||||
Network buffer in ms.
|
||||
'';
|
||||
example = 1000;
|
||||
};
|
||||
|
||||
sendToMuted = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Send audio to muted clients.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
systemd.services.snapserver = {
|
||||
after = [ "network.target" ];
|
||||
description = "Snapserver";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "mpd.service" "mopidy.service" ];
|
||||
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
ExecStart = "${pkgs.snapcast}/bin/snapserver --daemon ${optionString}";
|
||||
Type = "forking";
|
||||
LimitRTPRIO = 50;
|
||||
LimitRTTIME = "infinity";
|
||||
NoNewPrivileges = true;
|
||||
PIDFile = "/run/${name}/pid";
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectKernelModules = true;
|
||||
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
|
||||
RestrictNamespaces = true;
|
||||
RuntimeDirectory = name;
|
||||
StateDirectory = name;
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = optionals cfg.openFirewall [ cfg.port cfg.controlPort ];
|
||||
};
|
||||
|
||||
meta = {
|
||||
maintainers = with maintainers; [ tobim ];
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user