Files
arr-init/modules/jellyseerr.nix
2026-04-17 00:38:32 -04:00

192 lines
5.4 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.jellyseerrInit;
scriptDir = ../scripts;
pythonEnv = pkgs.python3.withPackages (
ps: with ps; [
pyyaml
requests
]
);
jellyseerrProviderModule = lib.types.submodule {
options = {
profileName = lib.mkOption {
type = lib.types.str;
description = "Quality profile name to set as the default. Resolved to an ID at runtime by querying the Servarr API.";
example = "Remux + WEB 2160p";
};
dataDir = lib.mkOption {
type = lib.types.str;
description = "Path to the Servarr application data directory containing config.xml.";
example = "/services/radarr";
};
bindAddress = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "IP address the Servarr application API is listening on.";
};
port = lib.mkOption {
type = lib.types.port;
description = "API port of the Servarr application.";
example = 7878;
};
serviceName = lib.mkOption {
type = lib.types.str;
description = "Name of the systemd service to depend on.";
example = "radarr";
};
};
};
jellyseerrInitModule = lib.types.submodule {
options = {
enable = lib.mkEnableOption "Jellyseerr quality profile initialization";
configDir = lib.mkOption {
type = lib.types.str;
description = "Path to Jellyseerr's data directory containing settings.json.";
example = "/services/jellyseerr";
};
apiTimeout = lib.mkOption {
type = lib.types.ints.positive;
default = 90;
description = "Seconds to wait for Radarr/Sonarr APIs to become available.";
};
radarr = lib.mkOption {
type = jellyseerrProviderModule;
description = "Radarr quality profile configuration for Jellyseerr.";
};
sonarr = lib.mkOption {
type = lib.types.submodule {
options = {
profileName = lib.mkOption {
type = lib.types.str;
description = "Quality profile name for TV series.";
example = "WEB-2160p";
};
animeProfileName = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Quality profile name for anime. Defaults to profileName when null.";
};
dataDir = lib.mkOption {
type = lib.types.str;
description = "Path to the Sonarr data directory containing config.xml.";
example = "/services/sonarr";
};
bindAddress = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "IP address the Sonarr API is listening on.";
};
port = lib.mkOption {
type = lib.types.port;
description = "API port of Sonarr.";
example = 8989;
};
serviceName = lib.mkOption {
type = lib.types.str;
description = "Name of the systemd service to depend on.";
example = "sonarr";
};
};
};
description = "Sonarr quality profile configuration for Jellyseerr.";
};
};
};
mkJellyseerrInitConfig = builtins.toJSON {
configDir = cfg.configDir;
apiTimeout = cfg.apiTimeout;
radarr = {
profileName = cfg.radarr.profileName;
dataDir = cfg.radarr.dataDir;
bindAddress = cfg.radarr.bindAddress;
port = cfg.radarr.port;
};
sonarr = {
profileName = cfg.sonarr.profileName;
animeProfileName =
if cfg.sonarr.animeProfileName != null then cfg.sonarr.animeProfileName else cfg.sonarr.profileName;
dataDir = cfg.sonarr.dataDir;
bindAddress = cfg.sonarr.bindAddress;
port = cfg.sonarr.port;
};
};
configFile = pkgs.writeText "jellyseerr-init-config.json" mkJellyseerrInitConfig;
jellyseerrDeps = [
"jellyseerr.service"
"${cfg.radarr.serviceName}.service"
"${cfg.sonarr.serviceName}.service"
];
hardeningConfig = {
PrivateTmp = true;
NoNewPrivileges = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictSUIDSGID = true;
ProtectHome = true;
SystemCallArchitectures = "native";
};
in
{
options.services.jellyseerrInit = lib.mkOption {
type = jellyseerrInitModule;
default = {
enable = false;
};
description = ''
Jellyseerr quality profile initialization.
Patches Jellyseerr's settings.json so new requests default to the
correct Radarr/Sonarr quality profiles, resolved by name at runtime.
'';
};
config = lib.mkIf cfg.enable {
systemd.services.jellyseerr-init = {
description = "Initialize Jellyseerr quality profile defaults";
after = jellyseerrDeps;
requires = jellyseerrDeps;
wantedBy = [ "multi-user.target" ];
environment.PYTHONPATH = "${scriptDir}";
unitConfig = {
StartLimitIntervalSec = 5 * (cfg.apiTimeout + 30);
StartLimitBurst = 5;
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
Restart = "on-failure";
RestartSec = 30;
ExecStart = "${pythonEnv}/bin/python3 ${scriptDir}/jellyseerr_init.py ${configFile}";
}
// hardeningConfig;
};
};
}