From a5007d9d3772d24a5a1b19d9789a247ee3a3adfa Mon Sep 17 00:00:00 2001 From: Edward Pierzchalski Date: Tue, 11 Nov 2025 07:39:00 +1100 Subject: [PATCH 1/4] readarr: use the NixOS module as much as possible See also this PR doing something similar for `prowlarr`: https://github.com/rasmus-kirk/nixarr/pull/95 --- nixarr/readarr/default.nix | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) 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 { From 4b2316b18af34a0c22caa699763eb83c85682fe3 Mon Sep 17 00:00:00 2001 From: Edward Pierzchalski Date: Tue, 11 Nov 2025 08:24:48 +1100 Subject: [PATCH 2/4] readarr-audiobook: add service.readarr-audiobook to mirror other *arr services This lets us use `services.readarr-audiobook` to configure it the same way we can use `services.{readarr,radarr,sonarr,prowlarr}`. --- nixarr/readarr-audiobook/default.nix | 63 ++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/nixarr/readarr-audiobook/default.nix b/nixarr/readarr-audiobook/default.nix index 6f75ea6..568baa6 100644 --- a/nixarr/readarr-audiobook/default.nix +++ b/nixarr/readarr-audiobook/default.nix @@ -6,9 +6,17 @@ }: with lib; let cfg = config.nixarr.readarr-audiobook; + service-cfg = config.services.readarr-audiobook; globals = config.util-nixarr.globals; nixarr = config.nixarr; port = 9494; + + settings-options = pkgs.fetchurl { + # Master as of 2025-11-11 + url = "https://raw.githubusercontent.com/NixOS/nixpkgs/cf540f8c9840457ed90a315dd635bceecb78495a/nixos/modules/services/misc/servarr/settings-options.nix"; + hash = "sha256-7fh2fpYphR7kBV0zleRK+gL8gHLqVbjRbuv1B6x748s="; + }; + servarr = import settings-options {inherit lib pkgs;}; in { options.nixarr.readarr-audiobook = { enable = mkOption { @@ -69,6 +77,42 @@ 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. + ''; + }; + + settings = servarr.mkServarrSettingsOptions "readarr-audiobook" port; + environmentFiles = servarr.mkServarrEnvironmentFiles "readarr-audiobook"; + + 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 +140,28 @@ 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; + environment = servarr.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"; }; }; From d8ca911a13f7849ddce3d0819b8a2e2a3e25b12f Mon Sep 17 00:00:00 2001 From: Edward Pierzchalski Date: Fri, 14 Nov 2025 10:48:44 +1100 Subject: [PATCH 3/4] [readarr-audiobook] fix one call of `mkServarr` utility functions, document which calls need `readarr` vs `readarr-audiobook` and why --- nixarr/readarr-audiobook/default.nix | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/nixarr/readarr-audiobook/default.nix b/nixarr/readarr-audiobook/default.nix index 568baa6..c32c5ab 100644 --- a/nixarr/readarr-audiobook/default.nix +++ b/nixarr/readarr-audiobook/default.nix @@ -95,8 +95,12 @@ in { ''; }; + # Uses name in description to refer to + # `services.readarr-audiobook.environmentFiles`. settings = servarr.mkServarrSettingsOptions "readarr-audiobook" port; - environmentFiles = servarr.mkServarrEnvironmentFiles "readarr-audiobook"; + + # Uses name in description to document `READARR__*` environment variables. + environmentFiles = servarr.mkServarrEnvironmentFiles "readarr"; user = lib.mkOption { type = lib.types.str; @@ -154,7 +158,9 @@ in { description = "Readarr-Audiobook"; after = ["network.target"]; wantedBy = ["multi-user.target"]; - environment = servarr.mkServarrSettingsEnvVars "READARR" service-cfg.settings; + + # Uses name to define `READARR__*` environment variables. + environment = servarr.mkServarrSettingsEnvVars "readarr" service-cfg.settings; serviceConfig = { Type = "simple"; From 4d1d1cfe76bd2b89f02941e142063afcab876f79 Mon Sep 17 00:00:00 2001 From: Edward Pierzchalski Date: Sat, 22 Nov 2025 10:00:09 +1100 Subject: [PATCH 4/4] lib, readarr-audiobook: copy *arr helpers from nixpkgs instead of downloading them --- nixarr/lib/arr-settings-options.nix | 93 ++++++++++++++++++++++++++++ nixarr/readarr-audiobook/default.nix | 13 ++-- 2 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 nixarr/lib/arr-settings-options.nix 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 c32c5ab..92e9d68 100644 --- a/nixarr/readarr-audiobook/default.nix +++ b/nixarr/readarr-audiobook/default.nix @@ -11,12 +11,7 @@ with lib; let nixarr = config.nixarr; port = 9494; - settings-options = pkgs.fetchurl { - # Master as of 2025-11-11 - url = "https://raw.githubusercontent.com/NixOS/nixpkgs/cf540f8c9840457ed90a315dd635bceecb78495a/nixos/modules/services/misc/servarr/settings-options.nix"; - hash = "sha256-7fh2fpYphR7kBV0zleRK+gL8gHLqVbjRbuv1B6x748s="; - }; - servarr = import settings-options {inherit lib pkgs;}; + arr-settings-options = import ../lib/arr-settings-options.nix {inherit lib pkgs;}; in { options.nixarr.readarr-audiobook = { enable = mkOption { @@ -97,10 +92,10 @@ in { # Uses name in description to refer to # `services.readarr-audiobook.environmentFiles`. - settings = servarr.mkServarrSettingsOptions "readarr-audiobook" port; + settings = arr-settings-options.mkServarrSettingsOptions "readarr-audiobook" port; # Uses name in description to document `READARR__*` environment variables. - environmentFiles = servarr.mkServarrEnvironmentFiles "readarr"; + environmentFiles = arr-settings-options.mkServarrEnvironmentFiles "readarr"; user = lib.mkOption { type = lib.types.str; @@ -160,7 +155,7 @@ in { wantedBy = ["multi-user.target"]; # Uses name to define `READARR__*` environment variables. - environment = servarr.mkServarrSettingsEnvVars "readarr" service-cfg.settings; + environment = arr-settings-options.mkServarrSettingsEnvVars "readarr" service-cfg.settings; serviceConfig = { Type = "simple";