#!/usr/bin/env bash # Bootstrap SABnzbd folders + Usenet servers from secrets/.env # Run on the stack host after: docker compose up -d sabnzbd # # ./scripts/configure-usenet.sh # SECRETS_FILE=./secrets/.env SAB_URL=http://127.0.0.1:8085 ./scripts/configure-usenet.sh set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" STACK_DIR="${STACK_DIR:-$(cd "$SCRIPT_DIR/.." && pwd)}" SECRETS_FILE="${SECRETS_FILE:-$STACK_DIR/secrets/.env}" SAB_URL="${SAB_URL:-http://127.0.0.1:8085}" log() { echo "[configure-usenet] $*"; } die() { log "ERROR: $*"; exit 1; } # Prefer secrets/.env; fall back to root .env (tech-demo: everything in one file) if [[ ! -f "$SECRETS_FILE" && -f "$STACK_DIR/.env" ]]; then SECRETS_FILE="$STACK_DIR/.env" log "using $SECRETS_FILE (no secrets/.env)" fi [[ -f "$SECRETS_FILE" ]] || die "missing $SECRETS_FILE (see secrets/.env.example or .env.example)" # shellcheck disable=SC1090 set -a # shellcheck disable=SC1091 source "$SECRETS_FILE" set +a # Accept clean names AND the variable names used in secrets/.env # NZBgeek NZBGEEK_API_KEY="${NZBGEEK_API_KEY:-${NZBKEEPKEY:-${NZBGEEKKEY:-}}}" NZBGEEK_URL="${NZBGEEK_URL:-${NZBGEEKAPI:-https://api.nzbgeek.info/}}" NZBGEEK_USERNAME="${NZBGEEK_USERNAME:-${NZBGEEKUSERNAME:-}}" # Newshosting (provider host often in NEWSHOSTINGAPI) NEWSHOSTING_HOST="${NEWSHOSTING_HOST:-${NEWSHOSTINGAPI:-news.newshosting.com}}" NEWSHOSTING_PORT="${NEWSHOSTING_PORT:-${NEWSHOSTINGSSLPORT:-563}}" [[ "$NEWSHOSTING_PORT" == "653" ]] && NEWSHOSTING_PORT=563 # common typo for 563 NEWSHOSTING_USER="${NEWSHOSTING_USER:-${NEWSHOSTINGUSER:-}}" NEWSHOSTING_PASS="${NEWSHOSTING_PASS:-${HNEWSHOSTINGPASS:-${NEWSHOSTINGPASS:-${NEWSHOSTINGPASSWORD:-}}}}" NEWSHOSTING_CONNECTIONS="${NEWSHOSTING_CONNECTIONS:-${NEWSHOSTINGCONNECTIONS:-20}}" # Tweaknews (secrets use TWEAKNEWSLOGIN / TWEAKNEWSPASSWORD / TWEAKNEWSAPI as host) TWEAKNEWS_HOST="${TWEAKNEWS_HOST:-${TWEAKNEWSAPI:-${TWEAKNEWSHOST:-news.tweaknews.eu}}}" TWEAKNEWS_PORT="${TWEAKNEWS_PORT:-${TWEAKNEWSSSLPORT:-${TWEAKNEWSPORT:-563}}}" TWEAKNEWS_USER="${TWEAKNEWS_USER:-${TWEAKNEWSLOGIN:-${TWEAKNEWSUSER:-${TWEAKNEWSUSERNAME:-}}}}" TWEAKNEWS_PASS="${TWEAKNEWS_PASS:-${TWEAKNEWSPASSWORD:-${TWEAKNEWSPASS:-}}}" TWEAKNEWS_CONNECTIONS="${TWEAKNEWS_CONNECTIONS:-${TWEAKNEWSCONNECTIONS:-10}}" find_api_key() { local f for f in \ "${CONFIG_DIR:-$STACK_DIR/config}/sabnzbd/sabnzbd.ini" \ "$STACK_DIR/config/sabnzbd/sabnzbd.ini" \ "/opt/stack/config/sabnzbd/sabnzbd.ini" \ ; do if [[ -f "$f" ]]; then grep -E '^api_key[[:space:]]*=' "$f" | head -1 | cut -d= -f2- | tr -d '[:space:]' return 0 fi done # docker exec fallback if command -v docker >/dev/null 2>&1; then docker exec sabnzbd sh -c 'grep -E "^api_key" /config/sabnzbd.ini 2>/dev/null | head -1' \ | cut -d= -f2- | tr -d '[:space:]' || true fi } log "Waiting for SABnzbd at $SAB_URL ..." for i in $(seq 1 90); do if curl -fsS "$SAB_URL/api?mode=version&output=json" >/dev/null 2>&1; then break fi sleep 2 [[ $i -eq 90 ]] && die "SABnzbd not reachable — is the container up? (docker compose up -d sabnzbd)" done api_key="$(find_api_key)" [[ -n "$api_key" ]] || die "no api_key yet — open $SAB_URL once to finish first-run, then re-run this script" sab_set() { # set_config: section + keyword + value OR servers with name= + fields curl -fsS -G "${SAB_URL}/api" \ --data-urlencode "mode=set_config" \ --data-urlencode "apikey=${api_key}" \ --data-urlencode "output=json" \ "$@" } log "Folders: incomplete + complete under /data/usenet" sab_set --data-urlencode "section=misc" --data-urlencode "keyword=download_dir" \ --data-urlencode "value=/data/usenet/incomplete" >/dev/null sab_set --data-urlencode "section=misc" --data-urlencode "keyword=complete_dir" \ --data-urlencode "value=/data/usenet/complete" >/dev/null # Relative category folders under complete_dir add_category() { local name="$1" sab_set \ --data-urlencode "section=categories" \ --data-urlencode "name=${name}" \ --data-urlencode "dir=${name}" \ --data-urlencode "pp=3" \ >/dev/null || true } add_category movies add_category tv add_category music add_server() { local name="$1" host="$2" port="$3" user="$4" pass="$5" conns="$6" if [[ -z "$user" || -z "$pass" ]]; then log "skip server '$name' (missing user/password in secrets)" return 0 fi log "Usenet server: $name → $host:$port (ssl, ${conns} conn)" # https://sabnzbd.org/wiki/configuration/5.0/api — servers via set_config + name= sab_set \ --data-urlencode "section=servers" \ --data-urlencode "name=${name}" \ --data-urlencode "displayline=${name}" \ --data-urlencode "host=${host}" \ --data-urlencode "port=${port}" \ --data-urlencode "username=${user}" \ --data-urlencode "password=${pass}" \ --data-urlencode "connections=${conns}" \ --data-urlencode "ssl=1" \ --data-urlencode "enable=1" \ --data-urlencode "priority=0" \ --data-urlencode "timeout=60" \ >/dev/null } add_server "Newshosting" \ "$NEWSHOSTING_HOST" "$NEWSHOSTING_PORT" \ "$NEWSHOSTING_USER" "$NEWSHOSTING_PASS" \ "$NEWSHOSTING_CONNECTIONS" add_server "Tweaknews" \ "$TWEAKNEWS_HOST" "$TWEAKNEWS_PORT" \ "$TWEAKNEWS_USER" "$TWEAKNEWS_PASS" \ "$TWEAKNEWS_CONNECTIONS" log "Restart SABnzbd to pick up servers" if command -v docker >/dev/null 2>&1 && docker ps --format '{{.Names}}' | grep -qx sabnzbd; then docker restart sabnzbd >/dev/null sleep 5 fi log "" log "=== SABnzbd ready ===" log "UI: $SAB_URL" log "API: (Config → General) — use this key in Prowlarr/*arr" log "" log "=== Prowlarr (NZBgeek) ===" log "Indexers → Add Indexer → NZBgeek" log " URL: ${NZBGEEK_URL}" if [[ -n "$NZBGEEK_API_KEY" ]]; then log " API Key: ${NZBGEEK_API_KEY}" else log " API Key: (set NZBKEEPKEY or NZBGEEK_API_KEY in secrets/.env)" fi log "" log "Download Clients → Add → SABnzbd" log " Host: sabnzbd Port: 8080 API Key: (from SABnzbd)" log " (sync to apps so Sonarr/Radarr get SABnzbd too, or add per-app)" log "" log "=== Sonarr / Radarr / Lidarr ===" log "Download Clients → SABnzbd → Host sabnzbd Port 8080" log " Category: tv | movies | music" log "Root folders still /data/media/{tv,movies,music} (hardlinks on same /data)" log "" if [[ -z "$TWEAKNEWS_USER" || -z "$TWEAKNEWS_PASS" ]]; then log "NOTE: Tweaknews user/pass not in secrets — add TWEAKNEWS_USER / TWEAKNEWS_PASS and re-run." fi