diff --git a/nixarr/lib/arr-settings-options.nix b/nixarr/lib/arr-settings-options.nix new file mode 100644 index 0000000..290d433 --- /dev/null +++ b/nixarr/lib/arr-settings-options.nix @@ -0,0 +1,93 @@ +# Utilities for defining *arr service settings, options, and configs. +# +# Copied from nixpkgs master as of 2025-11-11: https://raw.githubusercontent.com/NixOS/nixpkgs/cf540f8c9840457ed90a315dd635bceecb78495a/nixos/modules/services/misc/servarr/settings-options.nix +{ + lib, + pkgs, +}: { + mkServarrSettingsOptions = name: port: + lib.mkOption { + type = lib.types.submodule { + freeformType = (pkgs.formats.ini {}).type; + options = { + update = { + mechanism = lib.mkOption { + type = with lib.types; + nullOr (enum [ + "external" + "builtIn" + "script" + ]); + description = "which update mechanism to use"; + default = "external"; + }; + automatically = lib.mkOption { + type = lib.types.bool; + description = "Automatically download and install updates."; + default = false; + }; + }; + server = { + port = lib.mkOption { + type = lib.types.port; + description = "Port Number"; + default = port; + }; + }; + log = { + analyticsEnabled = lib.mkOption { + type = lib.types.bool; + description = "Send Anonymous Usage Data"; + default = false; + }; + }; + }; + }; + example = lib.options.literalExpression '' + { + update.mechanism = "internal"; + server = { + urlbase = "localhost"; + port = ${toString port}; + bindaddress = "*"; + }; + } + ''; + default = {}; + description = '' + Attribute set of arbitrary config options. + Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config). + + WARNING: this configuration is stored in the world-readable Nix store! + For secrets use [](#opt-services.${name}.environmentFiles). + ''; + }; + + mkServarrEnvironmentFiles = name: + lib.mkOption { + type = lib.types.listOf lib.types.path; + default = []; + description = '' + Environment file to pass secret configuration values. + Each line must follow the `${lib.toUpper name}__SECTION__KEY=value` pattern. + Please consult the documentation at the [wiki](https://wiki.servarr.com/useful-tools#using-environment-variables-for-config). + ''; + }; + + mkServarrSettingsEnvVars = name: settings: + lib.pipe settings [ + (lib.mapAttrsRecursive ( + path: value: + lib.optionalAttrs (value != null) { + name = lib.toUpper "${name}__${lib.concatStringsSep "__" path}"; + value = toString ( + if lib.isBool value + then lib.boolToString value + else value + ); + } + )) + (lib.collect (x: lib.isString x.name or false && lib.isString x.value or false)) + lib.listToAttrs + ]; +} diff --git a/nixarr/readarr-audiobook/default.nix b/nixarr/readarr-audiobook/default.nix index 6f75ea6..92e9d68 100644 --- a/nixarr/readarr-audiobook/default.nix +++ b/nixarr/readarr-audiobook/default.nix @@ -6,9 +6,12 @@ }: with lib; let cfg = config.nixarr.readarr-audiobook; + service-cfg = config.services.readarr-audiobook; globals = config.util-nixarr.globals; nixarr = config.nixarr; port = 9494; + + arr-settings-options = import ../lib/arr-settings-options.nix {inherit lib pkgs;}; in { options.nixarr.readarr-audiobook = { enable = mkOption { @@ -69,6 +72,46 @@ in { }; }; + # A tweaked copy of services.readarr from nixpkgs + options.services.readarr-audiobook = { + enable = lib.mkEnableOption "Readarr-Audiobook, a Usenet/BitTorrent audiobook downloader"; + + dataDir = lib.mkOption { + type = lib.types.str; + description = "The directory where Readarr-Audiobook stores its data files."; + }; + + package = lib.mkPackageOption pkgs "readarr" {}; + + openFirewall = lib.mkOption { + type = lib.types.bool; + description = '' + Open ports in the firewall for Readarr-Audiobook. + ''; + }; + + # Uses name in description to refer to + # `services.readarr-audiobook.environmentFiles`. + settings = arr-settings-options.mkServarrSettingsOptions "readarr-audiobook" port; + + # Uses name in description to document `READARR__*` environment variables. + environmentFiles = arr-settings-options.mkServarrEnvironmentFiles "readarr"; + + user = lib.mkOption { + type = lib.types.str; + description = '' + User account under which Readarr-Audiobook runs. + ''; + }; + + group = lib.mkOption { + type = lib.types.str; + description = '' + Group under which Readarr-Audiobook runs. + ''; + }; + }; + config = mkIf (nixarr.enable && cfg.enable) { assertions = [ { @@ -96,17 +139,30 @@ in { "d '${nixarr.mediaDir}/library/audiobooks' 0775 ${globals.libraryOwner.user} ${globals.libraryOwner.group} - -" ]; + services.readarr-audiobook = { + enable = cfg.enable; + package = cfg.package; + settings.server.port = cfg.port; + openFirewall = cfg.openFirewall; + dataDir = cfg.stateDir; + user = globals.readarr-audiobook.user; + group = globals.readarr-audiobook.group; + }; + systemd.services.readarr-audiobook = { description = "Readarr-Audiobook"; after = ["network.target"]; wantedBy = ["multi-user.target"]; - environment.READARR__SERVER__PORT = builtins.toString cfg.port; + + # Uses name to define `READARR__*` environment variables. + environment = arr-settings-options.mkServarrSettingsEnvVars "readarr" service-cfg.settings; serviceConfig = { Type = "simple"; - User = globals.readarr-audiobook.user; - Group = globals.readarr-audiobook.group; - ExecStart = "${lib.getExe cfg.package} -nobrowser -data=${cfg.stateDir}"; + User = service-cfg.user; + Group = service-cfg.group; + EnvironmentFile = service-cfg.environmentFiles; + ExecStart = "${lib.getExe service-cfg.package} -nobrowser -data=${service-cfg.dataDir}"; Restart = "on-failure"; }; }; diff --git a/nixarr/readarr/default.nix b/nixarr/readarr/default.nix index a8e4606..c14b46a 100644 --- a/nixarr/readarr/default.nix +++ b/nixarr/readarr/default.nix @@ -94,19 +94,14 @@ in { "d '${nixarr.mediaDir}/library/books' 0775 ${globals.libraryOwner.user} ${globals.libraryOwner.group} - -" ]; - systemd.services.readarr = { - description = "Readarr"; - after = ["network.target"]; - wantedBy = ["multi-user.target"]; - environment.READARR__SERVER__PORT = builtins.toString cfg.port; - - serviceConfig = { - Type = "simple"; - User = globals.readarr.user; - Group = globals.readarr.group; - ExecStart = "${lib.getExe cfg.package} -nobrowser -data=${cfg.stateDir}"; - Restart = "on-failure"; - }; + services.readarr = { + enable = cfg.enable; + package = cfg.package; + settings.server.port = cfg.port; + openFirewall = cfg.openFirewall; + dataDir = cfg.stateDir; + user = globals.readarr.user; + group = globals.readarr.group; }; networking.firewall = mkIf cfg.openFirewall {