Skip to content

Commit c7c8d44

Browse files
committed
add sabnzbd configuration
1 parent 3b3084a commit c7c8d44

8 files changed

Lines changed: 995 additions & 2 deletions

File tree

modules/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
./postgres.nix
88
./prowlarr
99
./radarr
10+
./sabnzbd
1011
./sonarr
1112
];
1213
}

modules/nixflix.nix

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,24 @@ in {
7171
'';
7272
};
7373

74+
downloadsDir = mkOption {
75+
type = types.path;
76+
default = "/data/downloads";
77+
example = "/data/downloads";
78+
description = ''
79+
The location of the downloads directory for download clients.
80+
81+
> **Warning:** Setting this to any path, where the subpath is not
82+
> owned by root, will fail! For example:
83+
>
84+
> ```nix
85+
> downloadsDir = /home/user/downloads
86+
> ```
87+
>
88+
> Is not supported, because `/home/user` is owned by `user`.
89+
'';
90+
};
91+
7492
stateDir = mkOption {
7593
type = types.path;
7694
default = "/data/.state";
@@ -145,6 +163,10 @@ in {
145163
${pkgs.coreutils}/bin/chown ${globals.libraryOwner.user}:${globals.libraryOwner.group} ${cfg.mediaDir}
146164
${pkgs.coreutils}/bin/chmod 0775 ${cfg.mediaDir}
147165
166+
${pkgs.coreutils}/bin/mkdir -p ${cfg.downloadsDir}
167+
${pkgs.coreutils}/bin/chown ${globals.libraryOwner.user}:${globals.libraryOwner.group} ${cfg.downloadsDir}
168+
${pkgs.coreutils}/bin/chmod 0775 ${cfg.downloadsDir}
169+
148170
# Service-registered directories
149171
${concatMapStringsSep "\n" (reg: ''
150172
${pkgs.coreutils}/bin/mkdir -p ${reg.dir}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
{
2+
pkgs,
3+
lib,
4+
cfg,
5+
}:
6+
with lib; let
7+
configureScript = pkgs.writers.writePython3Bin "sabnzbd-configure-api" {
8+
libraries = with pkgs.python3Packages; [requests];
9+
} ''
10+
import requests
11+
import time
12+
import sys
13+
import os
14+
import json
15+
16+
# Read API key from environment or file
17+
API_KEY = os.environ.get("SABNZBD_API_KEY", "")
18+
BASE_URL = os.environ.get("SABNZBD_BASE_URL", "")
19+
20+
21+
def api_call(mode, **params):
22+
"""Make an API call to SABnzbd"""
23+
params.update({"mode": mode, "apikey": API_KEY, "output": "json"})
24+
try:
25+
response = requests.get(BASE_URL, params=params, timeout=10)
26+
response.raise_for_status()
27+
return response.json()
28+
except Exception as e:
29+
print(f"API call failed for mode={mode}: {e}", file=sys.stderr)
30+
return None
31+
32+
33+
def wait_for_sabnzbd():
34+
"""Wait for SABnzbd to be ready"""
35+
print("Waiting for SABnzbd to be ready...")
36+
for i in range(60):
37+
try:
38+
result = api_call("version")
39+
if result and "version" in result:
40+
print(f"SABnzbd is ready (version: {result['version']})")
41+
return True
42+
except Exception:
43+
pass
44+
time.sleep(1)
45+
print("SABnzbd did not become ready in time", file=sys.stderr)
46+
return False
47+
48+
49+
def set_config(section, keyword, value):
50+
"""Set a configuration value"""
51+
result = api_call("set_config", section=section, keyword=keyword,
52+
value=value)
53+
if result:
54+
print(f"Set {section}.{keyword} = {value}")
55+
return True
56+
return False
57+
58+
59+
def configure_misc_settings():
60+
"""Configure misc settings via API"""
61+
print("Configuring misc settings...")
62+
settings_json = os.environ.get("SETTINGS_JSON", "{}")
63+
settings = json.loads(settings_json)
64+
65+
for key, value in settings.items():
66+
# Convert booleans to 0/1
67+
if isinstance(value, bool):
68+
value = 1 if value else 0
69+
set_config("misc", key, str(value))
70+
71+
72+
def configure_servers():
73+
"""Configure SABnzbd servers via API"""
74+
print("Configuring servers...")
75+
servers_json = os.environ.get("SERVERS_JSON", "[]")
76+
servers = json.loads(servers_json)
77+
78+
for server in servers:
79+
name = server["name"]
80+
# Read secrets from environment if they're placeholders
81+
username = server["username"]
82+
password = server["password"]
83+
84+
if username.startswith("$"):
85+
username = os.environ.get(username[1:], username)
86+
if password.startswith("$"):
87+
password = os.environ.get(password[1:], password)
88+
89+
params = {
90+
"section": "servers",
91+
"name": name,
92+
"host": server["host"],
93+
"port": server["port"],
94+
"username": username,
95+
"password": password,
96+
"connections": server["connections"],
97+
"ssl": 1 if server["ssl"] else 0,
98+
"priority": server["priority"],
99+
"optional": 1 if server["optional"] else 0,
100+
"retention": server["retention"],
101+
"enable": 1 if server["enable"] else 0,
102+
}
103+
104+
result = api_call("set_config", **params)
105+
if result:
106+
print(f"Configured server: {name}")
107+
108+
109+
def configure_categories():
110+
"""Configure SABnzbd categories via API"""
111+
print("Configuring categories...")
112+
categories_json = os.environ.get("CATEGORIES_JSON", "[]")
113+
categories = json.loads(categories_json)
114+
115+
for category in categories:
116+
name = category["name"]
117+
params = {
118+
"section": "categories",
119+
"name": name,
120+
"dir": category["dir"],
121+
"priority": category["priority"],
122+
"pp": category["pp"],
123+
"script": category["script"],
124+
}
125+
126+
result = api_call("set_config", **params)
127+
if result:
128+
print(f"Configured category: {name}")
129+
130+
131+
if __name__ == "__main__":
132+
if not wait_for_sabnzbd():
133+
sys.exit(1)
134+
135+
configure_misc_settings()
136+
configure_servers()
137+
configure_categories()
138+
139+
print("SABnzbd configuration completed successfully")
140+
'';
141+
in {
142+
script = configureScript;
143+
144+
serviceConfig = {
145+
description = "Configure SABnzbd via API";
146+
after = ["sabnzbd.service"];
147+
wants = ["sabnzbd.service"];
148+
wantedBy = ["multi-user.target"];
149+
150+
serviceConfig = {
151+
Type = "oneshot";
152+
RemainAfterExit = true;
153+
};
154+
155+
script = ''
156+
# Export API key
157+
${optionalString (cfg.apiKeyPath != null) ''
158+
export SABNZBD_API_KEY=$(${pkgs.coreutils}/bin/cat ${cfg.apiKeyPath})
159+
''}
160+
161+
# Export base URL
162+
export SABNZBD_BASE_URL="http://${cfg.settings.host}:${toString cfg.settings.port}${cfg.settings.url_base}/api"
163+
164+
# Export configuration as JSON
165+
export SETTINGS_JSON='${builtins.toJSON (filterAttrs (n: v: n != "servers" && n != "categories") cfg.settings)}'
166+
export SERVERS_JSON='${builtins.toJSON cfg.settings.servers}'
167+
export CATEGORIES_JSON='${builtins.toJSON cfg.settings.categories}'
168+
169+
# Export environment secrets
170+
${concatMapStringsSep "\n" (secret: ''
171+
export ${secret.env}=$(${pkgs.coreutils}/bin/cat ${secret.path})
172+
'') cfg.environmentSecrets}
173+
174+
# Run configuration script
175+
${configureScript}/bin/sabnzbd-configure-api
176+
'';
177+
};
178+
}

0 commit comments

Comments
 (0)