Files
pijarr/setup.sh
2022-01-26 14:35:47 +10:00

408 lines
14 KiB
Bash

#!/usr/bin/env bash
set -o errexit
set -o pipefail
# Set valid applications which can be used with this script
# Tested applications are jackett lidarr radarr sonarr
readonly VALID_APPS=(jackett lidarr radarr sonarr)
# Fetch latest jackett ARM32 release from https://github.com/Jackett/Jackett/releases
jackett_releases=`curl -s https://github.com/Jackett/Jackett/releases | awk -F"[><]" '{for(i=1;i<=NF;i++){if($i ~ /a href=.*\//){print "<" $i ">"}}}' | grep ARM32.tar.gz -A 0`
jackett_latest=`echo "${jackett_releases}" | awk 'NR==1' | sed -r 's/.*href="([^"]+).*/\1/g'`
jackett_src_url="https://github.com${jackett_latest}"
# Fetch latest radarr, lidarr and sonarr builds. Links below select latest release.
radarr_src_url='https://radarr.servarr.com/v1/update/master/updatefile?os=linux&runtime=netcore&arch=arm'
lidarr_src_url='https://lidarr.servarr.com/v1/update/master/updatefile?os=linux&runtime=netcore&arch=arm'
sonarr_src_url='https://services.sonarr.tv/v1/download/main/latest?version=3&os=linux'
# Function to output PiJARR ascii and details of script.
script_info() {
cat <<EOF
${RED}▓▓▓▓▓▓▓▓▓▓▓▓▓ ${GREEN}▓▓▓▓${RESET} ▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓
${RED}▓▓▓▓ ▓▓▓▓ ${RESET} ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓
${RED}▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓${RESET} ▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓
${RED}▓▓▓▓ ▓▓▓▓${RESET} ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓
${RED}▓▓▓▓ ▓▓▓▓${RESET} ▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓ ▓▓▓▓▓
Name: pijarr-setup.sh
Description: Raspberry Pi installer for Jackett, Sonarr, Radarr, and Lidarr
Author: github.com/piscripts
Tested: Raspberry Pi 3 & 4 running Raspbian Buster
Usage: sudo bash pijarr-setup.sh
Notes: Requiries sudo/root superuser permissions to run.
If they are not already installed the required dependencies including
Mono (Cross platform, open source .NET framework) may take a while to
download and install.
After initial setup the web interface for the application may need a some time
to start before being available on the nominated host port.
EOF
}
# Temporary directory for application sources
temp_dir="/tmp/pijarr"
# Function to set terminal colors if supported.
term_colors() {
if [[ -t 1 ]]; then
RED=$(printf '\033[31m')
GREEN=$(printf '\033[32m')
YELLOW=$(printf '\033[33m')
BLUE=$(printf '\033[34m')
MAGENTA=$(printf '\033[35m')
CYAN=$(printf '\033[36m')
BOLD=$(printf '\033[1m')
RESET=$(printf '\033[0m')
else
RED=""
GREEN=""
YELLOW=""
BLUE=""
MAGENTA=""
CYAN=""
BOLD=""
RESET=""
fi
}
# Function to output colored or bold terminal messages.
# Usage examples: term_message "This is a default color and style message"
# term_message nb "This is a default color bold message"
# term_message rb "This is a red bold message"
term_message() {
local set_color=""
local set_style=""
[[ -z "${2}" ]] && echo -ne "${1}" >&2 && return
[[ ${1:0:1} == "d" ]] && set_color=${RESET}
[[ ${1:0:1} == "r" ]] && set_color=${RED}
[[ ${1:0:1} == "g" ]] && set_color=${GREEN}
[[ ${1:0:1} == "y" ]] && set_color=${YELLOW}
[[ ${1:0:1} == "b" ]] && set_color=${BLUE}
[[ ${1:0:1} == "m" ]] && set_color=${MAGENTA}
[[ ${1:0:1} == "c" ]] && set_color=${CYAN}
[[ ${1:1:2} == "b" ]] && set_style=${BOLD}
echo -e "${set_color}${set_style}${2}${RESET}" >&2 && return
}
# Displays a box containing a dash and message
task_start() {
echo -ne "[-] ${1}"
}
# Displays a box containing a green tick and optional message if required.
task_done() {
echo -e "\r[\033[0;32m\xE2\x9C\x94\033[0m] ${1}"
}
# Displays a box containing a red cross and optional message if required.
task_fail() {
echo -e "\r[\033[0;31m\xe2\x9c\x98\033[0m] ${1}"
}
# Function to pause script and check if the user wishes to continue.
check_continue() {
local response
while true; do
read -r -p "Do you wish to continue (y/N)? " response
case "${response}" in
[yY][eE][sS] | [yY])
echo
break
;;
*)
echo
exit
;;
esac
done
}
# Function to check if superuser or running using sudo
check_superuser() {
if [[ $(id -u) -ne 0 ]] >/dev/null 2>&1; then
term_message rb "Script must be run by superuser or using sudo command\n"
exit 1
fi
}
pkg_update() {
term_message cb "Updating packages using apt update..."
apt update -y
}
pkg_upgrade() {
term_message cb "Upgrading packages using apt upgrade..."
apt upgrade -y
}
pkg_cleanup() {
term_message cb "Running package clean-up using apt autoclean and autoremove..."
apt autoclean -y
apt autoremove -y
}
# Function to check if packages are installed and install them if they are not found.
pkg_install() {
for pkg in "${@}"; do
task_start "Checking for required package > ${pkg}"
if [[ $(dpkg -s "${pkg}") == *"Status: install ok installed"* ]] &>/dev/null; then
task_done
else
task_fail "Package ${pkg} not found.$(tput el)"
term_message c "Attempting to install ${pkg} package with apt..."
apt install -y "${pkg}"
if [[ $(dpkg -s "${pkg}") == *"Status: install ok installed"* ]] &>/dev/null; then
term_message g "Package ${pkg} is now installed."
else
term_message rb "Unable to install package ${pkg}"
fi
fi
done
}
# Function to check if a service is active will return green tick or red cross.
is_active() {
if [[ $(systemctl is-active "$1") == "active" ]] &>/dev/null; then
task_done
else
task_fail
fi
}
# Function to remove the temporary directory and files.
remove_temp() {
task_start "Removing temporary files from ${temp_dir}..."
rm -Rf ${temp_dir} &>/dev/null || true
task_done
}
# Function to install all the dependencies including packages and server keys.
setup_dependencies() {
term_message db "Setup Dependencies"
term_message c "Installing required dependencies..."
pkg_install apt-transport-https dirmngr gnupg ca-certificates
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF >/dev/null 2>&1
echo "deb https://download.mono-project.com/repo/debian stable-raspbianbuster main" | tee /etc/apt/sources.list.d/mono-official-stable.list >/dev/null 2>&1
term_message c "Updating packages to include newly added sources..."
pkg_update
term_message c "Installing mono, sqlite3 and supporting libraries..."
pkg_install mono-devel mediainfo sqlite3 libmono-cil-dev libchromaprint-tools
}
# Primary function to install and configure the applications.
# Function can take one or more apps ie. setup_app jackett sonarr lidarr radarr
setup_app() {
for app in "${@}"; do
local date_stamp="$(date '+%Y-%m-%d %H%M')"
local app_name="${app,,}"
local app_user="${app_name}"
local app_group="${app_user}"
local src_url=${app_name}\_src_url
local src_url=${!src_url}
local src_file="${src_url##*/}"
local new_file="${app_name}.tar.gz"
if [[ ! "${VALID_APPS[*]}" =~ ${app} ]]; then
term_message rb "${app} is not a valid application which can be installed.\n"
exit
fi
task_start "Creating temporary directory for sources ${temp_dir}"
mkdir -p "${temp_dir}" &>/dev/null || true
task_done
term_message cb "\nCommencing install for ${app_name}"
task_start "Adding user for daemon service username: ${app_user}"
useradd -s /usr/sbin/nologin -d /var/lib/"${app_user}" -r -m -U "${app_user}" &>/dev/null || true
task_done
term_message c "Fetching ${app_name} source file..."
wget -O "${temp_dir}"/"${new_file}" -q --show-progress --progress=bar:force "${src_url}" 2>&1 &&
task_done "Source file downloaded.$(tput el)"
term_message c "Extracting ${new_file} to /opt/..."
tar -xf "${temp_dir}"/"${new_file}" -C /opt/
task_start "Set user permissions on /opt/${app_name^}"
chown -R "${app_user}":"${app_group}" /opt/"${app_name^}"/ && task_done
# Just in case some apps have permission problems with their /var/lib config working directories.
task_start "Creating other /var/lib config directories and setting permissions"
mkdir -p /var/lib/"${app_name}"/.config/"${app_name^}"
chown -R "${app_user}":"${app_group}" /var/lib/"${app_name}"/.config/"${app_name^}"/ && task_done
# Begin writting out the service configuration file. Minor change needed for Jackett.
task_start "Configuring system service"
if [[ "${app_name}" == "jackett" ]]; then
app_exec="ExecStart=/opt/${app_name^}/${app_name}_launcher.sh"
elif [[ "${app_name}" == "sonarr" ]]; then
app_exec="ExecStart=/usr/bin/mono --debug /opt/${app_name^}/${app_name^}.exe -nobrowser"
else
app_exec="ExecStart=/opt/${app_name^}/${app_name^}"
fi
tee /etc/systemd/system/"${app_name}".service &>/dev/null <<EOF
# Generated by PiJARR ${date_stamp}
[Unit]
Description=${app_name^} Daemon
After=syslog.target network.target
[Service]
SyslogIdentifier=${app_name}
Restart=always
RestartSec=5
Type=simple
User=${app_user}
Group=${app_group}
WorkingDirectory=/opt/${app_name^}
${app_exec}
TimeoutStopSec=20
[Install]
WantedBy=multi-user.target
EOF
task_done
task_start "Reloading daemon and enabling ${app_name} service"
systemctl daemon-reload &>/dev/null
systemctl enable "${app_name}" &>/dev/null
systemctl start "${app_name}" &>/dev/null
task_done
task_start "Check if service is active"
is_active "${app_name}"
done
}
check_status() {
term_message cb "\nChecking service status..."
local hostip=$(hostname -I | awk '{print $1}')
task_start "Jackett: http://${hostip}:9117" && is_active jackett
task_start "Sonarr: http://${hostip}:8989" && is_active sonarr
task_start "Lidarr: http://${hostip}:8686" && is_active lidarr
task_start "Radarr: http://${hostip}:7878" && is_active radarr
}
# Function to assist in removing the applications and their configuration files
# Function can take one or more apps ie. remove_app jackett sonarr lidarr radarr
remove_app() {
for app in "${@}"; do
local app="${app,,}"
if [[ ! "${VALID_APPS[*]}" =~ ${app} ]]; then
term_message rb "${app} is not a valid which can be deleted.\n"
exit
fi
term_message rb "You are about to delete all settings and files for ${app}..."
check_continue
task_start "Deleting any existing settings or files for ${app}..."
systemctl stop "${app}" &>/dev/null || true
deluser "${app}" &>/dev/null || true
rm -Rf /opt/"${app^}" &>/dev/null || true
rm -Rf /var/lib/"${app}" &>/dev/null || true
rm /etc/systemd/system/"${app}.service"* &>/dev/null || true
systemctl daemon-reload &>/dev/null || true
task_done "\n"
done
}
# Pause and wait for any key to be pressed
any_key() {
echo ""
read -n 1 -s -r -p "Press any key to continue."
}
# Display a list of menu items for selection
display_menu () {
echo "====================="
echo " PiJARR Menu Options "
echo "====================="
echo
echo -e "1. Install ALL applications jackett sonarr lidarr and radarr"
echo -e "2. Install jackett only"
echo -e "3. Install sonarr only"
echo -e "4. Install lidarr only"
echo -e "5. Install radarr only"
echo -e "\n6. Remove ALL applications jackett sonarr lidarr and radarr"
echo -e "7. Remove jackett only"
echo -e "8. Remove sonarr only"
echo -e "9. Remove lidarr only"
echo -e "10. Remove radarr only"
echo -e "\n11. Check application service status"
echo -e "\n12. Exit"
echo
echo -n " Enter option [1-12]: "
while :
do
read choice
case ${choice} in
1) echo -e "\nInstalling ALL applications jackett sonarr lidarr and radarr..."
setup_app jackett sonarr lidarr radarr
remove_temp
check_status
;;
2) echo -e "\nInstalling jackett..."
setup_app jackett
remove_temp
check_status
;;
3) echo -e "\nInstalling sonarr..."
setup_app sonarr
remove_temp
check_status
;;
4) echo -e "\nInstalling lidarr..."
setup_app lidarr
remove_temp
check_status
;;
5) echo -e "\nInstalling radarr..."
setup_app radarr
remove_temp
check_status
;;
6) echo -e "\nRemoving ALL applications jackett sonarr lidarr and radarr..."
remove_app jackett sonarr lidarr radarr
check_status
;;
7) echo -e "\nRemoving jackett..."
remove_app jackett
check_status
;;
8) echo -e "\nRemoving sonarr..."
remove_app sonarr
check_status
;;
9) echo -e "\nRemoving lidarr..."
remove_app lidarr
check_status
;;
10) echo -e "\nRemoving radarr..."
remove_app radarr
check_status
;;
11) check_status
;;
12) echo -e "Exiting...\n"
exit;;
*) clear
display_menu;;
esac
echo -e "\nSelection [${choice}] completed."
any_key
clear
display_menu
done
}
# One function to rule them all.
main() {
clear
term_colors
script_info
check_superuser
check_continue
pkg_update
pkg_upgrade
pkg_cleanup
setup_dependencies
clear
display_menu
}
main "${@}"