|
| 1 | +\ |
| 2 | +#!/bin/sh |
| 3 | +# qbt-proton-qnap :: fix_after_login.sh |
| 4 | +# Goal: |
| 5 | +# - Stop containers cleanly |
| 6 | +# - Patch .fastresume paths (/downloads -> /Downloads) |
| 7 | +# - Ensure /Downloads subfolders & permissions |
| 8 | +# - Start Gluetun and wait healthy |
| 9 | +# - Start qBittorrent and wait for WebUI API ready |
| 10 | +# - Fetch forwarded port from Gluetun |
| 11 | +# - Apply WebUI & runtime prefs through API (bypass local, host header off, csrf off, whitelist localhost) |
| 12 | +# - Verify and restart qB to lock values |
| 13 | +set -eu |
| 14 | + |
| 15 | +echo "[*] qbt-proton-qnap :: fix_after_login.sh" |
| 16 | + |
| 17 | +# Load .env |
| 18 | +if [ -f ".env" ]; then |
| 19 | + # shellcheck disable=SC1091 |
| 20 | + . ./.env |
| 21 | +else |
| 22 | + echo "[-] .env not found. Run from project root after creating .env from envExample." |
| 23 | + exit 1 |
| 24 | +fi |
| 25 | + |
| 26 | +: "${CONFIG_ROOT:?set in .env}" |
| 27 | +: "${DOWNLOADS_ROOT:?set in .env}" |
| 28 | +: "${PUID:?set}" |
| 29 | +: "${PGID:?set}" |
| 30 | +: "${WEBUI_HOST_PORT:=8081}" |
| 31 | + |
| 32 | +CONF="${CONFIG_ROOT}/qBittorrent/qBittorrent.conf" |
| 33 | +BT_BACKUP="${CONFIG_ROOT}/qBittorrent/BT_backup" |
| 34 | + |
| 35 | +echo "[i] Using config: ${CONF}" |
| 36 | +echo "[i] PUID=${PUID} PGID=${PGID}" |
| 37 | + |
| 38 | +# Stop containers with patience |
| 39 | +echo "[*] Stopping qbittorrent" |
| 40 | +docker compose stop qbittorrent >/dev/null 2>&1 || true |
| 41 | +for i in $(seq 1 20); do |
| 42 | + state="$(docker inspect -f '{{.State.Running}}' qbittorrent 2>/dev/null || echo 'false')" |
| 43 | + [ "${state}" = "false" ] && break |
| 44 | + sleep 0.5 |
| 45 | +done |
| 46 | + |
| 47 | +echo "[*] Stopping gluetun" |
| 48 | +docker compose stop gluetun >/dev/null 2>&1 || true |
| 49 | +for i in $(seq 1 20); do |
| 50 | + state="$(docker inspect -f '{{.State.Running}}' gluetun 2>/dev/null || echo 'false')" |
| 51 | + [ "${state}" = "false" ] && break |
| 52 | + sleep 0.5 |
| 53 | +done |
| 54 | + |
| 55 | +# Patch /downloads to /Downloads in .fastresume (if any) |
| 56 | +if [ -d "${BT_BACKUP}" ]; then |
| 57 | + echo "[*] Patching /downloads -> /Downloads in .fastresume" |
| 58 | + # BusyBox compatible grep -r |
| 59 | + find "${BT_BACKUP}" -type f -name "*.fastresume" -print0 | xargs -0 -r sed -i 's#/downloads#/Downloads#g' |
| 60 | +fi |
| 61 | + |
| 62 | +# Ensure folders and permissions |
| 63 | +echo "[*] Ensuring folders & permissions under: ${DOWNLOADS_ROOT}" |
| 64 | +mkdir -p "${DOWNLOADS_ROOT}/Incomplete" "${DOWNLOADS_ROOT}/Torrents" |
| 65 | +chown -R "${PUID}:${PGID}" "${DOWNLOADS_ROOT}" |
| 66 | +chmod -R u+rwX,g+rwX "${DOWNLOADS_ROOT}" |
| 67 | + |
| 68 | +# Start Gluetun and wait healthy |
| 69 | +echo "[*] Starting gluetun" |
| 70 | +docker compose up -d gluetun >/dev/null |
| 71 | +# Wait for health=healthy |
| 72 | +for i in $(seq 1 60); do |
| 73 | + h="$(docker inspect -f '{{.State.Health.Status}}' gluetun 2>/dev/null || echo 'starting')" |
| 74 | + [ "${h}" = "healthy" ] && break |
| 75 | + sleep 2 |
| 76 | +done |
| 77 | +docker inspect -f '{{.State.Health.Status}}' gluetun 2>/dev/null || true |
| 78 | + |
| 79 | +# Start qB |
| 80 | +echo "[*] Starting qbittorrent" |
| 81 | +docker compose up -d qbittorrent >/dev/null |
| 82 | + |
| 83 | +# Wait WebUI readiness (API version) |
| 84 | +echo "[*] Waiting for qBittorrent API inside container..." |
| 85 | +ok=false |
| 86 | +for i in $(seq 1 60); do |
| 87 | + if docker exec qbittorrent sh -lc "wget -qO- http://localhost:8080/api/v2/app/version >/dev/null 2>&1"; then |
| 88 | + ok=true |
| 89 | + break |
| 90 | + fi |
| 91 | + sleep 2 |
| 92 | +done |
| 93 | +if [ "${ok}" != "true" ]; then |
| 94 | + echo "[-] WebUI API did not become ready" |
| 95 | + exit 1 |
| 96 | +fi |
| 97 | + |
| 98 | +# Get forwarded port from Gluetun |
| 99 | +FWD="$(docker exec gluetun sh -lc 'cat /tmp/gluetun/forwarded_port 2>/dev/null || curl -fsS http://localhost:8000/v1/openvpn/portforwarded' | sed -E 's/[^0-9]//g' || true)" |
| 100 | +FWD="${FWD:-0}" |
| 101 | +echo "[i] Forwarded port = ${FWD}" |
| 102 | + |
| 103 | +# Temp WebUI password if LSIO reset it |
| 104 | +QBU="admin" |
| 105 | +QBP="$(docker logs qbittorrent 2>&1 | awk '/WebUI administrator username is/{f=1;next} f{print $NF; exit}')" |
| 106 | + |
| 107 | +# Login & apply preferences via API (payload param 'json=...' per qB API spec) |
| 108 | +docker exec qbittorrent sh -lc " |
| 109 | +u='${QBU}'; p='${QBP}'; |
| 110 | +curl -s -c /tmp/c.txt -X POST --data \"username=\$u&password=\$p\" http://localhost:8080/api/v2/auth/login >/dev/null && \ |
| 111 | +curl -s -b /tmp/c.txt -H 'Referer: http://localhost:8080' \ |
| 112 | + -d 'json={\"bypass_local_auth\":true,\"web_ui_host_header_validation\":false,\"web_ui_csrf_protection_enabled\":false,\"bypass_auth_subnet_whitelist_enabled\":true,\"web_ui_auth_subnet_whitelist\":\"127.0.0.1/32,::1/128\",\"web_ui_address\":\"*\",\"save_path\":\"/Downloads\",\"temp_path_enabled\":true,\"temp_path\":\"/Downloads/Incomplete\"%s}' \ |
| 113 | + http://localhost:8080/api/v2/app/setPreferences >/dev/null |
| 114 | +" >/dev/null |
| 115 | + |
| 116 | +# If forwarded port is valid, set it too |
| 117 | +if [ "${FWD}" -gt 0 ]; then |
| 118 | + docker exec qbittorrent sh -lc " |
| 119 | +u='${QBU}'; p='${QBP}'; |
| 120 | +curl -s -c /tmp/c.txt -X POST --data \"username=\$u&password=\$p\" http://localhost:8080/api/v2/auth/login >/dev/null && \ |
| 121 | +curl -s -b /tmp/c.txt -H 'Referer: http://localhost:8080' \ |
| 122 | + -d 'json={\"listen_port\":${FWD}}' \ |
| 123 | + http://localhost:8080/api/v2/app/setPreferences >/dev/null |
| 124 | +" >/dev/null |
| 125 | +fi |
| 126 | + |
| 127 | +# Verify a subset |
| 128 | +docker exec qbittorrent sh -lc " |
| 129 | +u='${QBU}'; p='${QBP}'; |
| 130 | +curl -s -c /tmp/c.txt -X POST --data \"username=\$u&password=\$p\" http://localhost:8080/api/v2/auth/login >/dev/null && \ |
| 131 | +curl -s -b /tmp/c.txt http://localhost:8080/api/v2/app/preferences \ |
| 132 | + | tr ',' '\n' | grep -E '\"listen_port\"|\"save_path\"|\"temp_path\"|\"bypass_local_auth\"|\"web_ui_host_header_validation\"|\"web_ui_csrf_protection_enabled\"|\"bypass_auth_subnet_whitelist_enabled\"' || true |
| 133 | +" || true |
| 134 | + |
| 135 | +# Final restart to ensure the runtime applies cleanly |
| 136 | +docker restart qbittorrent >/dev/null |
| 137 | + |
| 138 | +echo "[OK] Done. Open WebUI at your host:port. If some torrents error due to path updates, select them and 'Force recheck'." |
0 commit comments