diff --git a/install/huntarr-install.sh b/install/huntarr-install.sh new file mode 100644 index 0000000..753929e --- /dev/null +++ b/install/huntarr-install.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/huntarr/huntarr + +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 curl wget gnupg git openjdk-17-jdk nodejs npm +msg_ok "Installed Dependencies" + +msg_info "Installing Huntarr" +mkdir -p /var/lib/huntarr/ +chmod 775 /var/lib/huntarr/ +$STD git clone https://github.com/huntarr/huntarr.git /opt/huntarr +cd /opt/huntarr +HUNTARR_VERSION="${HUNTARR_VERSION:-6.2}" +$STD git checkout "v${HUNTARR_VERSION}" +$STD npm install +$STD npm run build +msg_ok "Installed Huntarr" + +msg_info "Creating Service" +cat </etc/systemd/system/huntarr.service +[Unit] +Description=Huntarr Service +After=network.target + +[Service] +UMask=0002 +Type=simple +WorkingDirectory=/opt/huntarr +ExecStart=/usr/bin/npm start +Restart=on-failure +TimeoutStopSec=20 +KillMode=process + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now huntarr +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" \ No newline at end of file diff --git a/lxc/huntarr.sh b/lxc/huntarr.sh new file mode 100644 index 0000000..7d1f347 --- /dev/null +++ b/lxc/huntarr.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/huntarr/huntarr + +APP="Huntarr" +var_tags="${var_tags:-arr}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +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/huntarr/ ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Updating $APP LXC" + temp_file="$(mktemp)" + rm -rf /opt/huntarr + + # If Huntarr publishes releases with version tags: + RELEASE=$(curl -fsSL https://api.github.com/repos/huntarr/huntarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + curl -fsSL "https://github.com/huntarr/huntarr/archive/refs/tags/v${RELEASE}.tar.gz" -o "$temp_file" + $STD tar -xvzf "$temp_file" + $STD mv huntarr-${RELEASE} /opt/huntarr + + # Build process still needed for source-based installation + cd /opt/huntarr + $STD npm install + $STD npm run build + $STD chown -R huntarr:huntarr /opt/huntarr + $STD chmod 775 /opt/huntarr + + $STD systemctl restart huntarr + msg_ok "Updated $APP to version ${RELEASE}" + + msg_info "Cleaning up" + rm -rf "$temp_file" + msg_ok "Cleaned up" + exit +} + +start +build_container +description + +msg_info "Installing Dependencies" +$STD apt-get update +$STD apt-get -y install curl wget gnupg git openjdk-17-jdk nodejs npm +msg_ok "Installed Dependencies" + +msg_info "Creating Huntarr user" +$STD useradd -r -d /var/lib/huntarr -s /bin/bash huntarr +msg_ok "Created Huntarr user" + +msg_info "Setting up directories" +$STD mkdir -p /opt/huntarr +$STD mkdir -p /var/lib/huntarr +$STD chown -R huntarr:huntarr /var/lib/huntarr +msg_ok "Set up directories" + +msg_info "Installing $APP" +$STD git clone https://github.com/huntarr/huntarr.git /opt/huntarr +cd /opt/huntarr +$STD git checkout "v${var_huntarr_version}" +$STD npm install +$STD npm run build +$STD chown -R huntarr:huntarr /opt/huntarr +msg_ok "Installed $APP" + +msg_info "Creating Service" +cat < /etc/systemd/system/huntarr.service +[Unit] +Description=Huntarr Service +After=network.target + +[Service] +WorkingDirectory=/opt/huntarr +ExecStart=/usr/bin/npm start +Restart=on-failure +User=huntarr +Group=huntarr +Environment=HOME=/var/lib/huntarr + +[Install] +WantedBy=multi-user.target +EOF + +$STD systemctl daemon-reload +$STD systemctl enable huntarr +$STD systemctl start huntarr +msg_ok "Created Service" + +# Determine the port Huntarr is using (assuming default 3000) +HUNTARR_PORT=3000 + +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}:${HUNTARR_PORT}${CL}" \ No newline at end of file diff --git a/misc/install.func b/misc/install.func new file mode 100644 index 0000000..d38ec1e --- /dev/null +++ b/misc/install.func @@ -0,0 +1,278 @@ +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# Co-Author: MickLesk +# License: MIT +# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +# This function sets color variables for formatting output in the terminal +color() { + # Colors + YW=$(echo "\033[33m") + YWB=$(echo "\033[93m") + BL=$(echo "\033[36m") + RD=$(echo "\033[01;31m") + GN=$(echo "\033[1;92m") + + # Formatting + CL=$(echo "\033[m") + BFR="\\r\\033[K" + BOLD=$(echo "\033[1m") + HOLD=" " + TAB=" " + + # System + RETRY_NUM=10 + RETRY_EVERY=3 + + # Icons + CM="${TAB}✔️${TAB}${CL}" + CROSS="${TAB}✖️${TAB}${CL}" + INFO="${TAB}💡${TAB}${CL}" + NETWORK="${TAB}📡${TAB}${CL}" + OS="${TAB}🖥️${TAB}${CL}" + OSVERSION="${TAB}🌟${TAB}${CL}" + HOSTNAME="${TAB}🏠${TAB}${CL}" + GATEWAY="${TAB}🌐${TAB}${CL}" + DEFAULT="${TAB}⚙️${TAB}${CL}" +} + +# Function to set STD mode based on verbosity +set_std_mode() { + if [ "$VERBOSE" = "yes" ]; then + STD="" + else + STD="silent" + fi +} + +# Silent execution function +silent() { + "$@" >/dev/null 2>&1 +} + +# This function enables IPv6 if it's not disabled and sets verbose mode +verb_ip6() { + set_std_mode # Set STD mode based on VERBOSE + + if [ "$DISABLEIPV6" == "yes" ]; then + echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf + $STD sysctl -p + fi +} + +# This function sets error handling options and defines the error_handler function to handle errors +catch_errors() { + set -Eeuo pipefail + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +} + +# This function handles errors +error_handler() { + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + printf "\e[?25h" + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + echo -e "\n$error_message" + if [[ "$line_number" -eq 50 ]]; then + echo -e "The silent function has suppressed the error, run the script with verbose mode enabled, which will provide more detailed output.\n" + post_update_to_api "failed" "No error message, script ran in silent mode" + else + post_update_to_api "failed" "${command}" + fi +} + +# This function displays a spinner. +spinner() { + local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') + local spin_i=0 + local interval=0.1 + printf "\e[?25l" + + local color="${YWB}" + + while true; do + printf "\r ${color}%s${CL}" "${frames[spin_i]}" + spin_i=$(((spin_i + 1) % ${#frames[@]})) + sleep "$interval" + done +} + +# This function displays an informational message with a yellow color. +msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" + spinner & + SPINNER_PID=$! +} + +# This function displays a success message with a green color. +msg_ok() { + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + printf "\e[?25h" + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +# This function displays a error message with a red color. +msg_error() { + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + printf "\e[?25h" + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection +setting_up_container() { + msg_info "Setting up Container OS" + sed -i "/$LANG/ s/\(^# \)//" /etc/locale.gen + locale_line=$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print $1}' | head -n 1) + echo "LANG=${locale_line}" >/etc/default/locale + locale-gen >/dev/null + export LANG=${locale_line} + echo "$tz" >/etc/timezone + ln -sf /usr/share/zoneinfo/"$tz" /etc/localtime + for ((i = RETRY_NUM; i > 0; i--)); do + if [ "$(hostname -I)" != "" ]; then + break + fi + echo 1>&2 -en "${CROSS}${RD} No Network! " + sleep "$RETRY_EVERY" + done + if [ "$(hostname -I)" = "" ]; then + echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + systemctl disable -q --now systemd-networkd-wait-online.service + msg_ok "Set up Container OS" + msg_ok "Network Connected: ${BL}$(hostname -I)" +} + +# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected +network_check() { + set +e + trap - ERR + ipv4_connected=false + ipv6_connected=false + sleep 1 + # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. + if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then + msg_ok "IPv4 Internet Connected" + ipv4_connected=true + else + msg_error "IPv4 Internet Not Connected" + fi + + # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. + if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then + msg_ok "IPv6 Internet Connected" + ipv6_connected=true + else + msg_error "IPv6 Internet Not Connected" + fi + + # If both IPv4 and IPv6 checks fail, prompt the user + if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then + read -r -p "No Internet detected,would you like to continue anyway? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" + else + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi + fi + + RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') + if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi + set -e + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +} + +# This function updates the Container OS by running apt-get update and upgrade +update_os() { + msg_info "Updating Container OS" + if [[ "$CACHER" == "yes" ]]; then + echo "Acquire::http::Proxy-Auto-Detect \"/usr/local/bin/apt-proxy-detect.sh\";" >/etc/apt/apt.conf.d/00aptproxy + cat </usr/local/bin/apt-proxy-detect.sh +#!/bin/bash +if nc -w1 -z "${CACHER_IP}" 3142; then + echo -n "http://${CACHER_IP}:3142" +else + echo -n "DIRECT" +fi +EOF + chmod +x /usr/local/bin/apt-proxy-detect.sh + fi + $STD apt-get update + $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + msg_ok "Updated Container OS" + + msg_info "Installing core dependencies" + $STD apt-get update + $STD apt-get install -y sudo curl mc gnupg2 + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) + msg_ok "Core dependencies installed" +} + +# This function modifies the message of the day (motd) and SSH settings +motd_ssh() { + # Set terminal to 256-color mode + grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc + + # Get OS information (Debian / Ubuntu) + if [ -f "/etc/os-release" ]; then + OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') + OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') + elif [ -f "/etc/debian_version" ]; then + OS_NAME="Debian" + OS_VERSION=$(cat /etc/debian_version) + fi + + PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" + echo "echo -e \"\"" >"$PROFILE_FILE" + echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE" + echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/ProxmoxVE${CL}\"" >>"$PROFILE_FILE" + echo "echo \"\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${TAB}${OS}${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" + + # Disable default MOTD scripts + chmod -x /etc/update-motd.d/* + + if [[ "${SSH_ROOT}" == "yes" ]]; then + sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config + systemctl restart sshd + fi +} + +# This function customizes the container by modifying the getty service and enabling auto-login for the root user +customize() { + if [[ "$PASSWORD" == "" ]]; then + msg_info "Customizing Container" + GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" + mkdir -p $(dirname $GETTY_OVERRIDE) + cat <$GETTY_OVERRIDE + [Service] + ExecStart= + ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM +EOF + systemctl daemon-reload + systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') + msg_ok "Customized Container" + fi + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update + chmod +x /usr/bin/update + + if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then + mkdir -p /root/.ssh + echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys + chmod 700 /root/.ssh + chmod 600 /root/.ssh/authorized_keys + fi +} diff --git a/radarr-install.sh b/radarr-install.sh new file mode 100644 index 0000000..816d613 --- /dev/null +++ b/radarr-install.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://radarr.video/ + +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 sqlite3 +msg_ok "Installed Dependencies" + +msg_info "Installing Radarr" +temp_file="$(mktemp)" +mkdir -p /var/lib/radarr/ +chmod 775 /var/lib/radarr/ +cd /var/lib/radarr/ +RELEASE=$(curl -fsSL https://api.github.com/repos/Radarr/Radarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') +curl -fsSL "https://github.com/Radarr/Radarr/releases/download/v${RELEASE}/Radarr.master.${RELEASE}.linux-core-x64.tar.gz" -o "$temp_file" +$STD tar -xvzf "$temp_file" +mv Radarr /opt +chmod 775 /opt/Radarr +msg_ok "Installed Radarr" + +msg_info "Creating Service" +cat </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 + +msg_info "Cleaning up" +rm -rf "$temp_file" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..01c7cae --- /dev/null +++ b/test.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://radarr.video/ + +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:-12}" +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 + + msg_info "Updating $APP LXC" + temp_file="$(mktemp)" + rm -rf /opt/Radarr + RELEASE=$(curl -fsSL https://api.github.com/repos/Radarr/Radarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + curl -fsSL "https://github.com/Radarr/Radarr/releases/download/v${RELEASE}/Radarr.master.${RELEASE}.linux-core-x64.tar.gz" -o "$temp_file" + $STD tar -xvzf "$temp_file" + mv Radarr /opt + chmod 775 /opt/Radarr + msg_ok "Updated $APP LXC" + + msg_info "Cleaning up" + rm -rf "$temp_file" + msg_ok "Cleaned up" + 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}"