2026-07-22 11:59:51 -07:00
|
|
|
#!/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; }
|
|
|
|
|
|
|
|
|
|
[[ -f "$SECRETS_FILE" ]] || die "missing $SECRETS_FILE (see secrets/.env.example)"
|
|
|
|
|
|
|
|
|
|
# shellcheck disable=SC1090
|
|
|
|
|
set -a
|
|
|
|
|
# shellcheck disable=SC1091
|
|
|
|
|
source "$SECRETS_FILE"
|
|
|
|
|
set +a
|
|
|
|
|
|
2026-07-22 12:48:07 -07:00
|
|
|
# Accept clean names AND the variable names used in secrets/.env
|
|
|
|
|
# NZBgeek
|
|
|
|
|
NZBGEEK_API_KEY="${NZBGEEK_API_KEY:-${NZBKEEPKEY:-${NZBGEEKKEY:-}}}"
|
2026-07-22 11:59:51 -07:00
|
|
|
NZBGEEK_URL="${NZBGEEK_URL:-${NZBGEEKAPI:-https://api.nzbgeek.info/}}"
|
2026-07-22 12:48:07 -07:00
|
|
|
NZBGEEK_USERNAME="${NZBGEEK_USERNAME:-${NZBGEEKUSERNAME:-}}"
|
|
|
|
|
|
|
|
|
|
# Newshosting (provider host often in NEWSHOSTINGAPI)
|
2026-07-22 11:59:51 -07:00
|
|
|
NEWSHOSTING_HOST="${NEWSHOSTING_HOST:-${NEWSHOSTINGAPI:-news.newshosting.com}}"
|
|
|
|
|
NEWSHOSTING_PORT="${NEWSHOSTING_PORT:-${NEWSHOSTINGSSLPORT:-563}}"
|
2026-07-22 12:48:07 -07:00
|
|
|
[[ "$NEWSHOSTING_PORT" == "653" ]] && NEWSHOSTING_PORT=563 # common typo for 563
|
2026-07-22 11:59:51 -07:00
|
|
|
NEWSHOSTING_USER="${NEWSHOSTING_USER:-${NEWSHOSTINGUSER:-}}"
|
2026-07-22 12:48:07 -07:00
|
|
|
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}}"
|
2026-07-22 11:59:51 -07:00
|
|
|
|
|
|
|
|
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
|