Merge branch 'main' of https://github.com/community-scripts/ProxmoxVED
This commit is contained in:
59
ct/flaresolverr.sh
Normal file
59
ct/flaresolverr.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster) | Co-Author: remz1337
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/FlareSolverr/FlareSolverr
|
||||
|
||||
APP="FlareSolverr"
|
||||
var_tags="${var_tags:-proxy}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -f /etc/systemd/system/flaresolverr.service ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
if [[ $(grep -E '^VERSION_ID=' /etc/os-release) == *"12"* ]]; then
|
||||
msg_error "Wrong Debian version detected!"
|
||||
msg_error "You must upgrade your LXC to Debian Trixie before updating."
|
||||
exit
|
||||
fi
|
||||
if check_for_gh_release "flaresolverr" "FlareSolverr/FlareSolverr"; then
|
||||
msg_info "Stopping service"
|
||||
systemctl stop flaresolverr
|
||||
msg_ok "Stopped service"
|
||||
|
||||
rm -rf /opt/flaresolverr
|
||||
fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz"
|
||||
|
||||
msg_info "Starting service"
|
||||
systemctl start flaresolverr
|
||||
msg_ok "Started service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8191${CL}"
|
||||
61
ct/gluetun.sh
Normal file
61
ct/gluetun.sh
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/qdm12/gluetun
|
||||
|
||||
APP="Gluetun"
|
||||
var_tags="${var_tags:-vpn;wireguard;openvpn}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-8}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
var_tun="${var_tun:-yes}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -f /usr/local/bin/gluetun ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "gluetun" "qdm12/gluetun"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop gluetun
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "gluetun" "qdm12/gluetun" "tarball"
|
||||
|
||||
msg_info "Building Gluetun"
|
||||
cd /opt/gluetun
|
||||
$STD go mod download
|
||||
CGO_ENABLED=0 $STD go build -trimpath -ldflags="-s -w" -o /usr/local/bin/gluetun ./cmd/gluetun/
|
||||
msg_ok "Built Gluetun"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start gluetun
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed Successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}"
|
||||
6
ct/headers/flaresolverr
Normal file
6
ct/headers/flaresolverr
Normal file
@@ -0,0 +1,6 @@
|
||||
________ _____ __
|
||||
/ ____/ /___ _________ / ___/____ / / _____ __________
|
||||
/ /_ / / __ `/ ___/ _ \\__ \/ __ \/ / | / / _ \/ ___/ ___/
|
||||
/ __/ / / /_/ / / / __/__/ / /_/ / /| |/ / __/ / / /
|
||||
/_/ /_/\__,_/_/ \___/____/\____/_/ |___/\___/_/ /_/
|
||||
|
||||
6
ct/headers/gluetun
Normal file
6
ct/headers/gluetun
Normal file
@@ -0,0 +1,6 @@
|
||||
________ __
|
||||
/ ____/ /_ _____ / /___ ______
|
||||
/ / __/ / / / / _ \/ __/ / / / __ \
|
||||
/ /_/ / / /_/ / __/ /_/ /_/ / / / /
|
||||
\____/_/\__,_/\___/\__/\__,_/_/ /_/
|
||||
|
||||
6
ct/headers/lidarr
Normal file
6
ct/headers/lidarr
Normal file
@@ -0,0 +1,6 @@
|
||||
__ _ __
|
||||
/ / (_)___/ /___ ___________
|
||||
/ / / / __ / __ `/ ___/ ___/
|
||||
/ /___/ / /_/ / /_/ / / / /
|
||||
/_____/_/\__,_/\__,_/_/ /_/
|
||||
|
||||
6
ct/headers/prowlarr
Normal file
6
ct/headers/prowlarr
Normal file
@@ -0,0 +1,6 @@
|
||||
____ __
|
||||
/ __ \_________ _ __/ /___ ___________
|
||||
/ /_/ / ___/ __ \ | /| / / / __ `/ ___/ ___/
|
||||
/ ____/ / / /_/ / |/ |/ / / /_/ / / / /
|
||||
/_/ /_/ \____/|__/|__/_/\__,_/_/ /_/
|
||||
|
||||
6
ct/headers/qbittorrent
Normal file
6
ct/headers/qbittorrent
Normal file
@@ -0,0 +1,6 @@
|
||||
____ _ __ __ __
|
||||
____ _/ __ )(_) /_/ /_____ _____________ ____ / /_
|
||||
/ __ `/ __ / / __/ __/ __ \/ ___/ ___/ _ \/ __ \/ __/
|
||||
/ /_/ / /_/ / / /_/ /_/ /_/ / / / / / __/ / / / /_
|
||||
\__, /_____/_/\__/\__/\____/_/ /_/ \___/_/ /_/\__/
|
||||
/_/
|
||||
6
ct/headers/radarr
Normal file
6
ct/headers/radarr
Normal file
@@ -0,0 +1,6 @@
|
||||
____ __
|
||||
/ __ \____ _____/ /___ ___________
|
||||
/ /_/ / __ `/ __ / __ `/ ___/ ___/
|
||||
/ _, _/ /_/ / /_/ / /_/ / / / /
|
||||
/_/ |_|\__,_/\__,_/\__,_/_/ /_/
|
||||
|
||||
6
ct/headers/rdtclient
Normal file
6
ct/headers/rdtclient
Normal file
@@ -0,0 +1,6 @@
|
||||
____ ____ _______________ __
|
||||
/ __ \/ __ \/_ __/ ____/ (_)__ ____ / /_
|
||||
/ /_/ / / / / / / / / / / / _ \/ __ \/ __/
|
||||
/ _, _/ /_/ / / / / /___/ / / __/ / / / /_
|
||||
/_/ |_/_____/ /_/ \____/_/_/\___/_/ /_/\__/
|
||||
|
||||
6
ct/headers/sabnzbd
Normal file
6
ct/headers/sabnzbd
Normal file
@@ -0,0 +1,6 @@
|
||||
_____ ___ ____ __ __
|
||||
/ ___// | / __ )____ ____ / /_ ____/ /
|
||||
\__ \/ /| | / __ / __ \/_ / / __ \/ __ /
|
||||
___/ / ___ |/ /_/ / / / / / /_/ /_/ / /_/ /
|
||||
/____/_/ |_/_____/_/ /_/ /___/_.___/\__,_/
|
||||
|
||||
6
ct/headers/seerr
Normal file
6
ct/headers/seerr
Normal file
@@ -0,0 +1,6 @@
|
||||
_____
|
||||
/ ___/___ ___ __________
|
||||
\__ \/ _ \/ _ \/ ___/ ___/
|
||||
___/ / __/ __/ / / /
|
||||
/____/\___/\___/_/ /_/
|
||||
|
||||
6
ct/headers/slskd
Normal file
6
ct/headers/slskd
Normal file
@@ -0,0 +1,6 @@
|
||||
__ __ __
|
||||
_____/ /____/ /______/ /
|
||||
/ ___/ / ___/ //_/ __ /
|
||||
(__ ) (__ ) ,< / /_/ /
|
||||
/____/_/____/_/|_|\__,_/
|
||||
|
||||
6
ct/headers/sonarr
Normal file
6
ct/headers/sonarr
Normal file
@@ -0,0 +1,6 @@
|
||||
_____
|
||||
/ ___/____ ____ ____ ___________
|
||||
\__ \/ __ \/ __ \/ __ `/ ___/ ___/
|
||||
___/ / /_/ / / / / /_/ / / / /
|
||||
/____/\____/_/ /_/\__,_/_/ /_/
|
||||
|
||||
55
ct/lidarr.sh
Normal file
55
ct/lidarr.sh
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://lidarr.audio/ | Github: https://github.com/Lidarr/Lidarr
|
||||
|
||||
APP="Lidarr"
|
||||
var_tags="${var_tags:-arr;torrent;usenet}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /var/lib/lidarr/ ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "lidarr" "Lidarr/Lidarr"; then
|
||||
msg_info "Stopping service"
|
||||
systemctl stop lidarr
|
||||
msg_ok "Service stopped"
|
||||
|
||||
fetch_and_deploy_gh_release "lidarr" "Lidarr/Lidarr" "prebuild" "latest" "/opt/Lidarr" "Lidarr.master*linux-core-x64.tar.gz"
|
||||
chmod 775 /opt/Lidarr
|
||||
|
||||
msg_info "Starting service"
|
||||
systemctl start lidarr
|
||||
msg_ok "Service started"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8686${CL}"
|
||||
54
ct/prowlarr.sh
Normal file
54
ct/prowlarr.sh
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://prowlarr.com/ | Github: https://github.com/Prowlarr/Prowlarr
|
||||
|
||||
APP="Prowlarr"
|
||||
var_tags="${var_tags:-arr}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
if [[ ! -d /var/lib/prowlarr/ ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
if check_for_gh_release "prowlarr" "Prowlarr/Prowlarr"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop prowlarr
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
rm -rf /opt/Prowlarr
|
||||
fetch_and_deploy_gh_release "prowlarr" "Prowlarr/Prowlarr" "prebuild" "latest" "/opt/Prowlarr" "Prowlarr.master*linux-core-x64.tar.gz"
|
||||
chmod 775 /opt/Prowlarr
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start prowlarr
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9696${CL}"
|
||||
58
ct/qbittorrent.sh
Normal file
58
ct/qbittorrent.sh
Normal file
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: tteck (tteckster) | Co-Author: Slaviša Arežina (tremor021)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://www.qbittorrent.org/ | Github: https://github.com/qbittorrent/qBittorrent
|
||||
|
||||
APP="qBittorrent"
|
||||
var_tags="${var_tags:-torrent}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-8}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
if [[ ! -f /etc/systemd/system/qbittorrent-nox.service ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
if [[ ! -f ~/.qbittorrent ]]; then
|
||||
msg_error "Please create new qBittorrent LXC. Updating from v4.x to v5.x is not supported!"
|
||||
exit
|
||||
fi
|
||||
if check_for_gh_release "qbittorrent" "userdocs/qbittorrent-nox-static"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop qbittorrent-nox
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
rm -f /opt/qbittorrent/qbittorrent-nox
|
||||
fetch_and_deploy_gh_release "qbittorrent" "userdocs/qbittorrent-nox-static" "singlefile" "latest" "/opt/qbittorrent" "x86_64-qbittorrent-nox"
|
||||
mv /opt/qbittorrent/qbittorrent /opt/qbittorrent/qbittorrent-nox
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start qbittorrent-nox
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}"
|
||||
56
ct/radarr.sh
Normal file
56
ct/radarr.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://radarr.video/ | Github: https://github.com/Radarr/Radarr
|
||||
|
||||
APP="Radarr"
|
||||
var_tags="${var_tags:-arr}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /var/lib/radarr/ ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "Radarr" "Radarr/Radarr"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop radarr
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
rm -rf /opt/Radarr
|
||||
fetch_and_deploy_gh_release "Radarr" "Radarr/Radarr" "prebuild" "latest" "/opt/Radarr" "Radarr.master*linux-core-x64.tar.gz"
|
||||
chmod 775 /opt/Radarr
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start radarr
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7878${CL}"
|
||||
63
ct/rdtclient.sh
Normal file
63
ct/rdtclient.sh
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/rogerfar/rdt-client
|
||||
|
||||
APP="RDTClient"
|
||||
var_tags="${var_tags:-torrent}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
if [[ ! -d /opt/rdtc/ ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
if check_for_gh_release "rdt-client" "rogerfar/rdt-client"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop rdtc
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Creating backup"
|
||||
mkdir -p /opt/rdtc-backup
|
||||
cp -R /opt/rdtc/appsettings.json /opt/rdtc-backup/
|
||||
msg_ok "Backup created"
|
||||
|
||||
fetch_and_deploy_gh_release "rdt-client" "rogerfar/rdt-client" "prebuild" "latest" "/opt/rdtc" "RealDebridClient.zip"
|
||||
cp -R /opt/rdtc-backup/appsettings.json /opt/rdtc/
|
||||
if dpkg-query -W aspnetcore-runtime-9.0 >/dev/null 2>&1; then
|
||||
$STD apt remove --purge -y aspnetcore-runtime-9.0
|
||||
ensure_dependencies aspnetcore-runtime-10.0
|
||||
fi
|
||||
rm -rf /opt/rdtc-backup
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start rdtc
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:6500${CL}"
|
||||
70
ct/sabnzbd.sh
Normal file
70
ct/sabnzbd.sh
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster) | Co-Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://sabnzbd.org/ | Github: https://github.com/sabnzbd/sabnzbd
|
||||
|
||||
APP="SABnzbd"
|
||||
var_tags="${var_tags:-downloader}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-2048}"
|
||||
var_disk="${var_disk:-5}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if par2 --version | grep -q "par2cmdline-turbo"; then
|
||||
fetch_and_deploy_gh_release "par2cmdline-turbo" "animetosho/par2cmdline-turbo" "prebuild" "latest" "/usr/bin/" "*-linux-amd64.zip"
|
||||
fi
|
||||
|
||||
if [[ ! -d /opt/sabnzbd ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
if check_for_gh_release "sabnzbd-org" "sabnzbd/sabnzbd"; then
|
||||
PYTHON_VERSION="3.13" setup_uv
|
||||
systemctl stop sabnzbd
|
||||
cp -r /opt/sabnzbd /opt/sabnzbd_backup_$(date +%s)
|
||||
fetch_and_deploy_gh_release "sabnzbd-org" "sabnzbd/sabnzbd" "prebuild" "latest" "/opt/sabnzbd" "SABnzbd-*-src.tar.gz"
|
||||
|
||||
# Always ensure venv exists
|
||||
if [[ ! -d /opt/sabnzbd/venv ]]; then
|
||||
msg_info "Migrating SABnzbd to uv virtual environment"
|
||||
$STD uv venv --clear /opt/sabnzbd/venv
|
||||
msg_ok "Created uv venv at /opt/sabnzbd/venv"
|
||||
fi
|
||||
|
||||
# Always check and fix service file if needed
|
||||
if [[ -f /etc/systemd/system/sabnzbd.service ]] && grep -q "ExecStart=python3 SABnzbd.py" /etc/systemd/system/sabnzbd.service; then
|
||||
sed -i "s|ExecStart=python3 SABnzbd.py|ExecStart=/opt/sabnzbd/venv/bin/python SABnzbd.py|" /etc/systemd/system/sabnzbd.service
|
||||
systemctl daemon-reload
|
||||
msg_ok "Updated SABnzbd service to use uv venv"
|
||||
fi
|
||||
$STD uv pip install --upgrade pip --python=/opt/sabnzbd/venv/bin/python
|
||||
$STD uv pip install -r /opt/sabnzbd/requirements.txt --python=/opt/sabnzbd/venv/bin/python
|
||||
|
||||
systemctl start sabnzbd
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7777${CL}"
|
||||
167
ct/seerr.sh
Normal file
167
ct/seerr.sh
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: CrazyWolf13
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://docs.seerr.dev/ | Github: https://github.com/seerr-team/seerr
|
||||
|
||||
APP="Seerr"
|
||||
var_tags="${var_tags:-media}"
|
||||
var_cpu="${var_cpu:-4}"
|
||||
var_ram="${var_ram:-4096}"
|
||||
var_disk="${var_disk:-12}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/seerr && ! -d /opt/jellyseerr && ! -d /opt/overseerr ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Start Migration from Jellyseerr
|
||||
if [[ -f /etc/systemd/system/jellyseerr.service ]]; then
|
||||
msg_info "Stopping Jellyseerr"
|
||||
$STD systemctl stop jellyseerr || true
|
||||
$STD systemctl disable jellyseerr || true
|
||||
[ -f /etc/systemd/system/jellyseerr.service ] && rm -f /etc/systemd/system/jellyseerr.service
|
||||
msg_ok "Stopped Jellyseerr"
|
||||
|
||||
msg_info "Creating Backup (Patience)"
|
||||
tar -czf /opt/jellyseerr_backup_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt jellyseerr
|
||||
msg_ok "Created Backup"
|
||||
|
||||
msg_info "Migrating Jellyseerr to seerr"
|
||||
[ -d /opt/jellyseerr ] && mv /opt/jellyseerr /opt/seerr
|
||||
[ -d /etc/jellyseerr ] && mv /etc/jellyseerr /etc/seerr
|
||||
[ -f /etc/seerr/jellyseerr.conf ] && mv /etc/seerr/jellyseerr.conf /etc/seerr/seerr.conf
|
||||
cat <<EOF >/etc/systemd/system/seerr.service
|
||||
[Unit]
|
||||
Description=Seerr Service
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=/etc/seerr/seerr.conf
|
||||
Environment=NODE_ENV=production
|
||||
Type=exec
|
||||
Restart=on-failure
|
||||
WorkingDirectory=/opt/seerr
|
||||
ExecStart=/usr/bin/node dist/index.js
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable -q --now seerr
|
||||
msg_ok "Migrated Jellyserr to Seerr"
|
||||
fi
|
||||
# END Jellyseerr Migration
|
||||
|
||||
# Start Migration from Overseerr
|
||||
if [[ -f /etc/systemd/system/overseerr.service ]]; then
|
||||
msg_info "Stopping Overseerr"
|
||||
$STD systemctl stop overseerr || true
|
||||
$STD systemctl disable overseerr || true
|
||||
[ -f /etc/systemd/system/overseerr.service ] && rm -f /etc/systemd/system/overseerr.service
|
||||
msg_ok "Stopped Overseerr"
|
||||
|
||||
msg_info "Creating Backup (Patience)"
|
||||
tar -czf /opt/overseerr_backup_$(date +%Y%m%d_%H%M%S).tar.gz -C /opt overseerr
|
||||
msg_ok "Created Backup"
|
||||
|
||||
msg_info "Migrating Overseerr to seerr"
|
||||
[ -d /opt/overseerr ] && mv /opt/overseerr /opt/seerr
|
||||
mkdir -p /etc/seerr
|
||||
cat <<EOF >/etc/seerr/seerr.conf
|
||||
## Seerr's default port is 5055, if you want to use both, change this.
|
||||
## specify on which port to listen
|
||||
PORT=5055
|
||||
|
||||
## specify on which interface to listen, by default seerr listens on all interfaces
|
||||
#HOST=127.0.0.1
|
||||
|
||||
## Uncomment if you want to force Node.js to resolve IPv4 before IPv6 (advanced users only)
|
||||
# FORCE_IPV4_FIRST=true
|
||||
EOF
|
||||
cat <<EOF >/etc/systemd/system/seerr.service
|
||||
[Unit]
|
||||
Description=Seerr Service
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=/etc/seerr/seerr.conf
|
||||
Environment=NODE_ENV=production
|
||||
Type=exec
|
||||
Restart=on-failure
|
||||
WorkingDirectory=/opt/seerr
|
||||
ExecStart=/usr/bin/node dist/index.js
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable -q --now seerr
|
||||
msg_ok "Migrated Overseerr to Seerr"
|
||||
fi
|
||||
# END Overseerr Migration
|
||||
|
||||
if check_for_gh_release "seerr" "seerr-team/seerr"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop seerr
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
msg_info "Creating Backup"
|
||||
cp -a /opt/seerr/config /opt/seerr_backup
|
||||
msg_ok "Created Backup"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "seerr" "seerr-team/seerr" "tarball"
|
||||
|
||||
ensure_dependencies build-essential python3-setuptools
|
||||
|
||||
msg_info "Updating PNPM Version"
|
||||
pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/seerr/package.json)
|
||||
NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs
|
||||
msg_ok "Updated PNPM Version"
|
||||
|
||||
msg_info "Updating Seerr"
|
||||
cd /opt/seerr
|
||||
rm -rf dist .next node_modules
|
||||
export CYPRESS_INSTALL_BINARY=0
|
||||
$STD pnpm install --frozen-lockfile
|
||||
export NODE_OPTIONS="--max-old-space-size=3072"
|
||||
$STD pnpm build
|
||||
msg_ok "Updated Seerr"
|
||||
|
||||
msg_info "Restoring Backup"
|
||||
rm -rf /opt/seerr/config
|
||||
mv /opt/seerr_backup /opt/seerr/config
|
||||
msg_ok "Restored Backup"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start seerr
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5055${CL}"
|
||||
100
ct/slskd.sh
Normal file
100
ct/slskd.sh
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: vhsdream
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/slskd/slskd/, https://github.com/mrusse/soularr
|
||||
|
||||
APP="slskd"
|
||||
var_tags="${var_tags:-arr;p2p}"
|
||||
var_cpu="${var_cpu:-1}"
|
||||
var_ram="${var_ram:-512}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /opt/slskd ]]; then
|
||||
msg_error "No Slskd Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "Slskd" "slskd/slskd"; then
|
||||
msg_info "Stopping Service(s)"
|
||||
systemctl stop slskd
|
||||
[[ -f /etc/systemd/system/soularr.service ]] && systemctl stop soularr.timer soularr.service
|
||||
msg_ok "Stopped Service(s)"
|
||||
|
||||
msg_info "Backing up config"
|
||||
cp /opt/slskd/config/slskd.yml /opt/slskd.yml.bak
|
||||
msg_ok "Backed up config"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Slskd" "slskd/slskd" "prebuild" "latest" "/opt/slskd" "slskd-*-linux-x64.zip"
|
||||
|
||||
msg_info "Restoring config"
|
||||
mv /opt/slskd.yml.bak /opt/slskd/config/slskd.yml
|
||||
|
||||
# Migrate 0.25.0 breaking config key renames
|
||||
sed -i 's/^global:/transfers:/' /opt/slskd/config/slskd.yml
|
||||
sed -i 's/^integration:/integrations:/' /opt/slskd/config/slskd.yml
|
||||
msg_ok "Restored config"
|
||||
|
||||
msg_info "Starting Service(s)"
|
||||
systemctl start slskd
|
||||
[[ -f /etc/systemd/system/soularr.service ]] && systemctl start soularr.timer
|
||||
msg_ok "Started Service(s)"
|
||||
msg_ok "Updated Slskd successfully!"
|
||||
fi
|
||||
[[ -d /opt/soularr ]] && if check_for_gh_release "Soularr" "mrusse/soularr"; then
|
||||
if systemctl is-active soularr.timer >/dev/null; then
|
||||
msg_info "Stopping Timer and Service"
|
||||
systemctl stop soularr.timer soularr.service
|
||||
msg_ok "Stopped Timer and Service"
|
||||
fi
|
||||
|
||||
msg_info "Backing up Soularr config"
|
||||
cp /opt/soularr/config.ini /opt/soularr_config.ini.bak
|
||||
cp /opt/soularr/run.sh /opt/soularr_run.sh.bak
|
||||
msg_ok "Backed up Soularr config"
|
||||
|
||||
PYTHON_VERSION="3.11" setup_uv
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Soularr" "mrusse/soularr" "tarball" "latest" "/opt/soularr"
|
||||
msg_info "Updating Soularr"
|
||||
cd /opt/soularr
|
||||
$STD uv venv -c venv
|
||||
$STD source venv/bin/activate
|
||||
$STD uv pip install -r requirements.txt
|
||||
deactivate
|
||||
msg_ok "Updated Soularr"
|
||||
|
||||
msg_info "Restoring Soularr config"
|
||||
mv /opt/soularr_config.ini.bak /opt/soularr/config.ini
|
||||
mv /opt/soularr_run.sh.bak /opt/soularr/run.sh
|
||||
msg_ok "Restored Soularr config"
|
||||
|
||||
msg_info "Starting Soularr Timer"
|
||||
systemctl restart soularr.timer
|
||||
msg_ok "Started Soularr Timer"
|
||||
msg_ok "Updated Soularr successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5030${CL}"
|
||||
54
ct/sonarr.sh
Normal file
54
ct/sonarr.sh
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env bash
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://sonarr.tv/ | Github: https://github.com/Sonarr/Sonarr
|
||||
|
||||
APP="Sonarr"
|
||||
var_tags="${var_tags:-arr}"
|
||||
var_cpu="${var_cpu:-2}"
|
||||
var_ram="${var_ram:-1024}"
|
||||
var_disk="${var_disk:-4}"
|
||||
var_os="${var_os:-debian}"
|
||||
var_version="${var_version:-13}"
|
||||
var_unprivileged="${var_unprivileged:-1}"
|
||||
|
||||
header_info "$APP"
|
||||
variables
|
||||
color
|
||||
catch_errors
|
||||
|
||||
function update_script() {
|
||||
header_info
|
||||
check_container_storage
|
||||
check_container_resources
|
||||
|
||||
if [[ ! -d /var/lib/sonarr/ ]]; then
|
||||
msg_error "No ${APP} Installation Found!"
|
||||
exit
|
||||
fi
|
||||
|
||||
if check_for_gh_release "Sonarr" "Sonarr/Sonarr"; then
|
||||
msg_info "Stopping Service"
|
||||
systemctl stop sonarr
|
||||
msg_ok "Stopped Service"
|
||||
|
||||
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "Sonarr" "Sonarr/Sonarr" "prebuild" "latest" "/opt/Sonarr" "Sonarr.main.*.linux-x64.tar.gz"
|
||||
|
||||
msg_info "Starting Service"
|
||||
systemctl start sonarr
|
||||
msg_ok "Started Service"
|
||||
msg_ok "Updated successfully!"
|
||||
fi
|
||||
exit
|
||||
}
|
||||
|
||||
start
|
||||
build_container
|
||||
description
|
||||
|
||||
msg_ok "Completed successfully!\n"
|
||||
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
|
||||
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8989${CL}"
|
||||
62
install/flaresolverr-install.sh
Normal file
62
install/flaresolverr-install.sh
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# Co-Author: remz1337
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/FlareSolverr/FlareSolverr
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt-get install -y \
|
||||
apt-transport-https \
|
||||
xvfb
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
msg_info "Installing Chrome"
|
||||
setup_deb822_repo \
|
||||
"google-chrome" \
|
||||
"https://dl.google.com/linux/linux_signing_key.pub" \
|
||||
"https://dl.google.com/linux/chrome/deb/" \
|
||||
"stable"
|
||||
$STD apt update
|
||||
$STD apt install -y google-chrome-stable
|
||||
# remove google-chrome.list added by google-chrome-stable
|
||||
if [ -f /etc/apt/sources.list.d/google-chrome.list ]; then
|
||||
rm /etc/apt/sources.list.d/google-chrome.list
|
||||
fi
|
||||
msg_ok "Installed Chrome"
|
||||
|
||||
fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/flaresolverr.service
|
||||
[Unit]
|
||||
Description=FlareSolverr
|
||||
After=network.target
|
||||
[Service]
|
||||
SyslogIdentifier=flaresolverr
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
Type=simple
|
||||
Environment="LOG_LEVEL=info"
|
||||
Environment="CAPTCHA_SOLVER=none"
|
||||
WorkingDirectory=/opt/flaresolverr
|
||||
ExecStart=/opt/flaresolverr/flaresolverr
|
||||
TimeoutStopSec=30
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now flaresolverr
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
96
install/gluetun-install.sh
Normal file
96
install/gluetun-install.sh
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/qdm12/gluetun
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
openvpn \
|
||||
wireguard-tools \
|
||||
iptables
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
msg_info "Configuring iptables"
|
||||
$STD update-alternatives --set iptables /usr/sbin/iptables-legacy
|
||||
$STD update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
|
||||
ln -sf /usr/sbin/openvpn /usr/sbin/openvpn2.6
|
||||
msg_ok "Configured iptables"
|
||||
|
||||
setup_go
|
||||
|
||||
fetch_and_deploy_gh_release "gluetun" "qdm12/gluetun" "tarball"
|
||||
|
||||
msg_info "Building Gluetun"
|
||||
cd /opt/gluetun
|
||||
$STD go mod download
|
||||
CGO_ENABLED=0 $STD go build -trimpath -ldflags="-s -w" -o /usr/local/bin/gluetun ./cmd/gluetun/
|
||||
msg_ok "Built Gluetun"
|
||||
|
||||
msg_info "Configuring Gluetun"
|
||||
mkdir -p /opt/gluetun-data
|
||||
touch /etc/alpine-release
|
||||
ln -sf /opt/gluetun-data /gluetun
|
||||
cat <<EOF >/opt/gluetun-data/.env
|
||||
VPN_SERVICE_PROVIDER=custom
|
||||
VPN_TYPE=openvpn
|
||||
OPENVPN_CUSTOM_CONFIG=/opt/gluetun-data/custom.ovpn
|
||||
OPENVPN_USER=
|
||||
OPENVPN_PASSWORD=
|
||||
OPENVPN_PROCESS_USER=root
|
||||
PUID=0
|
||||
PGID=0
|
||||
HTTP_CONTROL_SERVER_ADDRESS=:8000
|
||||
HTTPPROXY=off
|
||||
SHADOWSOCKS=off
|
||||
PPROF_ENABLED=no
|
||||
PPROF_BLOCK_PROFILE_RATE=0
|
||||
PPROF_MUTEX_PROFILE_RATE=0
|
||||
PPROF_HTTP_SERVER_ADDRESS=:6060
|
||||
FIREWALL_ENABLED_DISABLING_IT_SHOOTS_YOU_IN_YOUR_FOOT=on
|
||||
HEALTH_SERVER_ADDRESS=127.0.0.1:9999
|
||||
DNS_UPSTREAM_RESOLVERS=cloudflare
|
||||
LOG_LEVEL=info
|
||||
STORAGE_FILEPATH=/gluetun/servers.json
|
||||
PUBLICIP_FILE=/gluetun/ip
|
||||
VPN_PORT_FORWARDING_STATUS_FILE=/gluetun/forwarded_port
|
||||
TZ=UTC
|
||||
EOF
|
||||
msg_ok "Configured Gluetun"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/gluetun.service
|
||||
[Unit]
|
||||
Description=Gluetun VPN Client
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/gluetun-data
|
||||
EnvironmentFile=/opt/gluetun-data/.env
|
||||
UnsetEnvironment=USER
|
||||
ExecStartPre=/bin/sh -c 'rm -f /etc/openvpn/target.ovpn'
|
||||
ExecStart=/usr/local/bin/gluetun
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
AmbientCapabilities=CAP_NET_ADMIN
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now gluetun
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
53
install/lidarr-install.sh
Normal file
53
install/lidarr-install.sh
Normal file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://lidarr.audio/ | Github: https://github.com/Lidarr/Lidarr
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
sqlite3 \
|
||||
libchromaprint-tools \
|
||||
mediainfo
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
fetch_and_deploy_gh_release "lidarr" "Lidarr/Lidarr" "prebuild" "latest" "/opt/Lidarr" "Lidarr.master*linux-core-x64.tar.gz"
|
||||
|
||||
msg_info "Configuring Lidarr"
|
||||
mkdir -p /var/lib/lidarr/
|
||||
chmod 775 /var/lib/lidarr/
|
||||
chmod 775 /opt/Lidarr
|
||||
msg_ok "Configured Lidarr"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/lidarr.service
|
||||
[Unit]
|
||||
Description=Lidarr Daemon
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
UMask=0002
|
||||
Type=simple
|
||||
ExecStart=/opt/Lidarr/Lidarr -nobrowser -data=/var/lib/lidarr/
|
||||
TimeoutStopSec=20
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now lidarr
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
49
install/prowlarr-install.sh
Normal file
49
install/prowlarr-install.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://prowlarr.com/ | Github: https://github.com/Prowlarr/Prowlarr
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y sqlite3
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
fetch_and_deploy_gh_release "prowlarr" "Prowlarr/Prowlarr" "prebuild" "latest" "/opt/Prowlarr" "Prowlarr.master*linux-core-x64.tar.gz"
|
||||
|
||||
msg_info "Configuring Prowlarr"
|
||||
mkdir -p /var/lib/prowlarr/
|
||||
chmod 775 /var/lib/prowlarr/ /opt/Prowlarr
|
||||
msg_ok "Configured Prowlarr"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/prowlarr.service
|
||||
[Unit]
|
||||
Description=Prowlarr Daemon
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
UMask=0002
|
||||
Type=simple
|
||||
ExecStart=/opt/Prowlarr/Prowlarr -nobrowser -data=/var/lib/prowlarr/
|
||||
TimeoutStopSec=20
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now prowlarr
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
56
install/qbittorrent-install.sh
Normal file
56
install/qbittorrent-install.sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: tteck (tteckster) | Co-Author: Slaviša Arežina (tremor021)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://www.qbittorrent.org/ | Github: https://github.com/qbittorrent/qBittorrent
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
fetch_and_deploy_gh_release "qbittorrent" "userdocs/qbittorrent-nox-static" "singlefile" "latest" "/opt/qbittorrent" "x86_64-qbittorrent-nox"
|
||||
|
||||
msg_info "Setup qBittorrent-nox"
|
||||
mv /opt/qbittorrent/qbittorrent /opt/qbittorrent/qbittorrent-nox
|
||||
mkdir -p ~/.config/qBittorrent/
|
||||
cat <<EOF >~/.config/qBittorrent/qBittorrent.conf
|
||||
[LegalNotice]
|
||||
Accepted=true
|
||||
|
||||
[Preferences]
|
||||
WebUI\Password_PBKDF2="@ByteArray(amjeuVrF3xRbgzqWQmes5A==:XK3/Ra9jUmqUc4RwzCtrhrkQIcYczBl90DJw2rT8DFVTss4nxpoRhvyxhCf87ahVE3SzD8K9lyPdpyUCfmVsUg==)"
|
||||
WebUI\Port=8090
|
||||
WebUI\UseUPnP=false
|
||||
WebUI\Username=admin
|
||||
|
||||
[Network]
|
||||
PortForwardingEnabled=false
|
||||
EOF
|
||||
msg_ok "Setup qBittorrent-nox"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/qbittorrent-nox.service
|
||||
[Unit]
|
||||
Description=qBittorrent client
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/opt/qbittorrent/qbittorrent-nox
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now qbittorrent-nox
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
49
install/radarr-install.sh
Normal file
49
install/radarr-install.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://radarr.video/ | Github: https://github.com/Radarr/Radarr
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y sqlite3
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
fetch_and_deploy_gh_release "Radarr" "Radarr/Radarr" "prebuild" "latest" "/opt/Radarr" "Radarr.master*linux-core-x64.tar.gz"
|
||||
|
||||
msg_info "Configuring Radarr"
|
||||
mkdir -p /var/lib/radarr/
|
||||
chmod 775 /var/lib/radarr/ /opt/Radarr/
|
||||
msg_ok "Configured Radarr"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/radarr.service
|
||||
[Unit]
|
||||
Description=Radarr Daemon
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
UMask=0002
|
||||
Type=simple
|
||||
ExecStart=/opt/Radarr/Radarr -nobrowser -data=/var/lib/radarr/
|
||||
TimeoutStopSec=20
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now radarr
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
52
install/rdtclient-install.sh
Normal file
52
install/rdtclient-install.sh
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/rogerfar/rdt-client
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
setup_deb822_repo \
|
||||
"microsoft" \
|
||||
"https://packages.microsoft.com/keys/microsoft-2025.asc" \
|
||||
"https://packages.microsoft.com/debian/13/prod/" \
|
||||
"trixie"
|
||||
$STD apt install -y aspnetcore-runtime-10.0
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
fetch_and_deploy_gh_release "rdt-client" "rogerfar/rdt-client" "prebuild" "latest" "/opt/rdtc" "RealDebridClient.zip"
|
||||
|
||||
msg_info "Setting up rdtclient"
|
||||
cd /opt/rdtc
|
||||
mkdir -p data/{db,downloads}
|
||||
sed -i 's#/data/db/#/opt/rdtc&#g' /opt/rdtc/appsettings.json
|
||||
msg_ok "Configured rdtclient"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/rdtc.service
|
||||
[Unit]
|
||||
Description=RdtClient Service
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/rdtc
|
||||
ExecStart=/usr/bin/dotnet RdtClient.Web.dll
|
||||
SyslogIdentifier=RdtClient
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now rdtc
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
69
install/sabnzbd-install.sh
Normal file
69
install/sabnzbd-install.sh
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster) | Co-Author: MickLesk (CanbiZ)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://sabnzbd.org/ | Github: https://github.com/sabnzbd/sabnzbd
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
par2 \
|
||||
p7zip-full
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
PYTHON_VERSION="3.13" setup_uv
|
||||
|
||||
msg_info "Setup Unrar"
|
||||
cat <<EOF >/etc/apt/sources.list.d/non-free.sources
|
||||
Types: deb
|
||||
URIs: http://deb.debian.org/debian/
|
||||
Suites: trixie
|
||||
Components: non-free
|
||||
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
|
||||
EOF
|
||||
$STD apt update
|
||||
$STD apt install -y unrar
|
||||
msg_ok "Setup Unrar"
|
||||
|
||||
fetch_and_deploy_gh_release "sabnzbd-org" "sabnzbd/sabnzbd" "prebuild" "latest" "/opt/sabnzbd" "SABnzbd-*-src.tar.gz"
|
||||
|
||||
msg_info "Installing SABnzbd"
|
||||
$STD uv venv --clear /opt/sabnzbd/venv
|
||||
$STD uv pip install -r /opt/sabnzbd/requirements.txt --python=/opt/sabnzbd/venv/bin/python
|
||||
msg_ok "Installed SABnzbd"
|
||||
|
||||
read -r -p "Would you like to install par2cmdline-turbo? <y/N> " prompt
|
||||
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
|
||||
mv /usr/bin/par2 /usr/bin/par2.old
|
||||
fetch_and_deploy_gh_release "par2cmdline-turbo" "animetosho/par2cmdline-turbo" "prebuild" "latest" "/usr/bin/" "*-linux-amd64.zip"
|
||||
fi
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/sabnzbd.service
|
||||
[Unit]
|
||||
Description=SABnzbd
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/sabnzbd
|
||||
ExecStart=/opt/sabnzbd/venv/bin/python SABnzbd.py -s 0.0.0.0:7777
|
||||
Restart=always
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now sabnzbd
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
69
install/seerr-install.sh
Normal file
69
install/seerr-install.sh
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: CrazyWolf13
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://docs.seerr.dev/ | Github: https://github.com/seerr-team/seerr
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y \
|
||||
build-essential \
|
||||
python3-setuptools
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
fetch_and_deploy_gh_release "seerr" "seerr-team/seerr" "tarball"
|
||||
pnpm_desired=$(grep -Po '"pnpm":\s*"\K[^"]+' /opt/seerr/package.json)
|
||||
NODE_VERSION="22" NODE_MODULE="pnpm@$pnpm_desired" setup_nodejs
|
||||
|
||||
msg_info "Installing Seerr (Patience)"
|
||||
export CYPRESS_INSTALL_BINARY=0
|
||||
cd /opt/seerr
|
||||
$STD pnpm install --frozen-lockfile
|
||||
export NODE_OPTIONS="--max-old-space-size=3072"
|
||||
$STD pnpm build
|
||||
mkdir -p /etc/seerr/
|
||||
cat <<EOF >/etc/seerr/seerr.conf
|
||||
## Seerr's default port is 5055, if you want to use both, change this.
|
||||
## specify on which port to listen
|
||||
PORT=5055
|
||||
|
||||
## specify on which interface to listen, by default seerr listens on all interfaces
|
||||
HOST=0.0.0.0
|
||||
|
||||
## Uncomment if you want to force Node.js to resolve IPv4 before IPv6 (advanced users only)
|
||||
# FORCE_IPV4_FIRST=true
|
||||
EOF
|
||||
msg_ok "Installed Seerr"
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/seerr.service
|
||||
[Unit]
|
||||
Description=Seerr Service
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=/etc/seerr/seerr.conf
|
||||
Environment=NODE_ENV=production
|
||||
Type=exec
|
||||
Restart=on-failure
|
||||
WorkingDirectory=/opt/seerr
|
||||
ExecStart=/usr/bin/node dist/index.js
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now seerr
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
119
install/slskd-install.sh
Normal file
119
install/slskd-install.sh
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: vhsdream
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://github.com/slskd/slskd/, https://github.com/mrusse/soularr
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
fetch_and_deploy_gh_release "Slskd" "slskd/slskd" "prebuild" "latest" "/opt/slskd" "slskd-*-linux-x64.zip"
|
||||
|
||||
msg_info "Configuring Slskd"
|
||||
JWT_KEY=$(openssl rand -base64 44)
|
||||
SLSKD_API_KEY=$(openssl rand -base64 44)
|
||||
cp /opt/slskd/config/slskd.example.yml /opt/slskd/config/slskd.yml
|
||||
sed -i \
|
||||
-e '/web:/,/cidr/s/^# //' \
|
||||
-e '/https:/,/port: 5031/s/false/true/' \
|
||||
-e '/port: 5030/,/socket/s/,.*$//' \
|
||||
-e '/content_path:/,/authentication/s/false/true/' \
|
||||
-e "\|api_keys|,\|cidr|s|<some.*$|$SLSKD_API_KEY|; \
|
||||
s|role: readonly|role: readwrite|; \
|
||||
s|0.0.0.0/0,::/0|& # Replace this with your subnet|" \
|
||||
-e "\|jwt:|,\|ttl|s|key: ~|key: $JWT_KEY|" \
|
||||
-e '/soulseek/,/write_queue/s/^# //' \
|
||||
-e 's/^.*picture/#&/' /opt/slskd/config/slskd.yml
|
||||
msg_ok "Configured Slskd"
|
||||
|
||||
read -rp "${TAB3}Do you want to install Soularr? y/N " soularr
|
||||
if [[ ${soularr,,} =~ ^(y|yes)$ ]]; then
|
||||
PYTHON_VERSION="3.11" setup_uv
|
||||
fetch_and_deploy_gh_release "Soularr" "mrusse/soularr" "tarball" "latest" "/opt/soularr"
|
||||
cd /opt/soularr
|
||||
$STD uv venv venv
|
||||
$STD source venv/bin/activate
|
||||
$STD uv pip install -r requirements.txt
|
||||
sed -i \
|
||||
-e "\|[Slskd]|,\|host_url|s|yourslskdapikeygoeshere|$SLSKD_API_KEY|" \
|
||||
-e "/host_url/s/slskd/localhost/" \
|
||||
/opt/soularr/config.ini
|
||||
cat <<EOF >/opt/soularr/run.sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if ps aux | grep "[s]oularr.py" >/dev/null; then
|
||||
echo "Soularr is already running. Exiting..." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove stale lock file from previous ungraceful exit
|
||||
rm -f "/opt/soularr/.soularr.lock"
|
||||
|
||||
source /opt/soularr/venv/bin/activate
|
||||
uv run python3 -u /opt/soularr/soularr.py --config-dir /opt/soularr 2>&1
|
||||
EOF
|
||||
chmod +x /opt/soularr/run.sh
|
||||
deactivate
|
||||
msg_ok "Installed Soularr"
|
||||
fi
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/slskd.service
|
||||
[Unit]
|
||||
Description=Slskd Service
|
||||
After=network.target
|
||||
Wants=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/opt/slskd
|
||||
ExecStart=/opt/slskd/slskd --config /opt/slskd/config/slskd.yml
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
if [[ -d /opt/soularr ]]; then
|
||||
cat <<EOF >/etc/systemd/system/soularr.timer
|
||||
[Unit]
|
||||
Description=Soularr service timer
|
||||
RefuseManualStart=no
|
||||
RefuseManualStop=no
|
||||
|
||||
[Timer]
|
||||
Persistent=true
|
||||
# run every 10 minutes
|
||||
OnCalendar=*-*-* *:0/10:00
|
||||
Unit=soularr.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
|
||||
cat <<EOF >/etc/systemd/system/soularr.service
|
||||
[Unit]
|
||||
Description=Soularr service
|
||||
After=network.target slskd.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/soularr
|
||||
ExecStart=/bin/bash -c /opt/soularr/run.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
msg_warn "Add your Lidarr API key to Soularr in '/opt/soularr/config.ini', then run 'systemctl enable --now soularr.timer'"
|
||||
fi
|
||||
systemctl enable -q --now slskd
|
||||
msg_ok "Created Services"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
45
install/sonarr-install.sh
Normal file
45
install/sonarr-install.sh
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 tteck
|
||||
# Author: tteck (tteckster)
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
# Source: https://sonarr.tv/ | Github: https://github.com/Sonarr/Sonarr
|
||||
|
||||
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||
color
|
||||
verb_ip6
|
||||
catch_errors
|
||||
setting_up_container
|
||||
network_check
|
||||
update_os
|
||||
|
||||
msg_info "Installing Dependencies"
|
||||
$STD apt install -y sqlite3
|
||||
msg_ok "Installed Dependencies"
|
||||
|
||||
fetch_and_deploy_gh_release "Sonarr" "Sonarr/Sonarr" "prebuild" "latest" "/opt/Sonarr" "Sonarr.main.*.linux-x64.tar.gz"
|
||||
mkdir -p /var/lib/sonarr/
|
||||
chmod 775 /var/lib/sonarr/
|
||||
|
||||
msg_info "Creating Service"
|
||||
cat <<EOF >/etc/systemd/system/sonarr.service
|
||||
[Unit]
|
||||
Description=Sonarr Daemon
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/opt/Sonarr/Sonarr -nobrowser -data=/var/lib/sonarr/
|
||||
TimeoutStopSec=20
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl enable -q --now sonarr
|
||||
msg_ok "Created Service"
|
||||
|
||||
motd_ssh
|
||||
customize
|
||||
cleanup_lxc
|
||||
40
json/arr-stack.json
Normal file
40
json/arr-stack.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "ARR-Stack",
|
||||
"slug": "arr-stack",
|
||||
"categories": [
|
||||
12
|
||||
],
|
||||
"date_created": "2026-01-18",
|
||||
"type": "ct",
|
||||
"updateable": true,
|
||||
"privileged": false,
|
||||
"interface_port": 3010,
|
||||
"documentation": "community-scripts/ProxmoxVED/blob/main/tools/arr-stack.sh",
|
||||
"website": "https://community-scripts.org",
|
||||
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/ProxmoxVE.webp",
|
||||
"description": "Proxmox VE Helper Scripts for setting up a stack of applications for use with Prowlarr, Sonarr, Radarr, Lidarr, Seerr, and qBittorrent.",
|
||||
"install_methods": [
|
||||
{
|
||||
"type": "default",
|
||||
"script": "tools/arr-stack.sh",
|
||||
"config_path": "",
|
||||
"resources": {
|
||||
"cpu": 1,
|
||||
"ram": 1,
|
||||
"hdd": 2,
|
||||
"os": "Debian",
|
||||
"version": "13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_credentials": {
|
||||
"username": null,
|
||||
"password": null
|
||||
},
|
||||
"notes": [
|
||||
{
|
||||
"text": "This script will prompt you to select the applications you want to install and configure the network settings.",
|
||||
"type": "info"
|
||||
}
|
||||
]
|
||||
}
|
||||
981
tools/arr-stack.sh
Normal file
981
tools/arr-stack.sh
Normal file
@@ -0,0 +1,981 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright (c) 2021-2026 community-scripts ORG
|
||||
# Author: community-scripts
|
||||
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
||||
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/core.func)
|
||||
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func)
|
||||
|
||||
set -eEo pipefail
|
||||
|
||||
color
|
||||
formatting
|
||||
icons
|
||||
set_std_mode
|
||||
|
||||
SILENT_LOGFILE="/tmp/arr-stack-$$.log"
|
||||
silent() { "$@" >>"$SILENT_LOGFILE" 2>&1; }
|
||||
|
||||
msg_info() { echo -e "${INFO:-[i]} ${YW}${1}${CL}"; }
|
||||
msg_ok() { echo -e "${CM:-[ok]} ${GN}${1}${CL}"; }
|
||||
msg_warn() { echo -e "${YW}[WARN]${CL} ${1}"; }
|
||||
msg_error() { echo -e "${CROSS:-[x]} ${RD}${1}${CL}"; }
|
||||
msg_step() { echo -e "${BL}==>${CL} ${1}"; }
|
||||
|
||||
cancelled() { msg_warn "Cancelled at $1."; exit 0; }
|
||||
|
||||
var_container_storage="${var_container_storage:-}"
|
||||
var_template_storage="${var_template_storage:-}"
|
||||
var_bridge="${var_bridge:-}"
|
||||
var_gateway="${var_gateway:-}"
|
||||
var_cidr="${var_cidr:-24}"
|
||||
var_start_ctid="${var_start_ctid:-}"
|
||||
var_repo="${var_repo:-ProxmoxVED}"
|
||||
SUMMARY_FILE="${SUMMARY_FILE:-/root/arr-stack-summary.txt}"
|
||||
|
||||
BACKTITLE="Proxmox VE Helper Scripts — arr Stack"
|
||||
|
||||
TEMP_DIR=$(mktemp -d)
|
||||
_on_exit() {
|
||||
local rc=$?
|
||||
if (( rc != 0 )); then
|
||||
if (( ${#INSTALLED_SLUGS[@]} > 0 )); then orphan_report; fi
|
||||
if [[ -s "$SILENT_LOGFILE" ]]; then
|
||||
echo
|
||||
msg_error "Last 20 lines of ${SILENT_LOGFILE}:"
|
||||
tail -n 20 "$SILENT_LOGFILE"
|
||||
fi
|
||||
fi
|
||||
rm -rf "$TEMP_DIR"
|
||||
}
|
||||
trap _on_exit EXIT
|
||||
|
||||
declare -A CTID_BY_SLUG
|
||||
declare -A IP_BY_SLUG
|
||||
declare -A PORT_BY_SLUG
|
||||
declare -A APIKEY_BY_SLUG
|
||||
declare -A USER_BY_SLUG
|
||||
declare -A PASS_BY_SLUG
|
||||
declare -A SCRIPT_BY_SLUG
|
||||
declare -A IMPL_BY_SLUG
|
||||
declare -A KIND_BY_SLUG
|
||||
declare -A ARR_API_VER_BY_SLUG
|
||||
declare -A NAME_BY_SLUG
|
||||
declare -A CONFIG_CONTRACT_BY_SLUG
|
||||
|
||||
SELECTED_ARRS=""
|
||||
SELECTED_CLIENTS=""
|
||||
ORDERED_SLUGS=()
|
||||
INSTALLED_SLUGS=()
|
||||
WIRING_RESULTS=()
|
||||
WIRING_FAILURES=()
|
||||
|
||||
SYNC_CATEGORIES_SONARR='[5000,5010,5020,5030,5040,5045,5050]'
|
||||
SYNC_CATEGORIES_RADARR='[2000,2010,2020,2030,2040,2045,2050,2060]'
|
||||
SYNC_CATEGORIES_LIDARR='[3000,3010,3020,3030,3040]'
|
||||
|
||||
header_info() {
|
||||
clear
|
||||
cat <<"EOF"
|
||||
_ _
|
||||
__ _ _ __ _ __ ___| |_ __ _ ___| | __
|
||||
/ _` | '__| '__|____ / __| __/ _` |/ __| |/ /
|
||||
| (_| | | | | |_____|\__ \ || (_| | (__| <
|
||||
\__,_|_| |_| |___/\__\__,_|\___|_|\_\
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
msg_error "Run this script as root."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_pve_tools() {
|
||||
local missing=()
|
||||
for cmd in pct pvesh pvesm; do
|
||||
command -v "$cmd" >/dev/null 2>&1 || missing+=("$cmd")
|
||||
done
|
||||
if (( ${#missing[@]} > 0 )); then
|
||||
msg_error "Missing Proxmox VE tools: ${missing[*]}. Run this on a PVE node."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_port() {
|
||||
local ip=$1 port=$2 timeout=${3:-60} elapsed=0
|
||||
while ! (echo > "/dev/tcp/${ip}/${port}") >/dev/null 2>&1; do
|
||||
sleep 2
|
||||
elapsed=$((elapsed + 2))
|
||||
if (( elapsed >= timeout )); then return 1; fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
is_valid_ipv4() {
|
||||
local ip=$1
|
||||
[[ "$ip" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]] || return 1
|
||||
local a=${BASH_REMATCH[1]} b=${BASH_REMATCH[2]} c=${BASH_REMATCH[3]} d=${BASH_REMATCH[4]}
|
||||
(( a <= 255 && b <= 255 && c <= 255 && d <= 255 )) || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
seed_catalog() {
|
||||
while IFS='|' read -r slug script port impl apiver kind name contract; do
|
||||
[[ -z "$slug" ]] && continue
|
||||
SCRIPT_BY_SLUG[$slug]="$script"
|
||||
PORT_BY_SLUG[$slug]="$port"
|
||||
IMPL_BY_SLUG[$slug]="$impl"
|
||||
ARR_API_VER_BY_SLUG[$slug]="$apiver"
|
||||
KIND_BY_SLUG[$slug]="$kind"
|
||||
NAME_BY_SLUG[$slug]="$name"
|
||||
CONFIG_CONTRACT_BY_SLUG[$slug]="$contract"
|
||||
done <<'EOF'
|
||||
prowlarr|prowlarr.sh|9696||v1|indexer|Prowlarr|
|
||||
sonarr|sonarr.sh|8989|Sonarr|v3|arr|Sonarr|SonarrSettings
|
||||
radarr|radarr.sh|7878|Radarr|v3|arr|Radarr|RadarrSettings
|
||||
lidarr|lidarr.sh|8686|Lidarr|v1|arr|Lidarr|LidarrSettings
|
||||
seerr|seerr.sh|5055||-|requests|Seerr|
|
||||
qbittorrent|qbittorrent.sh|8090|QBittorrent|-|client|qBittorrent|QBittorrentSettings
|
||||
sabnzbd|sabnzbd.sh|7777|Sabnzbd|-|client|SABnzbd|SabnzbdSettings
|
||||
EOF
|
||||
}
|
||||
|
||||
pick_storage() {
|
||||
if [[ -n "$var_container_storage" ]]; then
|
||||
msg_info "Container storage (from env): ${var_container_storage}"
|
||||
else
|
||||
local options=() row name type
|
||||
while IFS= read -r row; do
|
||||
name=$(awk '{print $1}' <<<"$row")
|
||||
type=$(awk '{print $2}' <<<"$row")
|
||||
[[ -z "$name" ]] && continue
|
||||
options+=("$name" "$type")
|
||||
done < <(pvesm status -content rootdir 2>/dev/null | awk 'NR>1')
|
||||
|
||||
if (( ${#options[@]} == 0 )); then
|
||||
msg_error "No PVE storage with content 'rootdir' available."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if (( ${#options[@]} == 2 )); then
|
||||
var_container_storage="${options[0]}"
|
||||
msg_info "Container storage (only option): ${var_container_storage}"
|
||||
else
|
||||
var_container_storage=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "Container Storage" \
|
||||
--menu "Pick a PVE storage for the container rootfs:" 20 70 10 \
|
||||
"${options[@]}" 3>&1 1>&2 2>&3) || cancelled "storage pick"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$var_template_storage" ]]; then
|
||||
var_template_storage=$(pvesm status -content vztmpl 2>/dev/null \
|
||||
| awk 'NR>1 && $1=="local" {print $1; exit}')
|
||||
[[ -z "$var_template_storage" ]] && var_template_storage=$(pvesm status -content vztmpl 2>/dev/null \
|
||||
| awk 'NR>1 {print $1; exit}')
|
||||
fi
|
||||
[[ -n "$var_template_storage" ]] && msg_info "Template storage: ${var_template_storage}"
|
||||
}
|
||||
|
||||
pick_network_defaults() {
|
||||
if [[ -z "$var_bridge" ]]; then
|
||||
local options=() b
|
||||
while IFS= read -r b; do
|
||||
[[ -n "$b" ]] && options+=("$b" "")
|
||||
done < <(awk '/^iface vmbr/ {print $2}' /etc/network/interfaces 2>/dev/null)
|
||||
|
||||
if (( ${#options[@]} == 0 )); then
|
||||
options=("vmbr0" "")
|
||||
fi
|
||||
|
||||
var_bridge=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "Network Bridge" \
|
||||
--menu "Pick the Linux bridge for all containers:" 15 60 6 \
|
||||
"${options[@]}" 3>&1 1>&2 2>&3) || cancelled "bridge pick"
|
||||
fi
|
||||
|
||||
while [[ -z "$var_gateway" ]] || ! is_valid_ipv4 "$var_gateway"; do
|
||||
var_gateway=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "Gateway" \
|
||||
--inputbox "IPv4 gateway for the container subnet:" 10 60 \
|
||||
"${var_gateway:-}" 3>&1 1>&2 2>&3) || cancelled "gateway prompt"
|
||||
if ! is_valid_ipv4 "$var_gateway"; then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Invalid" \
|
||||
--msgbox "Not a valid IPv4 address: ${var_gateway}" 8 60
|
||||
var_gateway=""
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
var_cidr=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "CIDR Mask" \
|
||||
--inputbox "Network mask (1-32, e.g. 24):" 10 60 \
|
||||
"${var_cidr:-24}" 3>&1 1>&2 2>&3) || cancelled "CIDR prompt"
|
||||
if [[ "$var_cidr" =~ ^[0-9]+$ ]] && (( var_cidr >= 1 && var_cidr <= 32 )); then
|
||||
break
|
||||
fi
|
||||
whiptail --backtitle "$BACKTITLE" --title "Invalid" \
|
||||
--msgbox "CIDR must be an integer between 1 and 32." 8 60
|
||||
done
|
||||
|
||||
msg_info "Bridge ${var_bridge} | gateway ${var_gateway} | mask /${var_cidr}"
|
||||
}
|
||||
|
||||
pick_apps() {
|
||||
while true; do
|
||||
local choice
|
||||
choice=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "Pick *arr Apps" \
|
||||
--checklist "Prowlarr is always installed. Pick additional apps:" 16 70 6 \
|
||||
"sonarr" "Sonarr (TV)" ON \
|
||||
"radarr" "Radarr (Movies)" ON \
|
||||
"lidarr" "Lidarr (Music)" OFF \
|
||||
"seerr" "Seerr (Requests)" OFF \
|
||||
3>&1 1>&2 2>&3) || cancelled "*arr app pick"
|
||||
|
||||
SELECTED_ARRS=$(echo "$choice" | tr -d '"')
|
||||
|
||||
if [[ -z "$SELECTED_ARRS" ]]; then
|
||||
if whiptail --backtitle "$BACKTITLE" --title "Confirm" \
|
||||
--yesno "You picked no *arr apps. Only Prowlarr will be installed and there will be nothing to wire. Continue anyway?" 10 70; then
|
||||
return
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
return
|
||||
done
|
||||
}
|
||||
|
||||
pick_clients() {
|
||||
local choice
|
||||
choice=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "Pick Download Clients" \
|
||||
--checklist "Optional download clients to install + wire:" 14 70 4 \
|
||||
"qbittorrent" "qBittorrent (Torrents)" ON \
|
||||
"sabnzbd" "SABnzbd (Usenet)" OFF \
|
||||
3>&1 1>&2 2>&3) || cancelled "download client pick"
|
||||
|
||||
SELECTED_CLIENTS=$(echo "$choice" | tr -d '"')
|
||||
}
|
||||
|
||||
compute_ordered_slugs() {
|
||||
ORDERED_SLUGS=("prowlarr")
|
||||
local s
|
||||
for s in $SELECTED_ARRS; do
|
||||
[[ "$s" == "seerr" ]] && continue
|
||||
ORDERED_SLUGS+=("$s")
|
||||
done
|
||||
for s in $SELECTED_CLIENTS; do
|
||||
ORDERED_SLUGS+=("$s")
|
||||
done
|
||||
for s in $SELECTED_ARRS; do
|
||||
[[ "$s" == "seerr" ]] && ORDERED_SLUGS+=("seerr")
|
||||
done
|
||||
}
|
||||
|
||||
pick_ip_mode_and_ips() {
|
||||
while true; do
|
||||
local mode
|
||||
mode=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "IP Entry Mode" \
|
||||
--menu "How would you like to enter IP addresses?" 15 75 3 \
|
||||
"list" "Enter all IPs at once (space- or comma-separated)" \
|
||||
"one_by_one" "Prompt per container" \
|
||||
"auto" "Auto-pick free IPs from a starting IP or range(s)" \
|
||||
3>&1 1>&2 2>&3) || cancelled "IP entry mode pick"
|
||||
|
||||
case "$mode" in
|
||||
list) _collect_ips_list_mode; return ;;
|
||||
one_by_one) _collect_ips_one_by_one; return ;;
|
||||
auto) _collect_ips_auto && return ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
_parse_ip_ranges() {
|
||||
local expr=$1
|
||||
local -a segments
|
||||
IFS=',' read -ra segments <<<"$expr"
|
||||
local seg prefix start end i
|
||||
for seg in "${segments[@]}"; do
|
||||
seg="${seg// /}"
|
||||
[[ -z "$seg" ]] && continue
|
||||
if [[ "$seg" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)-([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)$ ]]; then
|
||||
if [[ "${BASH_REMATCH[1]}" != "${BASH_REMATCH[3]}" ]]; then
|
||||
echo "ERR: cross-subnet range not supported: $seg" >&2; return 1
|
||||
fi
|
||||
prefix=${BASH_REMATCH[1]}; start=${BASH_REMATCH[2]}; end=${BASH_REMATCH[4]}
|
||||
elif [[ "$seg" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)-([0-9]+)$ ]]; then
|
||||
prefix=${BASH_REMATCH[1]}; start=${BASH_REMATCH[2]}; end=${BASH_REMATCH[3]}
|
||||
elif [[ "$seg" =~ ^([0-9]+\.[0-9]+\.[0-9]+\.)([0-9]+)$ ]]; then
|
||||
prefix=${BASH_REMATCH[1]}; start=${BASH_REMATCH[2]}; end=254
|
||||
else
|
||||
echo "ERR: invalid segment: $seg" >&2; return 1
|
||||
fi
|
||||
if (( start > end || start < 0 || end > 255 )); then
|
||||
echo "ERR: out-of-range octet: $seg" >&2; return 1
|
||||
fi
|
||||
for ((i=start; i<=end; i++)); do
|
||||
echo "${prefix}${i}"
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
_ip_is_free() {
|
||||
local ip=$1
|
||||
[[ "$ip" == "$var_gateway" ]] && return 1
|
||||
if ping -c 1 -W 1 "$ip" >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
_collect_ips_auto() {
|
||||
local expected_n=${#ORDERED_SLUGS[@]}
|
||||
local hint="Examples:"$'\n'" 10.0.0.50 (start, scans upward to .254)"$'\n'" 10.0.0.50-99 (single range)"$'\n'" 10.0.0.50-60,10.0.0.80-99 (multiple ranges)"
|
||||
|
||||
while true; do
|
||||
local expr
|
||||
expr=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "Auto IP allocation" \
|
||||
--inputbox "Need ${expected_n} free IPs. Enter a starting IP or range expression:"$'\n\n'"${hint}" \
|
||||
16 78 "" 3>&1 1>&2 2>&3) || return 1
|
||||
|
||||
local -a parsed=()
|
||||
while IFS= read -r ip; do
|
||||
[[ -n "$ip" ]] && parsed+=("$ip")
|
||||
done < <(_parse_ip_ranges "$expr" 2>/dev/null)
|
||||
|
||||
if (( ${#parsed[@]} == 0 )); then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Invalid" \
|
||||
--msgbox "Could not parse any IPs from: ${expr}"$'\n\n'"Try a starting IP, a range like 10.0.0.50-99, or comma-separated ranges." 12 70
|
||||
continue
|
||||
fi
|
||||
|
||||
msg_info "Pinging ${#parsed[@]} candidate(s) for ${expected_n} free IP(s)..."
|
||||
local -a found=()
|
||||
local ip already used
|
||||
for ip in "${parsed[@]}"; do
|
||||
(( ${#found[@]} >= expected_n )) && break
|
||||
already=0
|
||||
for used in "${IP_BY_SLUG[@]}"; do
|
||||
[[ "$used" == "$ip" ]] && { already=1; break; }
|
||||
done
|
||||
(( already )) && continue
|
||||
if _ip_is_free "$ip"; then
|
||||
found+=("$ip")
|
||||
echo " free: ${ip}"
|
||||
fi
|
||||
done
|
||||
|
||||
if (( ${#found[@]} < expected_n )); then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Not enough free IPs" \
|
||||
--msgbox "Found ${#found[@]}/${expected_n} free IPs in the range. Widen the range and try again." 10 70
|
||||
continue
|
||||
fi
|
||||
|
||||
local lines="" i
|
||||
for i in "${!ORDERED_SLUGS[@]}"; do
|
||||
lines+=" $(printf '%-12s -> %s' "${ORDERED_SLUGS[$i]}" "${found[$i]}")"$'\n'
|
||||
done
|
||||
if ! whiptail --backtitle "$BACKTITLE" --title "Confirm auto-assigned IPs" \
|
||||
--yesno "Free IPs found:"$'\n\n'"${lines}"$'\n'"Use these?" 22 70; then
|
||||
continue
|
||||
fi
|
||||
|
||||
for i in "${!ORDERED_SLUGS[@]}"; do
|
||||
IP_BY_SLUG[${ORDERED_SLUGS[$i]}]=${found[$i]}
|
||||
done
|
||||
return 0
|
||||
done
|
||||
}
|
||||
|
||||
_collect_ips_list_mode() {
|
||||
local expected_n=${#ORDERED_SLUGS[@]}
|
||||
local hint="" s
|
||||
for s in "${ORDERED_SLUGS[@]}"; do hint+=" ${s}"$'\n'; done
|
||||
|
||||
while true; do
|
||||
local raw
|
||||
raw=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "Enter ${expected_n} IPv4 addresses" \
|
||||
--inputbox "Enter ${expected_n} IPs separated by spaces or commas, in this order:"$'\n\n'"${hint}" \
|
||||
22 78 "" 3>&1 1>&2 2>&3) || cancelled "IP list entry"
|
||||
|
||||
local normalized="${raw//,/ }"
|
||||
local -a ips=()
|
||||
# shellcheck disable=SC2206
|
||||
ips=( $normalized )
|
||||
|
||||
if (( ${#ips[@]} != expected_n )); then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Wrong count" \
|
||||
--msgbox "Expected ${expected_n} IPs, got ${#ips[@]}. Please re-enter." 8 60
|
||||
continue
|
||||
fi
|
||||
|
||||
local ok=1 i
|
||||
for i in "${!ips[@]}"; do
|
||||
if ! is_valid_ipv4 "${ips[$i]}"; then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Invalid" \
|
||||
--msgbox "Entry $((i+1)) is not a valid IPv4: ${ips[$i]}" 8 60
|
||||
ok=0; break
|
||||
fi
|
||||
if [[ "${ips[$i]}" == "$var_gateway" ]]; then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Invalid" \
|
||||
--msgbox "Entry $((i+1)) collides with the gateway: ${ips[$i]}" 8 60
|
||||
ok=0; break
|
||||
fi
|
||||
done
|
||||
(( ok == 0 )) && continue
|
||||
|
||||
local dup
|
||||
dup=$(printf '%s\n' "${ips[@]}" | sort | uniq -d | head -n1)
|
||||
if [[ -n "$dup" ]]; then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Duplicate IP" \
|
||||
--msgbox "IP appears more than once: ${dup}" 8 60
|
||||
continue
|
||||
fi
|
||||
|
||||
for i in "${!ORDERED_SLUGS[@]}"; do
|
||||
IP_BY_SLUG[${ORDERED_SLUGS[$i]}]=${ips[$i]}
|
||||
done
|
||||
return
|
||||
done
|
||||
}
|
||||
|
||||
_collect_ips_one_by_one() {
|
||||
local slug ip running=""
|
||||
for slug in "${ORDERED_SLUGS[@]}"; do
|
||||
while true; do
|
||||
ip=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "IP for ${slug}" \
|
||||
--inputbox "Enter IPv4 for ${slug}.${running:+$'\n\nAlready assigned:'}${running}" \
|
||||
16 60 "" 3>&1 1>&2 2>&3) || cancelled "IP prompt for ${slug}"
|
||||
|
||||
if ! is_valid_ipv4 "$ip"; then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Invalid" \
|
||||
--msgbox "Not a valid IPv4: ${ip}" 8 60
|
||||
continue
|
||||
fi
|
||||
if [[ "$ip" == "$var_gateway" ]]; then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Invalid" \
|
||||
--msgbox "Collides with the gateway: ${ip}" 8 60
|
||||
continue
|
||||
fi
|
||||
local dup=0 other
|
||||
for other in "${IP_BY_SLUG[@]}"; do
|
||||
[[ "$other" == "$ip" ]] && { dup=1; break; }
|
||||
done
|
||||
if (( dup )); then
|
||||
whiptail --backtitle "$BACKTITLE" --title "Duplicate" \
|
||||
--msgbox "Already used by another container: ${ip}" 8 60
|
||||
continue
|
||||
fi
|
||||
|
||||
IP_BY_SLUG[$slug]=$ip
|
||||
running+=$'\n '"${slug} -> ${ip}"
|
||||
break
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
pick_start_ctid() {
|
||||
local default_start
|
||||
if [[ -n "$var_start_ctid" ]]; then
|
||||
default_start="$var_start_ctid"
|
||||
else
|
||||
default_start=$(pvesh get /cluster/nextid 2>/dev/null || echo "100")
|
||||
fi
|
||||
|
||||
local start
|
||||
start=$(whiptail --backtitle "$BACKTITLE" \
|
||||
--title "Starting CTID" \
|
||||
--inputbox "Starting Container ID (in-use IDs are skipped):" 10 60 \
|
||||
"$default_start" 3>&1 1>&2 2>&3) || cancelled "starting CTID prompt"
|
||||
|
||||
if ! [[ "$start" =~ ^[0-9]+$ ]]; then
|
||||
msg_error "Invalid CTID: $start"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local id=$start s
|
||||
for s in "${ORDERED_SLUGS[@]}"; do
|
||||
while pct status "$id" >/dev/null 2>&1; do
|
||||
id=$((id + 1))
|
||||
(( id > 999999 )) && { msg_error "Ran out of CTID space."; exit 1; }
|
||||
done
|
||||
CTID_BY_SLUG[$s]=$id
|
||||
id=$((id + 1))
|
||||
done
|
||||
}
|
||||
|
||||
confirm_summary() {
|
||||
local lines="" s
|
||||
for s in "${ORDERED_SLUGS[@]}"; do
|
||||
lines+=" $(printf '%-12s ctid=%-5s ip=%-16s port=%s' \
|
||||
"$s" "${CTID_BY_SLUG[$s]}" "${IP_BY_SLUG[$s]}" "${PORT_BY_SLUG[$s]}")"$'\n'
|
||||
done
|
||||
|
||||
local body="About to create these containers and wire them together:"$'\n\n'"${lines}"$'\n'"Storage: ${var_container_storage} | Bridge: ${var_bridge} | Gateway: ${var_gateway} | Mask: /${var_cidr}"
|
||||
|
||||
whiptail --backtitle "$BACKTITLE" --title "Confirm" \
|
||||
--yesno "$body" 22 78 || { msg_warn "User cancelled."; exit 0; }
|
||||
}
|
||||
|
||||
orphan_report() {
|
||||
if (( ${#INSTALLED_SLUGS[@]} == 0 )); then return; fi
|
||||
msg_error "Containers already created (to clean up, run):"
|
||||
local s
|
||||
for s in "${INSTALLED_SLUGS[@]}"; do
|
||||
echo " pct stop ${CTID_BY_SLUG[$s]} && pct destroy ${CTID_BY_SLUG[$s]} # ${s}"
|
||||
done
|
||||
}
|
||||
|
||||
install_loop() {
|
||||
local total=${#ORDERED_SLUGS[@]} idx=0
|
||||
local s script_file ip ctid port
|
||||
|
||||
for s in "${ORDERED_SLUGS[@]}"; do
|
||||
idx=$((idx + 1))
|
||||
ip="${IP_BY_SLUG[$s]}"
|
||||
ctid="${CTID_BY_SLUG[$s]}"
|
||||
port="${PORT_BY_SLUG[$s]}"
|
||||
script_file="$TEMP_DIR/${s}.sh"
|
||||
|
||||
msg_step "[${idx}/${total}] Downloading ct/${s}.sh"
|
||||
$STD curl -fsSL \
|
||||
"https://raw.githubusercontent.com/community-scripts/${var_repo}/main/ct/${s}.sh" \
|
||||
-o "$script_file"
|
||||
|
||||
if [[ ! -s "$script_file" ]]; then
|
||||
msg_error "Empty/failed download for ${s}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg_step "[${idx}/${total}] Installing ${s} -> ctid=${ctid} ip=${ip}/${var_cidr}"
|
||||
$STD env \
|
||||
MODE=generated mode=generated PHS_SILENT=1 \
|
||||
var_ctid="$ctid" \
|
||||
var_hostname="$s" \
|
||||
var_brg="$var_bridge" \
|
||||
var_net="${ip}/${var_cidr}" \
|
||||
var_gateway="$var_gateway" \
|
||||
var_container_storage="$var_container_storage" \
|
||||
var_template_storage="$var_template_storage" \
|
||||
bash "$script_file"
|
||||
|
||||
INSTALLED_SLUGS+=("$s")
|
||||
msg_ok "Installed ${s}"
|
||||
|
||||
if [[ "${KIND_BY_SLUG[$s]}" == "arr" || "${KIND_BY_SLUG[$s]}" == "indexer" ]]; then
|
||||
msg_info "Waiting for ${s} to listen on ${port}..."
|
||||
if ! wait_for_port "$ip" "$port" 90; then
|
||||
msg_warn "${s} did not open ${port} within 90s; will retry during key extraction."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
extract_arr_key() {
|
||||
local slug=$1 ctid=$2 ip=$3 port=$4
|
||||
local config_dir="/var/lib/${slug}/config.xml"
|
||||
|
||||
msg_info "Waiting for ${slug} on ${ip}:${port}..."
|
||||
wait_for_port "$ip" "$port" 240 || { msg_error "${slug} never opened ${port}"; return 1; }
|
||||
|
||||
local i
|
||||
for ((i=0; i<60; i++)); do
|
||||
if pct exec "$ctid" -- test -f "$config_dir" 2>/dev/null; then break; fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
local key
|
||||
key=$(pct exec "$ctid" -- sed -n 's:.*<ApiKey>\([^<]*\)</ApiKey>.*:\1:p' "$config_dir" 2>/dev/null | head -n1 || true)
|
||||
if [[ -z "$key" ]]; then
|
||||
msg_error "Failed to extract API key for ${slug} (config: ${config_dir})"
|
||||
return 1
|
||||
fi
|
||||
APIKEY_BY_SLUG[$slug]="$key"
|
||||
msg_ok "${slug} apikey extracted (${key:0:6}…)"
|
||||
}
|
||||
|
||||
extract_sabnzbd_key() {
|
||||
local ctid=$1 ip=$2
|
||||
|
||||
msg_info "Waiting for sabnzbd on ${ip}:7777..."
|
||||
wait_for_port "$ip" 7777 240 || { msg_warn "sabnzbd never opened 7777"; return 1; }
|
||||
|
||||
local ini="" candidate
|
||||
for candidate in /opt/sabnzbd/sabnzbd.ini /root/.sabnzbd/sabnzbd.ini /etc/sabnzbd/sabnzbd.ini; do
|
||||
if pct exec "$ctid" -- test -f "$candidate" 2>/dev/null; then
|
||||
ini="$candidate"; break
|
||||
fi
|
||||
done
|
||||
if [[ -z "$ini" ]]; then
|
||||
msg_warn "Could not locate sabnzbd.ini inside ctid ${ctid}; SABnzbd will need manual setup."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local key="" i
|
||||
for ((i=0; i<60; i++)); do
|
||||
key=$(pct exec "$ctid" -- awk -F' *= *' '/^api_key/ {print $2; exit}' "$ini" 2>/dev/null || true)
|
||||
[[ -n "$key" ]] && break
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [[ -z "$key" ]]; then
|
||||
msg_warn "sabnzbd api_key not yet written. Open the web wizard once at http://${ip}:7777 and rerun wiring."
|
||||
return 1
|
||||
fi
|
||||
APIKEY_BY_SLUG[sabnzbd]="$key"
|
||||
msg_ok "sabnzbd apikey extracted (${key:0:6}…)"
|
||||
}
|
||||
|
||||
wait_and_extract_keys() {
|
||||
msg_step "Extracting credentials & API keys"
|
||||
local s ctid ip port tmp
|
||||
for s in "${ORDERED_SLUGS[@]}"; do
|
||||
ctid="${CTID_BY_SLUG[$s]}"
|
||||
ip="${IP_BY_SLUG[$s]}"
|
||||
port="${PORT_BY_SLUG[$s]}"
|
||||
case "${KIND_BY_SLUG[$s]}" in
|
||||
indexer|arr)
|
||||
extract_arr_key "$s" "$ctid" "$ip" "$port" || true
|
||||
;;
|
||||
client)
|
||||
if [[ "$s" == "qbittorrent" ]]; then
|
||||
USER_BY_SLUG[qbittorrent]="admin"
|
||||
PASS_BY_SLUG[qbittorrent]="adminadmin"
|
||||
tmp=$(pct exec "$ctid" -- bash -c "journalctl -u qbittorrent-nox --no-pager 2>/dev/null | grep -i 'temporary password' | tail -n1" 2>/dev/null || true)
|
||||
if [[ -n "$tmp" ]]; then
|
||||
msg_warn "qBittorrent journalctl mentioned a temporary password — see summary."
|
||||
PASS_BY_SLUG[qbittorrent]="<see journal: $tmp>"
|
||||
fi
|
||||
elif [[ "$s" == "sabnzbd" ]]; then
|
||||
extract_sabnzbd_key "$ctid" "$ip" || true
|
||||
fi
|
||||
;;
|
||||
requests)
|
||||
msg_warn "Seerr requires the web first-run wizard. URL + keys will be in the summary."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
record_wiring() { WIRING_RESULTS+=("$1"); }
|
||||
record_failure() { WIRING_FAILURES+=("$1"); }
|
||||
|
||||
api_post() {
|
||||
local url=$1 apikey=$2 payload=$3 label=$4
|
||||
local resp status=""
|
||||
resp=$(curl -fsS --max-time 30 --retry 2 \
|
||||
-H "X-Api-Key: $apikey" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST "$url" -d "$payload" \
|
||||
-w '\n__HTTP__%{http_code}' 2>&1) || status="curl_fail"
|
||||
|
||||
local code=""
|
||||
if [[ "$resp" =~ __HTTP__([0-9]+)$ ]]; then
|
||||
code="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
|
||||
if [[ "$status" == "curl_fail" || -z "$code" || "$code" -ge 400 ]]; then
|
||||
record_failure "${label} FAIL (http ${code:-?})"
|
||||
msg_warn "${label} failed (http ${code:-?})"
|
||||
return 1
|
||||
fi
|
||||
record_wiring "${label} OK"
|
||||
msg_ok "${label}"
|
||||
}
|
||||
|
||||
probe_lidarr_api_version() {
|
||||
if [[ -z "${APIKEY_BY_SLUG[lidarr]:-}" ]]; then return; fi
|
||||
local ip="${IP_BY_SLUG[lidarr]}" key="${APIKEY_BY_SLUG[lidarr]}"
|
||||
if curl -fsS --max-time 10 -H "X-Api-Key: $key" \
|
||||
"http://${ip}:8686/api/v3/system/status" >/dev/null 2>&1; then
|
||||
ARR_API_VER_BY_SLUG[lidarr]="v3"
|
||||
msg_info "Lidarr supports /api/v3 — using v3 for wiring."
|
||||
fi
|
||||
}
|
||||
|
||||
wire_arrs_into_prowlarr() {
|
||||
local prowlarr_ip="${IP_BY_SLUG[prowlarr]}"
|
||||
local prowlarr_key="${APIKEY_BY_SLUG[prowlarr]:-}"
|
||||
if [[ -z "$prowlarr_key" ]]; then
|
||||
msg_warn "Skipping Prowlarr wiring — no Prowlarr API key."
|
||||
return
|
||||
fi
|
||||
|
||||
local s sync_cats payload
|
||||
for s in $SELECTED_ARRS; do
|
||||
[[ "$s" == "seerr" ]] && continue
|
||||
local key="${APIKEY_BY_SLUG[$s]:-}"
|
||||
if [[ -z "$key" ]]; then
|
||||
record_failure "Prowlarr -> ${NAME_BY_SLUG[$s]} FAIL (no apikey)"
|
||||
continue
|
||||
fi
|
||||
|
||||
case "$s" in
|
||||
sonarr) sync_cats="$SYNC_CATEGORIES_SONARR" ;;
|
||||
radarr) sync_cats="$SYNC_CATEGORIES_RADARR" ;;
|
||||
lidarr) sync_cats="$SYNC_CATEGORIES_LIDARR" ;;
|
||||
*) sync_cats='[]' ;;
|
||||
esac
|
||||
|
||||
payload=$(jq -n \
|
||||
--arg name "${NAME_BY_SLUG[$s]}" \
|
||||
--arg impl "${IMPL_BY_SLUG[$s]}" \
|
||||
--arg contract "${CONFIG_CONTRACT_BY_SLUG[$s]}" \
|
||||
--arg prowlarr_url "http://${prowlarr_ip}:9696" \
|
||||
--arg base_url "http://${IP_BY_SLUG[$s]}:${PORT_BY_SLUG[$s]}" \
|
||||
--arg apikey "$key" \
|
||||
--argjson sync_cats "$sync_cats" \
|
||||
'{
|
||||
name: $name,
|
||||
syncLevel: "fullSync",
|
||||
implementation: $impl,
|
||||
implementationName: $impl,
|
||||
configContract: $contract,
|
||||
tags: [],
|
||||
fields: [
|
||||
{ name: "prowlarrUrl", value: $prowlarr_url },
|
||||
{ name: "baseUrl", value: $base_url },
|
||||
{ name: "apiKey", value: $apikey },
|
||||
{ name: "syncCategories", value: $sync_cats }
|
||||
]
|
||||
}')
|
||||
|
||||
api_post "http://${prowlarr_ip}:9696/api/v1/applications" \
|
||||
"$prowlarr_key" "$payload" \
|
||||
"Prowlarr -> ${NAME_BY_SLUG[$s]}" || true
|
||||
done
|
||||
}
|
||||
|
||||
wire_clients_into_arrs() {
|
||||
local arr client arr_key arr_ip arr_port api_ver category_field category_name payload url sab_key
|
||||
|
||||
for arr in $SELECTED_ARRS; do
|
||||
[[ "$arr" == "seerr" ]] && continue
|
||||
arr_key="${APIKEY_BY_SLUG[$arr]:-}"
|
||||
if [[ -z "$arr_key" ]]; then
|
||||
msg_warn "Skipping download-client wiring for ${arr} — no API key."
|
||||
continue
|
||||
fi
|
||||
arr_ip="${IP_BY_SLUG[$arr]}"
|
||||
arr_port="${PORT_BY_SLUG[$arr]}"
|
||||
api_ver="${ARR_API_VER_BY_SLUG[$arr]}"
|
||||
|
||||
case "$arr" in
|
||||
sonarr) category_field="tvCategory"; category_name="tv-sonarr" ;;
|
||||
radarr) category_field="movieCategory"; category_name="radarr" ;;
|
||||
lidarr) category_field="musicCategory"; category_name="lidarr" ;;
|
||||
esac
|
||||
|
||||
for client in $SELECTED_CLIENTS; do
|
||||
url="http://${arr_ip}:${arr_port}/api/${api_ver}/downloadclient"
|
||||
|
||||
if [[ "$client" == "qbittorrent" ]]; then
|
||||
payload=$(jq -n \
|
||||
--arg host "${IP_BY_SLUG[qbittorrent]}" \
|
||||
--argjson port 8090 \
|
||||
--arg user "${USER_BY_SLUG[qbittorrent]}" \
|
||||
--arg pass "${PASS_BY_SLUG[qbittorrent]}" \
|
||||
--arg category_field "$category_field" \
|
||||
--arg category_name "$category_name" \
|
||||
'{
|
||||
enable: true, protocol: "torrent", priority: 1,
|
||||
name: "qBittorrent",
|
||||
implementation: "QBittorrent",
|
||||
implementationName: "qBittorrent",
|
||||
configContract: "QBittorrentSettings",
|
||||
tags: [],
|
||||
fields: [
|
||||
{ name: "host", value: $host },
|
||||
{ name: "port", value: $port },
|
||||
{ name: "useSsl", value: false },
|
||||
{ name: "username", value: $user },
|
||||
{ name: "password", value: $pass },
|
||||
{ name: $category_field, value: $category_name }
|
||||
]
|
||||
}')
|
||||
api_post "$url" "$arr_key" "$payload" \
|
||||
"${NAME_BY_SLUG[$arr]} -> qBittorrent" || true
|
||||
|
||||
elif [[ "$client" == "sabnzbd" ]]; then
|
||||
sab_key="${APIKEY_BY_SLUG[sabnzbd]:-}"
|
||||
if [[ -z "$sab_key" ]]; then
|
||||
record_failure "${NAME_BY_SLUG[$arr]} -> SABnzbd FAIL (no sab apikey)"
|
||||
continue
|
||||
fi
|
||||
payload=$(jq -n \
|
||||
--arg host "${IP_BY_SLUG[sabnzbd]}" \
|
||||
--argjson port 7777 \
|
||||
--arg apikey "$sab_key" \
|
||||
--arg category_field "$category_field" \
|
||||
--arg category_name "$category_name" \
|
||||
'{
|
||||
enable: true, protocol: "usenet", priority: 1,
|
||||
name: "SABnzbd",
|
||||
implementation: "Sabnzbd",
|
||||
implementationName: "SABnzbd",
|
||||
configContract: "SabnzbdSettings",
|
||||
tags: [],
|
||||
fields: [
|
||||
{ name: "host", value: $host },
|
||||
{ name: "port", value: $port },
|
||||
{ name: "apiKey", value: $apikey },
|
||||
{ name: "useSsl", value: false },
|
||||
{ name: $category_field, value: $category_name }
|
||||
]
|
||||
}')
|
||||
api_post "$url" "$arr_key" "$payload" \
|
||||
"${NAME_BY_SLUG[$arr]} -> SABnzbd" || true
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
wire_apis() {
|
||||
msg_step "Wiring apps together via HTTP APIs"
|
||||
probe_lidarr_api_version
|
||||
wire_arrs_into_prowlarr
|
||||
wire_clients_into_arrs
|
||||
|
||||
if [[ " $SELECTED_ARRS " == *" seerr "* ]]; then
|
||||
record_wiring "Seerr -> (manual via web wizard)"
|
||||
msg_warn "Seerr can't be wired headlessly. URLs and keys are in the summary."
|
||||
fi
|
||||
}
|
||||
|
||||
write_summary() {
|
||||
msg_step "Writing summary"
|
||||
local now host
|
||||
now=$(date '+%Y-%m-%d %H:%M:%S %Z')
|
||||
host=$(hostname)
|
||||
|
||||
local -a lines=()
|
||||
lines+=( "============================================================" )
|
||||
lines+=( " arr Stack — Provisioning Summary" )
|
||||
lines+=( " Generated: ${now}" )
|
||||
lines+=( " Host: ${host}" )
|
||||
lines+=( "============================================================" )
|
||||
lines+=( "" )
|
||||
lines+=( "[Shared settings]" )
|
||||
lines+=( " Bridge: ${var_bridge}" )
|
||||
lines+=( " Gateway: ${var_gateway}" )
|
||||
lines+=( " CIDR: /${var_cidr}" )
|
||||
lines+=( " CT storage: ${var_container_storage}" )
|
||||
lines+=( " Template: ${var_template_storage}" )
|
||||
lines+=( "" )
|
||||
|
||||
lines+=( "[Containers]" )
|
||||
local s
|
||||
for s in "${ORDERED_SLUGS[@]}"; do
|
||||
lines+=( "$(printf ' %-12s ctid=%-5s ip=%-16s url=http://%s:%s' \
|
||||
"$s" "${CTID_BY_SLUG[$s]}" "${IP_BY_SLUG[$s]}" "${IP_BY_SLUG[$s]}" "${PORT_BY_SLUG[$s]}")" )
|
||||
done
|
||||
lines+=( "" )
|
||||
|
||||
lines+=( "[Credentials & API keys]" )
|
||||
for s in "${ORDERED_SLUGS[@]}"; do
|
||||
case "${KIND_BY_SLUG[$s]}" in
|
||||
indexer|arr)
|
||||
if [[ -n "${APIKEY_BY_SLUG[$s]:-}" ]]; then
|
||||
lines+=( "$(printf ' %-12s apikey: %s' "$s" "${APIKEY_BY_SLUG[$s]}")" )
|
||||
else
|
||||
lines+=( "$(printf ' %-12s apikey: (not extracted)' "$s")" )
|
||||
fi
|
||||
;;
|
||||
client)
|
||||
if [[ "$s" == "qbittorrent" ]]; then
|
||||
lines+=( "$(printf ' %-12s user: %s' "$s" "${USER_BY_SLUG[qbittorrent]:-admin}")" )
|
||||
lines+=( "$(printf ' %-12s pass: %s (CHANGE THIS!)' "" "${PASS_BY_SLUG[qbittorrent]:-adminadmin}")" )
|
||||
elif [[ "$s" == "sabnzbd" ]]; then
|
||||
if [[ -n "${APIKEY_BY_SLUG[sabnzbd]:-}" ]]; then
|
||||
lines+=( "$(printf ' %-12s apikey: %s' "$s" "${APIKEY_BY_SLUG[sabnzbd]}")" )
|
||||
else
|
||||
lines+=( "$(printf ' %-12s apikey: (open web wizard at http://%s:7777 once)' "$s" "${IP_BY_SLUG[sabnzbd]}")" )
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
requests)
|
||||
lines+=( "$(printf ' %-12s (set during first-run web wizard)' "$s")" )
|
||||
;;
|
||||
esac
|
||||
done
|
||||
lines+=( "" )
|
||||
|
||||
lines+=( "[Wired automatically]" )
|
||||
if (( ${#WIRING_RESULTS[@]} == 0 )); then
|
||||
lines+=( " (nothing)" )
|
||||
else
|
||||
local w
|
||||
for w in "${WIRING_RESULTS[@]}"; do lines+=( " ${w}" ); done
|
||||
fi
|
||||
lines+=( "" )
|
||||
|
||||
lines+=( "[Wiring failures]" )
|
||||
if (( ${#WIRING_FAILURES[@]} == 0 )); then
|
||||
lines+=( " (none)" )
|
||||
else
|
||||
local f
|
||||
for f in "${WIRING_FAILURES[@]}"; do lines+=( " ${f}" ); done
|
||||
fi
|
||||
lines+=( "" )
|
||||
|
||||
lines+=( "[Manual steps still required]" )
|
||||
lines+=( " 1. Prowlarr: add indexers (none ship by default)." )
|
||||
lines+=( " 2. Sonarr/Radarr/Lidarr: set root folders and at least one quality profile." )
|
||||
if [[ " $SELECTED_CLIENTS " == *" qbittorrent "* ]]; then
|
||||
lines+=( " 3. qBittorrent: change admin password (default admin/adminadmin)." )
|
||||
fi
|
||||
if [[ " $SELECTED_ARRS " == *" seerr "* ]]; then
|
||||
lines+=( " 4. Seerr: open http://${IP_BY_SLUG[seerr]}:5055, complete the first-run wizard, then add:" )
|
||||
for s in $SELECTED_ARRS; do
|
||||
[[ "$s" == "seerr" ]] && continue
|
||||
[[ "$s" == "lidarr" ]] && continue
|
||||
lines+=( " ${NAME_BY_SLUG[$s]} at http://${IP_BY_SLUG[$s]}:${PORT_BY_SLUG[$s]} apikey: ${APIKEY_BY_SLUG[$s]:-<missing>}" )
|
||||
done
|
||||
fi
|
||||
lines+=( "" )
|
||||
lines+=( "Summary written to ${SUMMARY_FILE} (chmod 600)." )
|
||||
lines+=( "============================================================" )
|
||||
|
||||
local body
|
||||
body=$(printf '%s\n' "${lines[@]}")
|
||||
|
||||
echo
|
||||
echo "$body"
|
||||
|
||||
( umask 077; printf '%s\n' "$body" > "$SUMMARY_FILE" )
|
||||
chmod 600 "$SUMMARY_FILE" 2>/dev/null || true
|
||||
|
||||
msg_ok "Wrote ${SUMMARY_FILE}"
|
||||
}
|
||||
|
||||
main() {
|
||||
header_info
|
||||
check_root
|
||||
check_pve_tools
|
||||
ensure_dependencies curl whiptail jq iputils-ping
|
||||
seed_catalog
|
||||
pick_storage
|
||||
pick_network_defaults
|
||||
pick_apps
|
||||
pick_clients
|
||||
compute_ordered_slugs
|
||||
pick_ip_mode_and_ips
|
||||
pick_start_ctid
|
||||
confirm_summary
|
||||
install_loop
|
||||
wait_and_extract_keys
|
||||
wire_apis
|
||||
write_summary
|
||||
msg_ok "arr-stack provisioning finished."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user