{ 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; }; }; }