33 lib ,
44 cfg ,
55} :
6- with lib ; let
7- configureScript =
8- pkgs . writers . writePython3Bin "sabnzbd-configure-api" {
9- libraries = with pkgs . python3Packages ; [ requests ] ;
10- } ''
11- import requests
12- import time
13- import sys
14- import os
15- import json
16-
17- # Read API key from environment or file
18- API_KEY = os.environ.get("SABNZBD_API_KEY", "")
19- BASE_URL = os.environ.get("SABNZBD_BASE_URL", "")
20-
21-
22- def api_call(mode, **params):
23- """Make an API call to SABnzbd"""
24- params.update({"mode": mode, "apikey": API_KEY, "output": "json"})
25- try:
26- response = requests.get(BASE_URL, params=params, timeout=10)
27- response.raise_for_status()
28- return response.json()
29- except Exception as e:
30- print(f"API call failed for mode={mode}: {e}", file=sys.stderr)
31- return None
32-
33-
34- def wait_for_sabnzbd():
35- """Wait for SABnzbd to be ready"""
36- print("Waiting for SABnzbd to be ready...")
37- for i in range(60):
38- try:
39- result = api_call("version")
40- if result and "version" in result:
41- print(f"SABnzbd is ready (version: {result['version']})")
42- return True
43- except Exception:
44- pass
45- time.sleep(1)
46- print("SABnzbd did not become ready in time", file=sys.stderr)
47- return False
48-
49-
50- def set_config(section, keyword, value):
51- """Set a configuration value"""
52- result = api_call("set_config", section=section, keyword=keyword,
53- value=value)
54- if result:
55- print(f"Set {section}.{keyword} = {value}")
56- return True
57- return False
58-
59-
60- def configure_misc_settings():
61- """Configure misc settings via API"""
62- print("Configuring misc settings...")
63- settings_json = os.environ.get("SETTINGS_JSON", "{}")
64- settings = json.loads(settings_json)
65-
66- for key, value in settings.items():
67- # Convert booleans to 0/1
68- if isinstance(value, bool):
69- value = 1 if value else 0
70- set_config("misc", key, str(value))
71-
72-
73- def configure_servers():
74- """Configure SABnzbd servers via API"""
75- print("Configuring servers...")
76- servers_json = os.environ.get("SERVERS_JSON", "[]")
77- servers = json.loads(servers_json)
78-
79- for server in servers:
80- name = server["name"]
81- # Read secrets from environment if they're placeholders
82- username = server["username"]
83- password = server["password"]
84-
85- if username.startswith("$"):
86- username = os.environ.get(username[1:], username)
87- if password.startswith("$"):
88- password = os.environ.get(password[1:], password)
89-
90- params = {
91- "section": "servers",
92- "name": name,
93- "host": server["host"],
94- "port": server["port"],
95- "username": username,
96- "password": password,
97- "connections": server["connections"],
98- "ssl": 1 if server["ssl"] else 0,
99- "priority": server["priority"],
100- "optional": 1 if server["optional"] else 0,
101- "retention": server["retention"],
102- "enable": 1 if server["enable"] else 0,
103- }
104-
105- result = api_call("set_config", **params)
106- if result:
107- print(f"Configured server: {name}")
108-
109-
110- def configure_categories():
111- """Configure SABnzbd categories via API"""
112- print("Configuring categories...")
113- categories_json = os.environ.get("CATEGORIES_JSON", "[]")
114- categories = json.loads(categories_json)
115-
116- for category in categories:
117- name = category["name"]
118- params = {
119- "section": "categories",
120- "name": name,
121- "dir": category["dir"],
122- "priority": category["priority"],
123- "pp": category["pp"],
124- "script": category["script"],
125- }
126-
127- result = api_call("set_config", **params)
128- if result:
129- print(f"Configured category: {name}")
130-
131-
132- if __name__ == "__main__":
133- if not wait_for_sabnzbd():
134- sys.exit(1)
135-
136- configure_misc_settings()
137- configure_servers()
138- configure_categories()
139-
140- print("SABnzbd configuration completed successfully")
141- '' ;
142- in {
143- script = configureScript ;
144-
6+ with lib ; {
1457 serviceConfig = {
1468 description = "Configure SABnzbd via API" ;
1479 after = [ "sabnzbd.service" ] ;
@@ -154,27 +16,136 @@ in {
15416 } ;
15517
15618 script = ''
157- # Export API key
19+ set -eu
20+
21+ # Read API key from file
15822 ${ optionalString ( cfg . apiKeyPath != null ) ''
159- export SABNZBD_API_KEY=$(${ pkgs . coreutils } /bin/cat ${ cfg . apiKeyPath } )
23+ API_KEY=$(cat ${ cfg . apiKeyPath } )
24+ '' }
25+ ${ optionalString ( cfg . apiKeyPath == null ) ''
26+ API_KEY=""
16027 '' }
16128
162- # Export base URL
163- export SABNZBD_BASE_URL="http://${ cfg . settings . host } :${ toString cfg . settings . port } ${ cfg . settings . url_base } /api"
164-
165- # Export configuration as JSON
166- export SETTINGS_JSON='${ builtins . toJSON ( filterAttrs ( n : v : n != "servers" && n != "categories" ) cfg . settings ) } '
167- export SERVERS_JSON='${ builtins . toJSON cfg . settings . servers } '
168- export CATEGORIES_JSON='${ builtins . toJSON cfg . settings . categories } '
29+ BASE_URL="http://${ cfg . settings . host } :${ toString cfg . settings . port } ${ cfg . settings . url_base } /api"
16930
17031 # Export environment secrets
17132 ${ concatMapStringsSep "\n " ( secret : ''
172- export ${ secret . env } =$(${ pkgs . coreutils } /bin/ cat ${ secret . path } )
33+ export ${ secret . env } =$(cat ${ secret . path } )
17334 '' )
17435 cfg . environmentSecrets }
17536
176- # Run configuration script
177- ${ configureScript } /bin/sabnzbd-configure-api
37+ # Function to make API calls
38+ api_call() {
39+ local mode="$1"
40+ shift
41+ local params="mode=$mode&apikey=$API_KEY&output=json"
42+
43+ while [[ $# -gt 0 ]]; do
44+ params="$params&$1"
45+ shift
46+ done
47+
48+ ${ pkgs . curl } /bin/curl -s -f -m 10 "$BASE_URL?$params" 2>&1 || return 1
49+ }
50+
51+ # Wait for SABnzbd to be ready
52+ echo "Waiting for SABnzbd API to be available..."
53+ for i in {1..60}; do
54+ if result=$(api_call "version" 2>/dev/null); then
55+ if version=$(echo "$result" | ${ pkgs . jq } /bin/jq -r '.version // empty' 2>/dev/null); then
56+ if [[ -n "$version" ]]; then
57+ echo "SABnzbd is ready (version: $version)"
58+ break
59+ fi
60+ fi
61+ fi
62+ if [[ $i -eq 60 ]]; then
63+ echo "SABnzbd did not become ready in time" >&2
64+ exit 1
65+ fi
66+ sleep 1
67+ done
68+
69+ # Configure misc settings
70+ echo "Configuring misc settings..."
71+ SETTINGS_JSON='${ builtins . toJSON ( filterAttrs ( n : v : n != "servers" && n != "categories" ) cfg . settings ) } '
72+
73+ echo "$SETTINGS_JSON" | ${ pkgs . jq } /bin/jq -r 'to_entries[] | "\(.key)=\(.value)"' | while IFS='=' read -r key value; do
74+ # Convert booleans to 0/1
75+ if [[ "$value" == "true" ]]; then
76+ value="1"
77+ elif [[ "$value" == "false" ]]; then
78+ value="0"
79+ fi
80+
81+ encoded_value=$(echo -n "$value" | ${ pkgs . jq } /bin/jq -sRr @uri)
82+ if api_call "set_config" "section=misc" "keyword=$key" "value=$encoded_value" >/dev/null 2>&1; then
83+ echo "Set misc.$key = $value"
84+ fi
85+ done
86+
87+ # Configure servers
88+ echo "Configuring servers..."
89+ SERVERS_JSON='${ builtins . toJSON cfg . settings . servers } '
90+
91+ echo "$SERVERS_JSON" | ${ pkgs . jq } /bin/jq -c '.[]' | while IFS= read -r server; do
92+ # Extract name for logging and env var substitution
93+ name=$(echo "$server" | ${ pkgs . jq } /bin/jq -r '.name')
94+ username=$(echo "$server" | ${ pkgs . jq } /bin/jq -r '.username')
95+ password=$(echo "$server" | ${ pkgs . jq } /bin/jq -r '.password')
96+
97+ # Handle environment variable substitution
98+ if [[ "$username" =~ ^\$ ]]; then
99+ var_name="'' ${username:1}"
100+ username="'' ${!var_name}"
101+ fi
102+ if [[ "$password" =~ ^\$ ]]; then
103+ var_name="'' ${password:1}"
104+ password="'' ${!var_name}"
105+ fi
106+
107+ # Build parameters using jq, converting booleans to 0/1 and URL encoding
108+ params=$(echo "$server" | ${ pkgs . jq } /bin/jq -r \
109+ --arg username "$username" \
110+ --arg password "$password" \
111+ '
112+ . + {username: $username, password: $password}
113+ | to_entries
114+ | map(
115+ if .value == true then .value = "1"
116+ elif .value == false then .value = "0"
117+ else .
118+ end
119+ )
120+ | map("\(.key)=\(.value | tostring | @uri)")
121+ | join("&")
122+ ')
123+
124+ if api_call "set_config" "section=servers" "$params" >/dev/null 2>&1; then
125+ echo "Configured server: $name"
126+ fi
127+ done
128+
129+ # Configure categories
130+ echo "Configuring categories..."
131+ CATEGORIES_JSON='${ builtins . toJSON cfg . settings . categories } '
132+
133+ echo "$CATEGORIES_JSON" | ${ pkgs . jq } /bin/jq -c '.[]' | while IFS= read -r category; do
134+ name=$(echo "$category" | ${ pkgs . jq } /bin/jq -r '.name')
135+
136+ # Build parameters using jq, URL encoding values
137+ params=$(echo "$category" | ${ pkgs . jq } /bin/jq -r '
138+ to_entries
139+ | map("\(.key)=\(.value | tostring | @uri)")
140+ | join("&")
141+ ')
142+
143+ if api_call "set_config" "section=categories" "$params" >/dev/null 2>&1; then
144+ echo "Configured category: $name"
145+ fi
146+ done
147+
148+ echo "SABnzbd configuration completed successfully"
178149 '' ;
179150 } ;
180151}
0 commit comments