{ config, lib, pkgs, ... }: let cfg = config.services.bazarrInit; scriptDir = ../scripts; pythonEnv = pkgs.python3.withPackages ( ps: with ps; [ pyyaml requests ] ); bazarrProviderModule = lib.types.submodule { options = { enable = lib.mkEnableOption "provider connection"; dataDir = lib.mkOption { type = lib.types.str; description = "Path to the provider's data directory containing config.xml."; example = "/services/sonarr"; }; bindAddress = lib.mkOption { type = lib.types.str; default = "127.0.0.1"; description = "IP address of the provider (Sonarr/Radarr) for Bazarr to connect to."; }; port = lib.mkOption { type = lib.types.port; description = "API port of the provider."; example = 8989; }; serviceName = lib.mkOption { type = lib.types.str; description = "Name of the systemd service to depend on."; example = "sonarr"; }; }; }; bazarrInitModule = lib.types.submodule { options = { enable = lib.mkEnableOption "Bazarr API initialization"; dataDir = lib.mkOption { type = lib.types.str; description = "Path to Bazarr's data directory containing config/config.yaml."; example = "/services/bazarr"; }; bindAddress = lib.mkOption { type = lib.types.str; default = "127.0.0.1"; description = "IP address the Bazarr API is listening on."; }; port = lib.mkOption { type = lib.types.port; default = 6767; description = "API port of Bazarr."; }; apiTimeout = lib.mkOption { type = lib.types.ints.positive; default = 90; description = '' Seconds to wait for the Bazarr API to become available before considering the init attempt failed. When the API is not reachable within this window, the service exits non-zero and systemd's Restart=on-failure will schedule another attempt after RestartSec. The systemd start limit is computed from this value to allow 5 full retry cycles before the unit enters permanent failure. ''; }; sonarr = lib.mkOption { type = bazarrProviderModule; default = { enable = false; }; description = "Sonarr provider configuration."; }; radarr = lib.mkOption { type = bazarrProviderModule; default = { enable = false; }; description = "Radarr provider configuration."; }; }; }; mkBazarrInitConfig = builtins.toJSON { dataDir = cfg.dataDir; bindAddress = cfg.bindAddress; port = cfg.port; apiTimeout = cfg.apiTimeout; providers = { } // lib.optionalAttrs cfg.sonarr.enable { sonarr = { enable = true; dataDir = cfg.sonarr.dataDir; bindAddress = cfg.sonarr.bindAddress; port = cfg.sonarr.port; }; } // lib.optionalAttrs cfg.radarr.enable { radarr = { enable = true; dataDir = cfg.radarr.dataDir; bindAddress = cfg.radarr.bindAddress; port = cfg.radarr.port; }; }; }; configFile = pkgs.writeText "bazarr-init-config.json" mkBazarrInitConfig; bazarrDeps = [ "bazarr.service" ] ++ (lib.optional cfg.sonarr.enable "${cfg.sonarr.serviceName}.service") ++ (lib.optional cfg.radarr.enable "${cfg.radarr.serviceName}.service"); hardeningConfig = { PrivateTmp = true; NoNewPrivileges = true; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; RestrictSUIDSGID = true; ProtectHome = true; SystemCallArchitectures = "native"; }; in { options.services.bazarrInit = lib.mkOption { type = bazarrInitModule; default = { enable = false; }; description = '' Bazarr API initialization for connecting Sonarr and Radarr providers. Bazarr uses a different API than Servarr applications, so it has its own module. ''; }; config = lib.mkIf cfg.enable { systemd.services.bazarr-init = { description = "Initialize Bazarr API connections"; after = bazarrDeps; requires = bazarrDeps; 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}/bazarr_init.py ${configFile}"; } // hardeningConfig; }; }; }