V3 - The big one #71

Merged
rogs merged 31 commits from v3 into master 2024-12-30 11:01:29 -03:00
6 changed files with 1052 additions and 749 deletions

Binary file not shown.

View File

@ -1,4 +1,2 @@
version: "3"
# services: # services:
# Add your custom services here! # Add your custom services here!

View File

@ -1,5 +1,3 @@
version: "3"
services: services:
# <media_service> is used to serve your media to the client devices # <media_service> is used to serve your media to the client devices
<media_service>: <media_service>:
@ -11,8 +9,8 @@ services:
- PGID=${PGID} - PGID=${PGID}
- VERSION=docker - VERSION=docker
volumes: volumes:
- ${MEDIA_DIRECTORY}/movies:/data/movies - /etc/localtime:/etc/localtime:ro
- ${MEDIA_DIRECTORY}/tvshows:/data/tvshows - ${MEDIA_DIRECTORY}:/data
- ${INSTALL_DIRECTORY}/config/${MEDIA_SERVICE}:/config - ${INSTALL_DIRECTORY}/config/${MEDIA_SERVICE}:/config
ports: # plex ports: # plex
- 8096:8096 # plex - 8096:8096 # plex
@ -20,18 +18,36 @@ services:
# qBitorrent is used to download torrents # qBitorrent is used to download torrents
qbittorrent: qbittorrent:
image: lscr.io/linuxserver/qbittorrent:4.6.0 image: lscr.io/linuxserver/qbittorrent
container_name: qbittorrent container_name: qbittorrent
environment: environment:
- PUID=${PUID} - PUID=${PUID}
- PGID=${PGID} - PGID=${PGID}
- WEBUI_PORT=8080 - WEBUI_PORT=8081
volumes: volumes:
- ${MEDIA_DIRECTORY}/downloads:/downloads - /etc/localtime:/etc/localtime:ro
- ${MEDIA_DIRECTORY}:/data
- ${INSTALL_DIRECTORY}/config/qbittorrent:/config - ${INSTALL_DIRECTORY}/config/qbittorrent:/config
restart: unless-stopped restart: unless-stopped
ports: # qbittorrent ports: # qbittorrent
- 8080:8080 # qbittorrent - 8081:8081 # qbittorrent
#network_mode: "service:gluetun"
# SABnzbd is used to download from usenet
sabnzbd:
image: lscr.io/linuxserver/sabnzbd:latest
container_name: sabnzbd
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=America/Montevideo
volumes:
- /etc/localtime:/etc/localtime:ro
- ${MEDIA_DIRECTORY}:/data
- ${INSTALL_DIRECTORY}/config/sabnzbd:/config
ports: # sabnzbd
- 8080:8080 # sabnzbd
restart: unless-stopped
#network_mode: "service:gluetun" #network_mode: "service:gluetun"
# Sonarr is used to query, add downloads to the download queue and index TV shows # Sonarr is used to query, add downloads to the download queue and index TV shows
@ -43,8 +59,8 @@ services:
- PUID=${PUID} - PUID=${PUID}
- PGID=${PGID} - PGID=${PGID}
volumes: volumes:
- ${MEDIA_DIRECTORY}/tvshows:/tv - /etc/localtime:/etc/localtime:ro
- ${MEDIA_DIRECTORY}/downloads:/downloads - ${MEDIA_DIRECTORY}:/data
- ${INSTALL_DIRECTORY}/config/sonarr:/config - ${INSTALL_DIRECTORY}/config/sonarr:/config
ports: ports:
- 8989:8989 - 8989:8989
@ -59,8 +75,8 @@ services:
- PUID=${PUID} - PUID=${PUID}
- PGID=${PGID} - PGID=${PGID}
volumes: volumes:
- ${MEDIA_DIRECTORY}/movies:/movies - /etc/localtime:/etc/localtime:ro
- ${MEDIA_DIRECTORY}/downloads:/downloads - ${MEDIA_DIRECTORY}:/data
- ${INSTALL_DIRECTORY}/config/radarr:/config - ${INSTALL_DIRECTORY}/config/radarr:/config
ports: ports:
- 7878:7878 - 7878:7878
@ -75,8 +91,8 @@ services:
- PUID=${PUID} - PUID=${PUID}
- PGID=${PGID} - PGID=${PGID}
volumes: volumes:
- ${MEDIA_DIRECTORY}/music:/music - /etc/localtime:/etc/localtime:ro
- ${MEDIA_DIRECTORY}/downloads:/downloads - ${MEDIA_DIRECTORY}:/data
- ${INSTALL_DIRECTORY}/config/lidarr:/config - ${INSTALL_DIRECTORY}/config/lidarr:/config
ports: ports:
- 8686:8686 - 8686:8686
@ -91,8 +107,8 @@ services:
- PUID=${PUID} - PUID=${PUID}
- PGID=${PGID} - PGID=${PGID}
volumes: volumes:
- ${MEDIA_DIRECTORY}/books:/books - /etc/localtime:/etc/localtime:ro
- ${MEDIA_DIRECTORY}/downloads:/downloads - ${MEDIA_DIRECTORY}:/data
- ${INSTALL_DIRECTORY}/config/readarr:/config - ${INSTALL_DIRECTORY}/config/readarr:/config
ports: ports:
- 8787:8787 - 8787:8787
@ -107,8 +123,8 @@ services:
- PUID=${PUID} - PUID=${PUID}
- PGID=${PGID} - PGID=${PGID}
volumes: volumes:
- ${MEDIA_DIRECTORY}/movies:/movies - /etc/localtime:/etc/localtime:ro
- ${MEDIA_DIRECTORY}/tvshows:/tv - ${MEDIA_DIRECTORY}:/data
- ${INSTALL_DIRECTORY}/config/bazarr:/config - ${INSTALL_DIRECTORY}/config/bazarr:/config
ports: ports:
- 6767:6767 - 6767:6767
@ -123,6 +139,7 @@ services:
- PUID=${PUID} - PUID=${PUID}
- PGID=${PGID} - PGID=${PGID}
volumes: volumes:
- /etc/localtime:/etc/localtime:ro
- ${INSTALL_DIRECTORY}/config/prowlarr:/config - ${INSTALL_DIRECTORY}/config/prowlarr:/config
ports: ports:
- 9696:9696 - 9696:9696
@ -140,15 +157,17 @@ services:
- 8888:8888/tcp # HTTP proxy - 8888:8888/tcp # HTTP proxy
- 8388:8388/tcp # Shadowsocks - 8388:8388/tcp # Shadowsocks
- 8388:8388/udp # Shadowsocks - 8388:8388/udp # Shadowsocks
- 8003:8000/tcp # Admin
#- 8080:8080/tcp # gluetun #- 8080:8080/tcp # gluetun
volumes: #- 8081:8081/tcp # gluetun
- ${INSTALL_DIRECTORY}/config/gluetun:/config
environment: environment:
- VPN_SERVICE_PROVIDER=${VPN_SERVICE} - VPN_SERVICE_PROVIDER=${VPN_SERVICE}
- VPN_TYPE=openvpn - VPN_TYPE=openvpn
- OPENVPN_USER=${VPN_USER} - OPENVPN_USER=${VPN_USER}
- OPENVPN_PASSWORD=${VPN_PASSWORD} - OPENVPN_PASSWORD=${VPN_PASSWORD}
- OPENVPN_CIPHERS=AES-256-GCM - OPENVPN_CIPHERS=AES-256-GCM
- PORT_FORWARD_ONLY=on
- VPN_PORT_FORWARDING=on
restart: unless-stopped restart: unless-stopped
# Portainer helps debugging and monitors the containers # Portainer helps debugging and monitors the containers

843
docs.org
View File

@ -6,37 +6,24 @@
:PROPERTIES: :PROPERTIES:
:ID: faf95c8a-9133-4072-8544-0ef456a67611 :ID: faf95c8a-9133-4072-8544-0ef456a67611
:END: :END:
- [[#welcome-message][Welcome message]] - [[#welcome-message][Welcome message]]
- [[#constants-and-configuration][Constants and Configuration]]
- [[#functions][Functions]] - [[#functions][Functions]]
- [[#message-formatting][Message formatting]] - [[#message-formatting][Message formatting]]
- [[#check-the-dependencies][Check the dependencies]] - [[#directory-handling][Directory Handling]]
- [[#running-services-location][Running services location]] - [[#check-dependencies][Check Dependencies]]
- [[#verify-all-the-dependencies][Verify all the dependencies]] - [[#service-configuration][Service Configuration]]
- [[#gather-all-the-required-information][Gather all the required information]] - [[#file-operations][File Operations]]
- [[#checking-install-location][Checking install location]] - [[#script-execution][Script Execution]]
- [[#setting-the-correct-user][Setting the correct user]] - [[#verify-prerequisites][Verify Prerequisites]]
- [[#media-directory][Media directory]] - [[#installation-process][Installation Process]]
- [[#setting-perferred-media-service][Setting perferred media service]] - [[#display-closing-message][Display Closing Message]]
- [[#setting-the-vpn][Setting the VPN]]
- [[#installing-yams][Installing YAMS]]
- [[#copy-the-docker-compose-file-to-the-install-location][Copy the docker-compose file to the install location]]
- [[#set-puid-pgid-media-folder-media-service-config-folder-and-vpn-on-the-yams-scripts][Set PUID, PGID, Media Folder, Media Service, Config folder and VPN on the YAMS scripts]]
- [[#set-the-configuration-for-the-yams-binary][Set the configuration for the YAMS binary]]
- [[#success-message][Success message!]]
- [[#final-steps][Final steps]]
- [[#install-the-yams-cli][Install the YAMS CLI]]
- [[#set-the-correct-permissions-to-the-media-and-install-directories][Set the correct permissions to the media and install directories]]
- [[#create-the-config-directory][Create the config directory]]
- [[#display-closing-message][Display closing message]]
* Welcome message * Welcome message
:PROPERTIES: :PROPERTIES:
:ID: 525c03eb-cab9-44f8-8cc5-e5ec9035a938 :ID: 525c03eb-cab9-44f8-8cc5-e5ec9035a938
:END: :END:
This is just a welcome message for the script
#+begin_src bash #+begin_src bash
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
@ -64,268 +51,238 @@ echo "===================================================="
echo "" echo ""
#+end_src #+end_src
* Constants and Configuration
:PROPERTIES:
:ID: new-constants-section
:END:
#+begin_src bash
# Constants
readonly DEFAULT_INSTALL_DIR="/opt/yams"
readonly DEFAULT_MEDIA_DIR="/srv/media"
readonly SUPPORTED_MEDIA_SERVICES=("jellyfin" "emby" "plex")
readonly DEFAULT_MEDIA_SERVICE="jellyfin"
readonly DEFAULT_VPN_SERVICE="protonvpn"
readonly MEDIA_SUBDIRS=("tvshows" "movies" "music" "books" "downloads" "blackhole")
# Color codes
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # No Color
# Dependencies
readonly REQUIRED_COMMANDS=("curl" "sed" "awk")
#+end_src
* Functions * Functions
:PROPERTIES: :PROPERTIES:
:ID: 111a7df4-08f5-4e6c-a799-dd822c5d030e :ID: 111a7df4-08f5-4e6c-a799-dd822c5d030e
:END: :END:
To make development easier, we declare some functions that are going to be used a lot later
** Message formatting ** Message formatting
:PROPERTIES: :PROPERTIES:
:ID: 61387bd4-2ecf-44fe-ac69-dc6347c0d1b8 :ID: 61387bd4-2ecf-44fe-ac69-dc6347c0d1b8
:END: :END:
Both of these functions format the message in different colors, depending on what the message means
*** Success
:PROPERTIES:
:ID: ec8f113c-43f9-4585-a1b5-8c7ec4e84bb2
:END:
#+begin_src bash #+begin_src bash
send_success_message() { log_success() {
echo -e $(printf "\e[32m$1\e[0m") echo -e "${GREEN}$1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}" >&2
exit 1
}
log_warning() {
echo -e "${YELLOW}$1${NC}"
}
log_info() {
echo "$1"
} }
#+end_src #+end_src
*** Error ** Directory Handling
:PROPERTIES: :PROPERTIES:
:ID: 1a6cd951-c9ce-46fc-8953-f5e206f7cd23 :ID: new-directory-section
:END: :END:
Error is basically the same as before, but it ~exit 255~ to finish the execution.
#+begin_src bash #+begin_src bash
send_error_message() { create_and_verify_directory() {
echo -e $(printf "\e[31m$1\e[0m") local dir="$1"
exit 255 local dir_type="$2"
if [ ! -d "$dir" ]; then
echo "The directory \"$dir\" does not exist. Attempting to create..."
if mkdir -p "$dir"; then
log_success "Directory $dir created ✅"
else
log_error "Failed to create $dir_type directory at \"$dir\". Check permissions ❌"
fi
fi
if [ ! -w "$dir" ] || [ ! -r "$dir" ]; then
log_error "Directory \"$dir\" is not writable or readable. Check permissions ❌"
fi
}
setup_directory_structure() {
local media_dir="$1"
create_and_verify_directory "$media_dir" "media"
for subdir in "${MEDIA_SUBDIRS[@]}"; do
create_and_verify_directory "$media_dir/$subdir" "media subdirectory"
done
}
verify_user_permissions() {
local username="$1"
local directory="$2"
if ! id -u "$username" &>/dev/null; then
log_error "User \"$username\" doesn't exist!"
fi
if ! sudo -u "$username" test -w "$directory"; then
log_error "User \"$username\" doesn't have write permissions to \"$directory\""
fi
} }
#+end_src #+end_src
** Check the dependencies ** Check Dependencies
:PROPERTIES: :PROPERTIES:
:ID: e7d01eeb-c7ef-42ff-b60d-010be30bc6a8 :ID: e7d01eeb-c7ef-42ff-b60d-010be30bc6a8
:END: :END:
This function verifies that the dependencies are installed. ~Docker~ and ~Docker Compose~ are required
for YAMS to work.
#+begin_src bash #+begin_src bash
check_dependencies() { check_dependencies() {
if command -v docker &> /dev/null; then local missing_packages=()
send_success_message "docker exists ✅ " local install_cmd=""
if docker compose version &> /dev/null; then
send_success_message "docker compose exists ✅ " # Check for required commands and collect missing ones
for pkg in "${REQUIRED_COMMANDS[@]}"; do
if ! command -v "$pkg" &> /dev/null; then
missing_packages+=("$pkg")
else else
echo -e $(printf "\e[31m ⚠️ docker compose not found! ⚠️\e[0m") log_success "$pkg exists ✅"
read -p "Do you want YAMS to install Docker Compose? IT ONLY WORKS ON DEBIAN AND UBUNTU! [y/N]: " install_docker
install_docker=${install_docker:-"n"}
if [ "$install_docker" == "y" ]; then
bash ./docker.sh
else
send_error_message "Install Docker Compose and come back later!"
fi fi
fi
else
echo -e $(printf "\e[31m ⚠️ docker not found! ⚠️\e[0m")
read -p "Do you want YAMS to install Docker and Docker Compose? IT ONLY WORKS ON DEBIAN AND UBUNTU! [y/N]: " install_docker
install_docker=${install_docker:-"n"}
if [ "$install_docker" == "y" ]; then
bash ./docker.sh
else
send_error_message "Install Docker and Docker Compose and come back later!"
fi
fi
}
#+end_src
** Running services location
:PROPERTIES:
:ID: 53213557-edfe-4da7-88c0-e0e202429116
:END:
This function just displays the locations for every container so the user can access to it when YAMS
finish installing.
#+begin_src bash
running_services_location() {
host_ip=$(hostname -I | awk '{ print $1 }')
services=(
"qBittorrent:8080"
"Radarr:7878"
"Sonarr:8989"
"Lidarr:8686"
"Readarr:8787"
"Prowlarr:9696"
"Bazarr:6767"
"$media_service:$media_service_port"
"Portainer:9000"
)
echo -e "Service URLs:"
for service in "${services[@]}"; do
service_name="${service%%:*}"
service_port="${service##*:}"
echo "$service_name: http://$host_ip:$service_port/"
done done
# If there are missing packages, offer to install them
if [ ${#missing_packages[@]} -gt 0 ]; then
log_warning "Missing required packages: ${missing_packages[*]}"
read -p "Would you like to install the missing packages? (y/N) [Default = n]: " install_deps
install_deps=${install_deps:-"n"}
if [ "${install_deps,,}" = "y" ]; then
echo "Installing missing packages..."
if ! sudo apt update && sudo apt install -y "${missing_packages[@]}"; then
log_error "Failed to install missing packages. Please install them manually: ${missing_packages[*]}"
fi
log_success "Successfully installed missing packages ✅"
else
log_error "Please install the required packages manually: ${missing_packages[*]}"
fi
fi
# Check Docker and Docker Compose
if command -v docker &> /dev/null; then
log_success "docker exists ✅"
if docker compose version &> /dev/null; then
log_success "docker compose exists ✅"
return 0
fi
fi
log_warning "⚠️ Docker/Docker Compose not found! ⚠️"
read -p "Install Docker and Docker Compose? Only works on Debian/Ubuntu (y/N) [Default = n]: " install_docker
install_docker=${install_docker:-"n"}
if [ "${install_docker,,}" = "y" ]; then
bash ./docker.sh
else
log_error "Please install Docker and Docker Compose first"
fi
} }
#+end_src #+end_src
* Verify all the dependencies ** Service Configuration
:PROPERTIES: :PROPERTIES:
:ID: e945d5a8-5142-41fe-8175-96de7aa84cf2 :ID: new-service-section
:END: :END:
#+begin_src bash #+begin_src bash
echo "Checking prerequisites..." configure_media_service() {
echo
echo
echo
log_info "Time to choose your media service."
log_info "Your media service is responsible for serving your files to your network."
log_info "Supported media services:"
log_info "- jellyfin (recommended, easier)"
log_info "- emby"
log_info "- plex (advanced, always online)"
read -p "Choose your media service [$DEFAULT_MEDIA_SERVICE]: " media_service
media_service=${media_service:-$DEFAULT_MEDIA_SERVICE}
media_service=$(echo "$media_service" | awk '{print tolower($0)}')
check_dependencies if [[ ! " ${SUPPORTED_MEDIA_SERVICES[@]} " =~ " ${media_service} " ]]; then
log_error "\"$media_service\" is not supported by YAMS"
if [[ "$EUID" = 0 ]]; then
send_error_message "YAMS has to run without sudo! Please, run it again with regular permissions"
fi
#+end_src
* Gather all the required information
:PROPERTIES:
:ID: 438cecef-2bd6-4d7c-b429-6c674ae311d9
:END:
** Checking install location
:PROPERTIES:
:ID: fff12355-9d79-40fe-a540-cfba2a176a3e
:END:
#+begin_src bash
default_install_directory="/opt/yams"
read -p "Where do you want to install the docker-compose file? [$default_install_directory]: " install_directory
install_directory=${install_directory:-$default_install_directory}
if [ ! -d "$install_directory" ]; then
echo "The directory \"$install_directory\" does not exists. Attempting to create..."
if mkdir -p "$install_directory"; then
send_success_message "Directory $install_directory created ✅"
else
send_error_message "There was an error creating the installation directory at \"$install_directory\". Make sure you have the necessary permissions ❌"
fi fi
fi
if [ ! -w "$install_directory" ] || [ ! -r "$install_directory" ]; then # Set media service port
send_error_message "The directory \"$install_directory\" is not writable or readable by the current user. Set the correct permissions or try a different directory" ❌ if [ "$media_service" == "plex" ]; then
fi
filename="$install_directory/docker-compose.yaml"
custom_file_filename="$install_directory/docker-compose.custom.yaml"
env_file="$install_directory/.env"
#+end_src
** Setting the correct user
:PROPERTIES:
:ID: 7428d7b7-aec5-4638-b370-84e9055fb412
:END:
#+begin_src bash
read -p "What's the user that is going to own the media server files? [$USER]: " username
username=${username:-$USER}
if id -u "$username" &>/dev/null; then
puid=$(id -u "$username");
pgid=$(id -g "$username");
else
send_error_message "The user \"$username\" doesn't exist!"
fi
#+end_src
** Media directory
:PROPERTIES:
:ID: 9726dead-8833-4f23-98b8-2790d72605de
:END:
#+begin_src bash
read -p "Please, input your media directory [/srv/media]: " media_directory
media_directory=${media_directory:-"/srv/media"}
read -p "Are you sure your media directory is \"$media_directory\"? [y/N]: " media_directory_correct
media_directory_correct=${media_directory_correct:-"n"}
if [ ! -d "$media_directory" ]; then
echo "The directory \"$media_directory\" does not exists. Attempting to create..."
if mkdir -p "$media_directory"; then
send_success_message "Directory $media_directory created ✅"
else
send_error_message "There was an error creating the installation directory at \"$media_directory\". Make sure you have the necessary permissions ❌"
fi
fi
if [ "$media_directory_correct" == "n" ]; then
send_error_message "Media directory is not correct. Please fix it and run the script again ❌"
fi
#+end_src
** Setting perferred media service
:PROPERTIES:
:ID: 3af8dbed-3a88-4739-a721-6434993c0b67
:END:
#+begin_src bash
echo -e "\n\n\nTime to choose your media service."
echo "Your media service is responsible for serving your files to your network."
echo "By default, YAMS supports 3 media services:"
echo "- jellyfin (recommended, easier)"
echo "- emby"
echo "- plex (advanced, always online)"
read -p "Choose your media service [jellyfin]: " media_service
media_service=${media_service:-"jellyfin"}
media_service=$(echo "$media_service" | awk '{print tolower($0)}')
media_service_port=8096
if [ "$media_service" == "plex" ]; then
media_service_port=32400 media_service_port=32400
fi else
media_service_port=8096
fi
if echo "emby plex jellyfin" | grep -qw "$media_service"; then echo
echo -e "\nYAMS is going to install \"$media_service\" on port \"$media_service_port\"" log_success "YAMS will install \"$media_service\" on port \"$media_service_port\""
else
send_error_message "\"$media_service\" is not supported by YAMS. Are you sure you chose the correct service?"
fi
#+end_src
** Setting the VPN # Export for use in other functions
:PROPERTIES: export media_service media_service_port
:ID: 1da4fe67-ee20-4b70-8f36-4a9f7161b6ca }
:END:
#+begin_src bash configure_vpn() {
echo -e "\nTime to set up the VPN." echo
echo "You can check the supported VPN list here: https://yams.media/advanced/vpn." echo
echo
log_info "Time to set up the VPN."
log_info "Supported VPN providers: https://yams.media/advanced/vpn"
read -p "Do you want to configure a VPN? [Y/n]: " setup_vpn read -p "Configure VPN? (Y/n) [Default = y]: " setup_vpn
setup_vpn=${setup_vpn:-"y"} setup_vpn=${setup_vpn:-"y"}
if [ "$setup_vpn" == "y" ]; then if [ "${setup_vpn,,}" != "y" ]; then
read -p "What's your VPN service? (with spaces) [mullvad]: " vpn_service export setup_vpn="n"
vpn_service=${vpn_service:-"mullvad"} return 0
fi
echo -e "\nYou should read $vpn_service's documentation in case it has different configurations for username and password." read -p "VPN service? (with spaces) [$DEFAULT_VPN_SERVICE]: " vpn_service
echo "The documentation for $vpn_service is here: https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/${vpn_service// /-}.md" vpn_service=${vpn_service:-$DEFAULT_VPN_SERVICE}
read -p "What's your VPN username? (without spaces): " vpn_user echo
log_info "Please check $vpn_service's documentation for specific configuration:"
log_info "https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/${vpn_service// /-}.md"
read -p "VPN username (without spaces): " vpn_user
[ -z "$vpn_user" ] && log_error "VPN username cannot be empty"
# Use hidden input for password
unset vpn_password unset vpn_password
charcount=0 charcount=0
prompt="What's your VPN password? (if you are using mullvad, just enter your username again): " prompt="VPN password (if using mullvad, enter username again): "
while IFS= read -p "$prompt" -r -s -n 1 char while IFS= read -p "$prompt" -r -s -n 1 char; do
do if [[ $char == $'\0' ]]; then
if [[ $char == $'\0' ]]
then
break break
fi fi
if [[ $char == $'\177' ]] ; then if [[ $char == $'\177' ]]; then
if [ $charcount -gt 0 ] ; then if [ $charcount -gt 0 ]; then
charcount=$((charcount-1)) charcount=$((charcount-1))
prompt=$'\b \b' prompt=$'\b \b'
vpn_password="${vpn_password%?}" vpn_password="${vpn_password%?}"
@ -339,173 +296,253 @@ if [ "$setup_vpn" == "y" ]; then
fi fi
done done
echo echo
fi
echo "Configuring the docker-compose file for the user \"$username\" on \"$install_directory\"..." [ -z "$vpn_password" ] && log_error "VPN password cannot be empty"
#+end_src
* Installing YAMS # Export for use in other functions
:PROPERTIES: export vpn_service vpn_user vpn_password setup_vpn
:ID: 44e5f3f1-3ae7-4f88-ba96-8149c9980fb2 }
:END:
** Copy the docker-compose file to the install location
:PROPERTIES:
:ID: 09018e25-ed48-46e9-85c3-586c37844c11
:END:
#+begin_src bash running_services_location() {
copy_files=( local host_ip
"docker-compose.example.yaml:$filename" host_ip=$(hostname -I | awk '{ print $1 }')
".env.example:$env_file"
"docker-compose.custom.yaml:$custom_file_filename"
)
for file_mapping in "${copy_files[@]}"; do local -A services=(
source_file="${file_mapping%%:*}" ["qBittorrent"]="8081"
destination_file="${file_mapping##*:}" ["SABnzbd"]="8080"
["Radarr"]="7878"
["Sonarr"]="8989"
["Lidarr"]="8686"
["Readarr"]="8787"
["Prowlarr"]="9696"
["Bazarr"]="6767"
["$media_service"]="$media_service_port"
["Portainer"]="9000"
)
echo -e "\nCopying $source_file to $destination_file..." echo -e "Service URLs:"
if cp "$source_file" "$destination_file"; then for service in "${!services[@]}"; do
send_success_message "$source_file was copied successfuly! ✅" echo "$service: http://$host_ip:${services[$service]}/"
done
}
get_user_info() {
read -p "User to own the media server files? [$USER]: " username
username=${username:-$USER}
if id -u "$username" &>/dev/null; then
puid=$(id -u "$username")
pgid=$(id -g "$username")
else else
send_error_message "Failed to copy $source_file to $destination_file. Ensure your user ($USER) has the necessary permissions ❌" log_error "User \"$username\" doesn't exist!"
fi fi
done
export username puid pgid
}
get_installation_paths() {
read -p "Installation directory? [$DEFAULT_INSTALL_DIR]: " install_directory
install_directory=${install_directory:-$DEFAULT_INSTALL_DIR}
create_and_verify_directory "$install_directory" "installation"
read -p "Media directory? [$DEFAULT_MEDIA_DIR]: " media_directory
media_directory=${media_directory:-$DEFAULT_MEDIA_DIR}
read -p "Are you sure your media directory is \"$media_directory\"? (y/N) [Default = n]: " media_directory_correct
media_directory_correct=${media_directory_correct:-"n"}
if [ "${media_directory_correct,,}" != "y" ]; then
log_error "Media directory is not correct. Please fix it and run the script again ❌"
fi
setup_directory_structure "$media_directory"
verify_user_permissions "$username" "$media_directory"
export install_directory media_directory
}
#+end_src #+end_src
#+RESULTS: ** File Operations
** Set PUID, PGID, Media Folder, Media Service, Config folder and VPN on the YAMS scripts
:PROPERTIES: :PROPERTIES:
:ID: 3d169001-f0f7-477f-a954-0460484f4b43 :ID: new-file-operations-section
:END:
This steps prepares all the files with the correct information that was collected on the "[[#gather-all-the-required-information][Gather all the
required information]]" step.
#+begin_src bash
sed -i -e "s|<your_PUID>|$puid|g" "$env_file" \
-e "s|<your_PGID>|$pgid|g" "$env_file" \
-e "s|<media_directory>|$media_directory|g" "$env_file" \
-e "s|<media_service>|$media_service|g" "$env_file" \
-e "s|<media_service>|$media_service|g" "$filename"
if [ "$media_service" == "plex" ]; then
sed -i -e "s|#network_mode: host # plex|network_mode: host # plex|g" "$filename" \
-e "s|ports: # plex|#ports: # plex|g" "$filename" \
-e "s|- 8096:8096 # plex|#- 8096:8096 # plex|g" "$filename"
fi
sed -i -e "s|<install_directory>|$install_directory|g" "$env_file" \
-e "s|vpn_enabled|$setup_vpn|g" "$env_file" \
if [ "$setup_vpn" == "y" ]; then
sed -i -e "s|vpn_service|$vpn_service|g" "$env_file" \
-e "s|vpn_user|$vpn_user|g" "$env_file" \
-e "s|vpn_password|$vpn_password|g" "$env_file" \
-e "s|#network_mode: \"service:gluetun\"|network_mode: \"service:gluetun\"|g" "$filename" \
-e "s|ports: # qbittorrent|#ports: # qbittorrent|g" "$filename" \
-e "s|- 8080:8080 # qbittorrent|#- 8080:8080 # qbittorrent|g" "$filename" \
-e "s|#- 8080:8080/tcp # gluetun|- 8080:8080/tcp # gluetun|g" "$filename"
fi
#+end_src
** Set the configuration for the YAMS binary
:PROPERTIES:
:ID: b6a8732f-9dbe-4d93-b04d-27156eacdea2
:END: :END:
#+begin_src bash #+begin_src bash
sed -i -e "s|<filename>|$filename|g" yams \ copy_configuration_files() {
-e "s|<custom_file_filename>|$custom_file_filename|g" yams \ local -A files=(
-e "s|<install_directory>|$install_directory|g" yams ["docker-compose.example.yaml"]="docker-compose.yaml"
#+end_src [".env.example"]=".env"
["docker-compose.custom.yaml"]="docker-compose.custom.yaml"
)
** Success message! for src in "${!files[@]}"; do
:PROPERTIES: local dest="$install_directory/${files[$src]}"
:ID: 7b0ed8f5-780b-4685-8123-8d5c4229eaba echo
:END: log_info "Copying $src to $dest..."
Finally, YAMS is installed 🔥. Show the success message if cp "$src" "$dest"; then
log_success "$src copied successfully ✅"
#+begin_src bash
send_success_message "Everything installed correctly! 🎉"
echo "Running the server..."
echo "This is going to take a while..."
docker compose -f "$filename" up -d
#+end_src
* Final steps
:PROPERTIES:
:ID: 65ce5828-b69a-4a0e-83f6-b029e19caea1
:END:
** Install the YAMS CLI
:PROPERTIES:
:ID: f4f9d166-8a2b-4d79-bc7f-fe73ecf5fb77
:END:
This steps requires ~sudo~ because it's copying the main yams script to the ~/usr/local/bin/yams~
directory.
#+begin_src bash
echo -e "\nWe need your sudo password to install the YAMS CLI and configure permissions..."
if sudo cp yams /usr/local/bin/yams && sudo chmod +x /usr/local/bin/yams; then
send_success_message "YAMS CLI installed successfully ✅"
else
send_error_message "Failed to install YAMS CLI. Make sure you have the necessary permissions ❌"
fi
#+end_src
** Set the correct permissions to the media and install directories
:PROPERTIES:
:ID: 4cfb9397-776d-46db-84cc-54b78395cba8
:END:
This adds the correct permissions to the media folder, in case they are not correct.
#+begin_src bash
if sudo chown -R "$puid":"$pgid" "$media_directory"; then
send_success_message "Media directory ownership and permissions set successfully ✅"
else
send_error_message "Failed to set ownership and permissions for the media directory. Check permissions ❌"
fi
if sudo chown -R "$puid":"$pgid" "$install_directory"; then
send_success_message "Install directory ownership and permissions set successfully ✅"
else
send_error_message "Failed to set ownership and permissions for the install directory. Check permissions ❌"
fi
#+end_src
** Create the config directory
:PROPERTIES:
:ID: 699f35fe-edde-406d-be0b-3ff2eaa6d7eb
:END:
This is where all the configurations are going to be saved. If it doesn't it will try and create it. If
it can't be created, we'll raise an error.
#+begin_src bash
if [[ -d "$install_directory/config" ]]; then
send_success_message "Configuration folder \"$install_directory/config\" exists ✅"
else
if sudo mkdir -p "$install_directory/config"; then
send_success_message "Configuration folder \"$install_directory/config\" created ✅"
else else
send_error_message "Failed to create or access the configuration folder. Check permissions ❌" log_error "Failed to copy $src to $dest. Check permissions ❌"
fi fi
done
}
update_configuration_files() {
local filename="$install_directory/docker-compose.yaml"
local env_file="$install_directory/.env"
local yams_script="yams"
# Update .env file
log_info "Updating environment configuration..."
sed -i -e "s|<your_PUID>|$puid|g" \
-e "s|<your_PGID>|$pgid|g" \
-e "s|<media_directory>|$media_directory|g" \
-e "s|<media_service>|$media_service|g" \
-e "s|<install_directory>|$install_directory|g" \
-e "s|vpn_enabled|$setup_vpn|g" "$env_file" || \
log_error "Failed to update .env file"
# Update VPN configuration in .env file
if [ "${setup_vpn,,}" == "y" ]; then
sed -i -e "s|^VPN_ENABLED=.*|VPN_ENABLED=y|" \
-e "s|^VPN_SERVICE=.*|VPN_SERVICE=$vpn_service|" \
-e "s|^VPN_USER=.*|VPN_USER=$vpn_user|" \
-e "s|^VPN_PASSWORD=.*|VPN_PASSWORD=$vpn_password|" "$env_file" || \
log_error "Failed to update VPN configuration in .env"
else
sed -i -e "s|^VPN_ENABLED=.*|VPN_ENABLED=n|" "$env_file" || \
log_error "Failed to update VPN configuration in .env"
fi fi
if sudo chown -R "$puid":"$pgid" "$install_directory/config"; then # Update docker-compose.yaml
send_success_message "Configuration folder ownership and permissions set successfully ✅" log_info "Updating docker-compose configuration..."
else sed -i "s|<media_service>|$media_service|g" "$filename" || \
send_error_message "Failed to set ownership and permissions for the configuration folder. Check permissions ❌" log_error "Failed to update docker-compose.yaml"
fi
# Configure Plex-specific settings
if [ "$media_service" == "plex" ]; then
log_info "Configuring Plex-specific settings..."
sed -i -e 's|#network_mode: host # plex|network_mode: host # plex|g' \
-e 's|ports: # plex|#ports: # plex|g' \
-e 's|- 8096:8096 # plex|#- 8096:8096 # plex|g' "$filename" || \
log_error "Failed to configure Plex settings"
fi
# Configure VPN settings if enabled
if [ "${setup_vpn,,}" == "y" ]; then
log_info "Configuring VPN settings..."
sed -i -e "s|vpn_service|$vpn_service|g" \
-e "s|vpn_user|$vpn_user|g" \
-e "s|vpn_password|$vpn_password|g" \
-e 's|#network_mode: "service:gluetun"|network_mode: "service:gluetun"|g' \
-e 's|ports: # qbittorrent|#ports: # qbittorrent|g' \
-e 's|ports: # sabnzbd|#ports: # sabnzbd|g' \
-e 's|- 8081:8081 # qbittorrent|#- 8081:8081 # qbittorrent|g' \
-e 's|- 8080:8080 # sabnzbd|#- 8080:8080 # sabnzbd|g' \
-e 's|#- 8080:8080/tcp # gluetun|- 8080:8080/tcp # gluetun|g' \
-e 's|#- 8081:8081/tcp # gluetun|- 8081:8081/tcp # gluetun|g' "$filename" || \
log_error "Failed to configure VPN settings"
fi
# Update YAMS CLI script
log_info "Updating YAMS CLI configuration..."
sed -i -e "s|<filename>|$filename|g" \
-e "s|<custom_file_filename>|$install_directory/docker-compose.custom.yaml|g" \
-e "s|<install_directory>|$install_directory|g" "$yams_script" || \
log_error "Failed to update YAMS CLI script"
}
install_cli() {
echo
log_info "Installing YAMS CLI..."
if sudo cp yams /usr/local/bin/yams && sudo chmod +x /usr/local/bin/yams; then
log_success "YAMS CLI installed successfully ✅"
else
log_error "Failed to install YAMS CLI. Check permissions ❌"
fi
}
set_permissions() {
local dirs=("$media_directory" "$install_directory" "$install_directory/config")
for dir in "${dirs[@]}"; do
log_info "Setting permissions for $dir..."
if [ ! -d "$dir" ]; then
mkdir -p "$dir" || log_error "Failed to create directory $dir"
fi
if sudo chown -R "$puid:$pgid" "$dir"; then
log_success "Permissions set successfully for $dir ✅"
else
log_error "Failed to set permissions for $dir ❌"
fi
done
}
#+end_src #+end_src
* Display closing message * Script Execution
:PROPERTIES:
:ID: new-script-execution
:END:
** Verify Prerequisites
:PROPERTIES:
:ID: e945d5a8-5142-41fe-8175-96de7aa84cf2
:END:
#+begin_src bash
# Prevent running as root
if [[ "$EUID" = 0 ]]; then
log_error "YAMS must run without sudo! Please run with regular permissions"
fi
# Check all dependencies
log_info "Checking prerequisites..."
check_dependencies
#+end_src
** Installation Process
:PROPERTIES:
:ID: new-installation-process
:END:
#+begin_src bash
# Get user information
get_user_info
# Get installation paths
get_installation_paths
# Configure services
configure_media_service
configure_vpn
log_info "Configuring the docker-compose file for user \"$username\" in \"$install_directory\"..."
# Copy and update configuration files
copy_configuration_files
update_configuration_files
log_success "Everything installed correctly! 🎉"
# Start services
log_info "Starting YAMS services..."
log_info "This may take a while..."
if ! docker compose -f "$install_directory/docker-compose.yaml" up -d; then
log_error "Failed to start YAMS services"
fi
# Install CLI and set permissions
echo
log_info "We need your sudo password to install the YAMS CLI and configure permissions..."
install_cli
set_permissions
#+end_src
* Display Closing Message
:PROPERTIES: :PROPERTIES:
:ID: 238e3eae-9df7-4a7f-a460-7a61c07b5442 :ID: 238e3eae-9df7-4a7f-a460-7a61c07b5442
:END: :END:
@ -513,36 +550,42 @@ fi
#+begin_src bash #+begin_src bash
printf "\033c" printf "\033c"
echo "========================================================" cat << "EOF"
echo " _____ ___ ___ ___ " ========================================================
echo " / /::\ / /\ /__/\ / /\ " _____ ___ ___ ___
echo " / /:/\:\ / /::\ \ \:\ / /:/_ " / /::\ / /\ /__/\ / /\
echo " / /:/ \:\ / /:/\:\ \ \:\ / /:/ /\ " / /:/\:\ / /::\ \ \:\ / /:/_
echo " /__/:/ \__\:| / /:/ \:\ _____\__\:\ / /:/ /:/_ " / /:/ \:\ / /:/\:\ \ \:\ / /:/ /\
echo " \ \:\ / /:/ /__/:/ \__\:\ /__/::::::::\ /__/:/ /:/ /\\" /__/:/ \__\:| / /:/ \:\ _____\__\:\ / /:/ /:/_
echo " \ \:\ /:/ \ \:\ / /:/ \ \:\~~\~~\/ \ \:\/:/ /:/" \ \:\ / /:/ /__/:/ \__\:\ /__/::::::::\ /__/:/ /:/ /\\
echo " \ \:\/:/ \ \:\ /:/ \ \:\ ~~~ \ \::/ /:/ " \ \:\ /:/ \ \:\ / /:/ \ \:\~~\~~\/ \ \:\/:/ /:/
echo " \ \::/ \ \:\/:/ \ \:\ \ \:\/:/ " \ \:\/:/ \ \:\ /:/ \ \:\ ~~~ \ \::/ /:/
echo " \__\/ \ \::/ \ \:\ \ \::/ " \ \::/ \ \:\/:/ \ \:\ \ \:\/:/
echo " \__\/ \__\/ \__\/ " \__\/ \ \::/ \ \:\ \ \::/
echo "========================================================" \__\/ \__\/ \__\/
send_success_message "All done!✅ Enjoy YAMS!" ========================================================
echo "You can check the installation on $install_directory" EOF
echo "========================================================"
echo "Everything should be running now! To check everything running, go to:" log_success "All done!✅ Enjoy YAMS!"
log_info "You can check the installation in $install_directory"
log_info "========================================================"
log_info "Everything should be running now! To check everything running, go to:"
echo echo
running_services_location running_services_location
echo echo
log_info "You might need to wait for a couple of minutes while everything gets up and running"
echo echo
echo "You might need to wait for a couple of minutes while everything gets up and running" log_info "All the service locations are also saved in ~/yams_services.txt"
echo
echo "All the services location are also saved in ~/yams_services.txt"
running_services_location > ~/yams_services.txt running_services_location > ~/yams_services.txt
echo "========================================================"
log_info "========================================================"
echo echo
echo "To configure YAMS, check the documentation at" log_info "To configure YAMS, check the documentation at"
echo "https://yams.media/config" log_info "https://yams.media/config"
echo echo
echo "========================================================" log_info "========================================================"
exit 0 exit 0
#+end_src #+end_src

View File

@ -23,175 +23,199 @@ echo "To finish the installation of the CLI"
echo "====================================================" echo "===================================================="
echo "" echo ""
send_success_message() { # Constants
echo -e $(printf "\e[32m$1\e[0m") readonly DEFAULT_INSTALL_DIR="/opt/yams"
readonly DEFAULT_MEDIA_DIR="/srv/media"
readonly SUPPORTED_MEDIA_SERVICES=("jellyfin" "emby" "plex")
readonly DEFAULT_MEDIA_SERVICE="jellyfin"
readonly DEFAULT_VPN_SERVICE="protonvpn"
readonly MEDIA_SUBDIRS=("tvshows" "movies" "music" "books" "downloads" "blackhole")
# Color codes
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # No Color
# Dependencies
readonly REQUIRED_COMMANDS=("curl" "sed" "awk")
log_success() {
echo -e "${GREEN}$1${NC}"
} }
send_error_message() { log_error() {
echo -e $(printf "\e[31m$1\e[0m") echo -e "${RED}$1${NC}" >&2
exit 255 exit 1
} }
check_dependencies() { log_warning() {
if command -v docker &> /dev/null; then echo -e "${YELLOW}$1${NC}"
send_success_message "docker exists ✅ " }
if docker compose version &> /dev/null; then
send_success_message "docker compose exists ✅ "
else
echo -e $(printf "\e[31m ⚠️ docker compose not found! ⚠️\e[0m")
read -p "Do you want YAMS to install Docker Compose? IT ONLY WORKS ON DEBIAN AND UBUNTU! (y/N) [Default = n]: " install_docker
install_docker=${install_docker:-"n"}
if [ "$install_docker" == "y" ]; then log_info() {
bash ./docker.sh echo "$1"
else }
send_error_message "Install Docker Compose and come back later!"
fi
fi
else
echo -e $(printf "\e[31m ⚠️ docker not found! ⚠️\e[0m")
read -p "Do you want YAMS to install Docker and Docker Compose? IT ONLY WORKS ON DEBIAN AND UBUNTU! (y/N) [Default = n]: " install_docker
install_docker=${install_docker:-"n"}
if [ "$install_docker" == "y" ]; then create_and_verify_directory() {
bash ./docker.sh local dir="$1"
local dir_type="$2"
if [ ! -d "$dir" ]; then
echo "The directory \"$dir\" does not exist. Attempting to create..."
if mkdir -p "$dir"; then
log_success "Directory $dir created ✅"
else else
send_error_message "Install Docker and Docker Compose and come back later!" log_error "Failed to create $dir_type directory at \"$dir\". Check permissions ❌"
fi fi
fi fi
if [ ! -w "$dir" ] || [ ! -r "$dir" ]; then
log_error "Directory \"$dir\" is not writable or readable. Check permissions ❌"
fi
} }
running_services_location() { setup_directory_structure() {
host_ip=$(hostname -I | awk '{ print $1 }') local media_dir="$1"
services=( create_and_verify_directory "$media_dir" "media"
"qBittorrent:8080"
"Radarr:7878"
"Sonarr:8989"
"Lidarr:8686"
"Readarr:8787"
"Prowlarr:9696"
"Bazarr:6767"
"$media_service:$media_service_port"
"Portainer:9000"
)
echo -e "Service URLs:" for subdir in "${MEDIA_SUBDIRS[@]}"; do
for service in "${services[@]}"; do create_and_verify_directory "$media_dir/$subdir" "media subdirectory"
service_name="${service%%:*}"
service_port="${service##*:}"
echo "$service_name: http://$host_ip:$service_port/"
done done
} }
echo "Checking prerequisites..." verify_user_permissions() {
local username="$1"
local directory="$2"
if ! id -u "$username" &>/dev/null; then
check_dependencies log_error "User \"$username\" doesn't exist!"
if [[ "$EUID" = 0 ]]; then
send_error_message "YAMS has to run without sudo! Please, run it again with regular permissions"
fi
default_install_directory="/opt/yams"
read -p "Where do you want to install the docker-compose file? [$default_install_directory]: " install_directory
install_directory=${install_directory:-$default_install_directory}
if [ ! -d "$install_directory" ]; then
echo "The directory \"$install_directory\" does not exists. Attempting to create..."
if mkdir -p "$install_directory"; then
send_success_message "Directory $install_directory created ✅"
else
send_error_message "There was an error creating the installation directory at \"$install_directory\". Make sure you have the necessary permissions ❌"
fi fi
fi
if [ ! -w "$install_directory" ] || [ ! -r "$install_directory" ]; then if ! sudo -u "$username" test -w "$directory"; then
send_error_message "The directory \"$install_directory\" is not writable or readable by the current user. Set the correct permissions or try a different directory" log_error "User \"$username\" doesn't have write permissions to \"$directory\""
fi
filename="$install_directory/docker-compose.yaml"
custom_file_filename="$install_directory/docker-compose.custom.yaml"
env_file="$install_directory/.env"
read -p "What's the user that is going to own the media server files? [$USER]: " username
username=${username:-$USER}
if id -u "$username" &>/dev/null; then
puid=$(id -u "$username");
pgid=$(id -g "$username");
else
send_error_message "The user \"$username\" doesn't exist!"
fi
read -p "Please, input your media directory [/srv/media]: " media_directory
media_directory=${media_directory:-"/srv/media"}
read -p "Are you sure your media directory is \"$media_directory\"? (y/N) [Default = n]: " media_directory_correct
media_directory_correct=${media_directory_correct:-"n"}
if [ ! -d "$media_directory" ]; then
echo "The directory \"$media_directory\" does not exists. Attempting to create..."
if mkdir -p "$media_directory"; then
send_success_message "Directory $media_directory created ✅"
else
send_error_message "There was an error creating the installation directory at \"$media_directory\". Make sure you have the necessary permissions ❌"
fi fi
fi }
if [ "$media_directory_correct" == "n" ]; then check_dependencies() {
send_error_message "Media directory is not correct. Please fix it and run the script again ❌" local missing_packages=()
fi local install_cmd=""
echo -e "\n\n\nTime to choose your media service." # Check for required commands and collect missing ones
echo "Your media service is responsible for serving your files to your network." for pkg in "${REQUIRED_COMMANDS[@]}"; do
echo "By default, YAMS supports 3 media services:" if ! command -v "$pkg" &> /dev/null; then
echo "- jellyfin (recommended, easier)" missing_packages+=("$pkg")
echo "- emby" else
echo "- plex (advanced, always online)" log_success "$pkg exists ✅"
fi
done
read -p "Choose your media service [jellyfin]: " media_service # If there are missing packages, offer to install them
media_service=${media_service:-"jellyfin"} if [ ${#missing_packages[@]} -gt 0 ]; then
media_service=$(echo "$media_service" | awk '{print tolower($0)}') log_warning "Missing required packages: ${missing_packages[*]}"
read -p "Would you like to install the missing packages? (y/N) [Default = n]: " install_deps
install_deps=${install_deps:-"n"}
media_service_port=8096 if [ "${install_deps,,}" = "y" ]; then
if [ "$media_service" == "plex" ]; then echo "Installing missing packages..."
if ! sudo apt update && sudo apt install -y "${missing_packages[@]}"; then
log_error "Failed to install missing packages. Please install them manually: ${missing_packages[*]}"
fi
log_success "Successfully installed missing packages ✅"
else
log_error "Please install the required packages manually: ${missing_packages[*]}"
fi
fi
# Check Docker and Docker Compose
if command -v docker &> /dev/null; then
log_success "docker exists ✅"
if docker compose version &> /dev/null; then
log_success "docker compose exists ✅"
return 0
fi
fi
log_warning "⚠️ Docker/Docker Compose not found! ⚠️"
read -p "Install Docker and Docker Compose? Only works on Debian/Ubuntu (y/N) [Default = n]: " install_docker
install_docker=${install_docker:-"n"}
if [ "${install_docker,,}" = "y" ]; then
bash ./docker.sh
else
log_error "Please install Docker and Docker Compose first"
fi
}
configure_media_service() {
echo
echo
echo
log_info "Time to choose your media service."
log_info "Your media service is responsible for serving your files to your network."
log_info "Supported media services:"
log_info "- jellyfin (recommended, easier)"
log_info "- emby"
log_info "- plex (advanced, always online)"
read -p "Choose your media service [$DEFAULT_MEDIA_SERVICE]: " media_service
media_service=${media_service:-$DEFAULT_MEDIA_SERVICE}
media_service=$(echo "$media_service" | awk '{print tolower($0)}')
if [[ ! " ${SUPPORTED_MEDIA_SERVICES[@]} " =~ " ${media_service} " ]]; then
log_error "\"$media_service\" is not supported by YAMS"
fi
# Set media service port
if [ "$media_service" == "plex" ]; then
media_service_port=32400 media_service_port=32400
fi else
media_service_port=8096
fi
if echo "emby plex jellyfin" | grep -qw "$media_service"; then echo
echo -e "\nYAMS is going to install \"$media_service\" on port \"$media_service_port\"" log_success "YAMS will install \"$media_service\" on port \"$media_service_port\""
else
send_error_message "\"$media_service\" is not supported by YAMS. Are you sure you chose the correct service?"
fi
echo -e "\nTime to set up the VPN." # Export for use in other functions
echo "You can check the supported VPN list here: https://yams.media/advanced/vpn." export media_service media_service_port
}
read -p "Do you want to configure a VPN? (Y/n) [Default = y]: " setup_vpn configure_vpn() {
setup_vpn=${setup_vpn:-"y"} echo
echo
echo
log_info "Time to set up the VPN."
log_info "Supported VPN providers: https://yams.media/advanced/vpn"
if [ "$setup_vpn" == "y" ]; then read -p "Configure VPN? (Y/n) [Default = y]: " setup_vpn
read -p "What's your VPN service? (with spaces) [mullvad]: " vpn_service setup_vpn=${setup_vpn:-"y"}
vpn_service=${vpn_service:-"mullvad"}
echo -e "\nYou should read $vpn_service's documentation in case it has different configurations for username and password." if [ "${setup_vpn,,}" != "y" ]; then
echo "The documentation for $vpn_service is here: https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/${vpn_service// /-}.md" export setup_vpn="n"
return 0
fi
read -p "What's your VPN username? (without spaces): " vpn_user read -p "VPN service? (with spaces) [$DEFAULT_VPN_SERVICE]: " vpn_service
vpn_service=${vpn_service:-$DEFAULT_VPN_SERVICE}
echo
log_info "Please check $vpn_service's documentation for specific configuration:"
log_info "https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/${vpn_service// /-}.md"
read -p "VPN username (without spaces): " vpn_user
[ -z "$vpn_user" ] && log_error "VPN username cannot be empty"
# Use hidden input for password
unset vpn_password unset vpn_password
charcount=0 charcount=0
prompt="What's your VPN password? (if you are using mullvad, just enter your username again): " prompt="VPN password (if using mullvad, enter username again): "
while IFS= read -p "$prompt" -r -s -n 1 char while IFS= read -p "$prompt" -r -s -n 1 char; do
do if [[ $char == $'\0' ]]; then
if [[ $char == $'\0' ]]
then
break break
fi fi
if [[ $char == $'\177' ]] ; then if [[ $char == $'\177' ]]; then
if [ $charcount -gt 0 ] ; then if [ $charcount -gt 0 ]; then
charcount=$((charcount-1)) charcount=$((charcount-1))
prompt=$'\b \b' prompt=$'\b \b'
vpn_password="${vpn_password%?}" vpn_password="${vpn_password%?}"
@ -205,131 +229,261 @@ if [ "$setup_vpn" == "y" ]; then
fi fi
done done
echo echo
fi
echo "Configuring the docker-compose file for the user \"$username\" on \"$install_directory\"..." [ -z "$vpn_password" ] && log_error "VPN password cannot be empty"
copy_files=( # Export for use in other functions
"docker-compose.example.yaml:$filename" export vpn_service vpn_user vpn_password setup_vpn
".env.example:$env_file" }
"docker-compose.custom.yaml:$custom_file_filename"
)
for file_mapping in "${copy_files[@]}"; do running_services_location() {
source_file="${file_mapping%%:*}" local host_ip
destination_file="${file_mapping##*:}" host_ip=$(hostname -I | awk '{ print $1 }')
echo -e "\nCopying $source_file to $destination_file..." local -A services=(
if cp "$source_file" "$destination_file"; then ["qBittorrent"]="8081"
send_success_message "$source_file was copied successfuly! ✅" ["SABnzbd"]="8080"
["Radarr"]="7878"
["Sonarr"]="8989"
["Lidarr"]="8686"
["Readarr"]="8787"
["Prowlarr"]="9696"
["Bazarr"]="6767"
["$media_service"]="$media_service_port"
["Portainer"]="9000"
)
echo -e "Service URLs:"
for service in "${!services[@]}"; do
echo "$service: http://$host_ip:${services[$service]}/"
done
}
get_user_info() {
read -p "User to own the media server files? [$USER]: " username
username=${username:-$USER}
if id -u "$username" &>/dev/null; then
puid=$(id -u "$username")
pgid=$(id -g "$username")
else else
send_error_message "Failed to copy $source_file to $destination_file. Ensure your user ($USER) has the necessary permissions ❌" log_error "User \"$username\" doesn't exist!"
fi fi
done
sed -i -e "s|<your_PUID>|$puid|g" "$env_file" \ export username puid pgid
-e "s|<your_PGID>|$pgid|g" "$env_file" \ }
-e "s|<media_directory>|$media_directory|g" "$env_file" \
-e "s|<media_service>|$media_service|g" "$env_file" \
-e "s|<media_service>|$media_service|g" "$filename"
if [ "$media_service" == "plex" ]; then get_installation_paths() {
sed -i -e "s|#network_mode: host # plex|network_mode: host # plex|g" "$filename" \ read -p "Installation directory? [$DEFAULT_INSTALL_DIR]: " install_directory
-e "s|ports: # plex|#ports: # plex|g" "$filename" \ install_directory=${install_directory:-$DEFAULT_INSTALL_DIR}
-e "s|- 8096:8096 # plex|#- 8096:8096 # plex|g" "$filename" create_and_verify_directory "$install_directory" "installation"
fi
sed -i -e "s|<install_directory>|$install_directory|g" "$env_file" \ read -p "Media directory? [$DEFAULT_MEDIA_DIR]: " media_directory
-e "s|vpn_enabled|$setup_vpn|g" "$env_file" \ media_directory=${media_directory:-$DEFAULT_MEDIA_DIR}
if [ "$setup_vpn" == "y" ]; then read -p "Are you sure your media directory is \"$media_directory\"? (y/N) [Default = n]: " media_directory_correct
sed -i -e "s|vpn_service|$vpn_service|g" "$env_file" \ media_directory_correct=${media_directory_correct:-"n"}
-e "s|vpn_user|$vpn_user|g" "$env_file" \
-e "s|vpn_password|$vpn_password|g" "$env_file" \
-e "s|#network_mode: \"service:gluetun\"|network_mode: \"service:gluetun\"|g" "$filename" \
-e "s|ports: # qbittorrent|#ports: # qbittorrent|g" "$filename" \
-e "s|- 8080:8080 # qbittorrent|#- 8080:8080 # qbittorrent|g" "$filename" \
-e "s|#- 8080:8080/tcp # gluetun|- 8080:8080/tcp # gluetun|g" "$filename"
fi
sed -i -e "s|<filename>|$filename|g" yams \ if [ "${media_directory_correct,,}" != "y" ]; then
-e "s|<custom_file_filename>|$custom_file_filename|g" yams \ log_error "Media directory is not correct. Please fix it and run the script again ❌"
-e "s|<install_directory>|$install_directory|g" yams fi
send_success_message "Everything installed correctly! 🎉" setup_directory_structure "$media_directory"
verify_user_permissions "$username" "$media_directory"
echo "Running the server..." export install_directory media_directory
echo "This is going to take a while..." }
docker compose -f "$filename" up -d copy_configuration_files() {
local -A files=(
["docker-compose.example.yaml"]="docker-compose.yaml"
[".env.example"]=".env"
["docker-compose.custom.yaml"]="docker-compose.custom.yaml"
)
echo -e "\nWe need your sudo password to install the YAMS CLI and configure permissions..." for src in "${!files[@]}"; do
local dest="$install_directory/${files[$src]}"
echo
log_info "Copying $src to $dest..."
if sudo cp yams /usr/local/bin/yams && sudo chmod +x /usr/local/bin/yams; then if cp "$src" "$dest"; then
send_success_message "YAMS CLI installed successfully ✅" log_success "$src copied successfully ✅"
else
send_error_message "Failed to install YAMS CLI. Make sure you have the necessary permissions ❌"
fi
if sudo chown -R "$puid":"$pgid" "$media_directory"; then
send_success_message "Media directory ownership and permissions set successfully ✅"
else
send_error_message "Failed to set ownership and permissions for the media directory. Check permissions ❌"
fi
if sudo chown -R "$puid":"$pgid" "$install_directory"; then
send_success_message "Install directory ownership and permissions set successfully ✅"
else
send_error_message "Failed to set ownership and permissions for the install directory. Check permissions ❌"
fi
if [[ -d "$install_directory/config" ]]; then
send_success_message "Configuration folder \"$install_directory/config\" exists ✅"
else
if sudo mkdir -p "$install_directory/config"; then
send_success_message "Configuration folder \"$install_directory/config\" created ✅"
else else
send_error_message "Failed to create or access the configuration folder. Check permissions ❌" log_error "Failed to copy $src to $dest. Check permissions ❌"
fi fi
done
}
update_configuration_files() {
local filename="$install_directory/docker-compose.yaml"
local env_file="$install_directory/.env"
local yams_script="yams"
# Update .env file
log_info "Updating environment configuration..."
sed -i -e "s|<your_PUID>|$puid|g" \
-e "s|<your_PGID>|$pgid|g" \
-e "s|<media_directory>|$media_directory|g" \
-e "s|<media_service>|$media_service|g" \
-e "s|<install_directory>|$install_directory|g" \
-e "s|vpn_enabled|$setup_vpn|g" "$env_file" || \
log_error "Failed to update .env file"
# Update VPN configuration in .env file
if [ "${setup_vpn,,}" == "y" ]; then
sed -i -e "s|^VPN_ENABLED=.*|VPN_ENABLED=y|" \
-e "s|^VPN_SERVICE=.*|VPN_SERVICE=$vpn_service|" \
-e "s|^VPN_USER=.*|VPN_USER=$vpn_user|" \
-e "s|^VPN_PASSWORD=.*|VPN_PASSWORD=$vpn_password|" "$env_file" || \
log_error "Failed to update VPN configuration in .env"
else
sed -i -e "s|^VPN_ENABLED=.*|VPN_ENABLED=n|" "$env_file" || \
log_error "Failed to update VPN configuration in .env"
fi fi
if sudo chown -R "$puid":"$pgid" "$install_directory/config"; then # Update docker-compose.yaml
send_success_message "Configuration folder ownership and permissions set successfully ✅" log_info "Updating docker-compose configuration..."
else sed -i "s|<media_service>|$media_service|g" "$filename" || \
send_error_message "Failed to set ownership and permissions for the configuration folder. Check permissions ❌" log_error "Failed to update docker-compose.yaml"
# Configure Plex-specific settings
if [ "$media_service" == "plex" ]; then
log_info "Configuring Plex-specific settings..."
sed -i -e 's|#network_mode: host # plex|network_mode: host # plex|g' \
-e 's|ports: # plex|#ports: # plex|g' \
-e 's|- 8096:8096 # plex|#- 8096:8096 # plex|g' "$filename" || \
log_error "Failed to configure Plex settings"
fi
# Configure VPN settings if enabled
if [ "${setup_vpn,,}" == "y" ]; then
log_info "Configuring VPN settings..."
sed -i -e "s|vpn_service|$vpn_service|g" \
-e "s|vpn_user|$vpn_user|g" \
-e "s|vpn_password|$vpn_password|g" \
-e 's|#network_mode: "service:gluetun"|network_mode: "service:gluetun"|g' \
-e 's|ports: # qbittorrent|#ports: # qbittorrent|g' \
-e 's|ports: # sabnzbd|#ports: # sabnzbd|g' \
-e 's|- 8081:8081 # qbittorrent|#- 8081:8081 # qbittorrent|g' \
-e 's|- 8080:8080 # sabnzbd|#- 8080:8080 # sabnzbd|g' \
-e 's|#- 8080:8080/tcp # gluetun|- 8080:8080/tcp # gluetun|g' \
-e 's|#- 8081:8081/tcp # gluetun|- 8081:8081/tcp # gluetun|g' "$filename" || \
log_error "Failed to configure VPN settings"
fi
# Update YAMS CLI script
log_info "Updating YAMS CLI configuration..."
sed -i -e "s|<filename>|$filename|g" \
-e "s|<custom_file_filename>|$install_directory/docker-compose.custom.yaml|g" \
-e "s|<install_directory>|$install_directory|g" "$yams_script" || \
log_error "Failed to update YAMS CLI script"
}
install_cli() {
echo
log_info "Installing YAMS CLI..."
if sudo cp yams /usr/local/bin/yams && sudo chmod +x /usr/local/bin/yams; then
log_success "YAMS CLI installed successfully ✅"
else
log_error "Failed to install YAMS CLI. Check permissions ❌"
fi
}
set_permissions() {
local dirs=("$media_directory" "$install_directory" "$install_directory/config")
for dir in "${dirs[@]}"; do
log_info "Setting permissions for $dir..."
if [ ! -d "$dir" ]; then
mkdir -p "$dir" || log_error "Failed to create directory $dir"
fi
if sudo chown -R "$puid:$pgid" "$dir"; then
log_success "Permissions set successfully for $dir"
else
log_error "Failed to set permissions for $dir"
fi
done
}
# Prevent running as root
if [[ "$EUID" = 0 ]]; then
log_error "YAMS must run without sudo! Please run with regular permissions"
fi fi
# Check all dependencies
log_info "Checking prerequisites..."
check_dependencies
# Get user information
get_user_info
# Get installation paths
get_installation_paths
# Configure services
configure_media_service
configure_vpn
log_info "Configuring the docker-compose file for user \"$username\" in \"$install_directory\"..."
# Copy and update configuration files
copy_configuration_files
update_configuration_files
log_success "Everything installed correctly! 🎉"
# Start services
log_info "Starting YAMS services..."
log_info "This may take a while..."
if ! docker compose -f "$install_directory/docker-compose.yaml" up -d; then
log_error "Failed to start YAMS services"
fi
# Install CLI and set permissions
echo
log_info "We need your sudo password to install the YAMS CLI and configure permissions..."
install_cli
set_permissions
printf "\033c" printf "\033c"
echo "========================================================" cat << "EOF"
echo " _____ ___ ___ ___ " ========================================================
echo " / /::\ / /\ /__/\ / /\ " _____ ___ ___ ___
echo " / /:/\:\ / /::\ \ \:\ / /:/_ " / /::\ / /\ /__/\ / /\
echo " / /:/ \:\ / /:/\:\ \ \:\ / /:/ /\ " / /:/\:\ / /::\ \ \:\ / /:/_
echo " /__/:/ \__\:| / /:/ \:\ _____\__\:\ / /:/ /:/_ " / /:/ \:\ / /:/\:\ \ \:\ / /:/ /\
echo " \ \:\ / /:/ /__/:/ \__\:\ /__/::::::::\ /__/:/ /:/ /\\" /__/:/ \__\:| / /:/ \:\ _____\__\:\ / /:/ /:/_
echo " \ \:\ /:/ \ \:\ / /:/ \ \:\~~\~~\/ \ \:\/:/ /:/" \ \:\ / /:/ /__/:/ \__\:\ /__/::::::::\ /__/:/ /:/ /\\
echo " \ \:\/:/ \ \:\ /:/ \ \:\ ~~~ \ \::/ /:/ " \ \:\ /:/ \ \:\ / /:/ \ \:\~~\~~\/ \ \:\/:/ /:/
echo " \ \::/ \ \:\/:/ \ \:\ \ \:\/:/ " \ \:\/:/ \ \:\ /:/ \ \:\ ~~~ \ \::/ /:/
echo " \__\/ \ \::/ \ \:\ \ \::/ " \ \::/ \ \:\/:/ \ \:\ \ \:\/:/
echo " \__\/ \__\/ \__\/ " \__\/ \ \::/ \ \:\ \ \::/
echo "========================================================" \__\/ \__\/ \__\/
send_success_message "All done!✅ Enjoy YAMS!" ========================================================
echo "You can check the installation on $install_directory" EOF
echo "========================================================"
echo "Everything should be running now! To check everything running, go to:" log_success "All done!✅ Enjoy YAMS!"
log_info "You can check the installation in $install_directory"
log_info "========================================================"
log_info "Everything should be running now! To check everything running, go to:"
echo echo
running_services_location running_services_location
echo echo
log_info "You might need to wait for a couple of minutes while everything gets up and running"
echo echo
echo "You might need to wait for a couple of minutes while everything gets up and running" log_info "All the service locations are also saved in ~/yams_services.txt"
echo
echo "All the services location are also saved in ~/yams_services.txt"
running_services_location > ~/yams_services.txt running_services_location > ~/yams_services.txt
echo "========================================================"
log_info "========================================================"
echo echo
echo "To configure YAMS, check the documentation at" log_info "To configure YAMS, check the documentation at"
echo "https://yams.media/config" log_info "https://yams.media/config"
echo echo
echo "========================================================" log_info "========================================================"
exit 0 exit 0

321
yams
View File

@ -1,142 +1,231 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
dc="docker compose -f <filename> -f <custom_file_filename>" # Constants
install_directory="<install_directory>" readonly DC="docker compose -f <filename> -f <custom_file_filename>"
readonly INSTALL_DIRECTORY="<install_directory>"
option=${1:-"--help"} readonly TIMEOUT_SECONDS=60
readonly IP_ENDPOINTS=(
help() {
echo "yams - Yet Another Media Server"
echo
echo "Usage: yams [--help|restart|stop|start|destroy|check-vpn|update]"
echo "options:"
echo "--help displays this help message"
echo "restart restarts yams services"
echo "stop stops all yams services"
echo "start starts yams services"
echo "destroy destroy yams services so you can start from scratch"
echo "check-vpn checks if the VPN is working as expected"
echo "update updates YAMS"
}
send_success_message() {
echo -e "$(printf "\e[32m$1\e[0m")"
}
send_error_message() {
echo -e "$(printf "\e[31m$1\e[0m")"
exit 255
}
find_available_ip_endpoint() {
ip_endpoints=(
"https://ipinfo.io/ip" "https://ipinfo.io/ip"
"https://api.ipify.org" "https://api.ipify.org"
"https://checkip.amazonaws.com" "https://checkip.amazonaws.com"
"https://tnedi.me" "https://tnedi.me"
"https://api.myip.la" "https://api.myip.la"
"https://wtfismyip.com/text" "https://wtfismyip.com/text"
) )
for ip in ${ip_endpoints[@]}; do # Color codes for better readability
endpoint=$(curl -s "$ip") readonly RED='\033[0;31m'
if [ "$endpoint" != "" ]; then readonly GREEN='\033[0;32m'
echo $ip readonly YELLOW='\033[1;33m'
break readonly NC='\033[0m' # No Color
fi
done # Available commands
declare -A COMMANDS=(
["--help"]="displays this help message"
["restart"]="restarts yams services"
["stop"]="stops all yams services"
["start"]="starts yams services"
["destroy"]="destroy yams services so you can start from scratch"
["check-vpn"]="checks if the VPN is working as expected"
["backup"]="backs up yams to the destination location"
)
# Functions
log_success() {
echo -e "${GREEN}$1${NC}"
} }
if [ "$option" == "--help" ]; then log_error() {
help echo -e "${RED}$1${NC}" >&2
exit 0 exit 1
fi }
if [ "$option" == "restart" ]; then log_warning() {
$dc stop && $dc up -d echo -e "${YELLOW}$1${NC}"
echo "YAMS is starting. Wait 1 min until all the services are up and running..." }
exit 0
fi
if [ "$option" == "stop" ]; then show_help() {
$dc stop echo "yams - Yet Another Media Server"
exit 0 echo
fi echo "Usage: yams [command] [options]"
echo
echo "Commands:"
for cmd in "${!COMMANDS[@]}"; do
printf "%-25s %s\n" "$cmd" "${COMMANDS[$cmd]}"
done
echo
echo "Examples:"
echo " yams start # Start all YAMS services"
echo " yams backup /path/to/backup # Backup YAMS to specified directory"
}
if [ "$option" == "start" ]; then wait_for_services() {
$dc up -d local wait_time=0
echo "YAMS is starting. Wait 1 min until all the services are up and running..." echo -n "Waiting for services to start"
exit 0
fi
if [ "$option" == "check-vpn" ]; then while [ $wait_time -lt $TIMEOUT_SECONDS ]; do
# Get the total number of services and number of running services
local total_services
local running_services
total_services=$($DC ps --format '{{.Name}}' | wc -l)
running_services=$($DC ps --format '{{.Status}}' | grep -c "Up")
if [ "$total_services" -eq "$running_services" ]; then
echo
log_success "All $total_services services are up and running!"
return 0
fi
# Show progress with count
echo -n "."
sleep 1
((wait_time++))
# Every 10 seconds, show status
if [ $((wait_time % 10)) -eq 0 ]; then
echo
echo -n "$running_services/$total_services services running"
fi
done
echo
log_error "Not all services started within ${TIMEOUT_SECONDS} seconds ($running_services/$total_services running)"
}
find_available_ip_endpoint() {
for endpoint in "${IP_ENDPOINTS[@]}"; do
if curl -s --connect-timeout 5 "$endpoint" > /dev/null; then
echo "$endpoint"
return 0
fi
done
return 1
}
get_ip_with_retries() {
local context=$1 # "local" or "qbittorrent"
local cmd_prefix=""
if [ "$context" = "qbittorrent" ]; then
cmd_prefix="docker exec qbittorrent"
fi
for endpoint in "${IP_ENDPOINTS[@]}"; do
local ip
if [ "$context" = "local" ]; then
ip=$(curl -s --connect-timeout 5 "$endpoint")
else
ip=$($cmd_prefix curl -s --connect-timeout 5 "$endpoint")
fi
if [ -n "$ip" ] && [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "$ip"
return 0
fi
done
return 1
}
check_vpn() {
echo "Getting your IP..." echo "Getting your IP..."
ip_endpoint=$(find_available_ip_endpoint) local your_ip
if [ "$ip_endpoint" == "" ]; then your_ip=$(get_ip_with_retries "local") || log_error "Failed to get your IP address from any endpoint"
send_error_message "No available endpoint to get IP address!" echo "Your IP: $your_ip"
fi
your_ip=$(curl -s $ip_endpoint)
echo "$your_ip"
echo "Your local IP country is $(curl -s https://am.i.mullvad.net/country)"
echo
echo
echo "Getting your qBittorrent IP..."
qbittorrent_ip=$(docker exec qbittorrent sh -c "curl -s $ip_endpoint"); local country
if [ -n "$qbittorrent_ip" ]; then country=$(curl -s --connect-timeout 5 "https://am.i.mullvad.net/country") || log_warning "Couldn't determine your country"
echo "$qbittorrent_ip" [ -n "$country" ] && echo "Your local IP country is $country"
echo "Your country in qBittorrent is $(docker exec -it qbittorrent sh -c 'curl -s https://am.i.mullvad.net/country')"
if [ "$qbittorrent_ip" == "$your_ip" ]; then echo -e "\nGetting your qBittorrent IP..."
send_error_message "Your IPs are the same! qBittorrent is exposing your IP! ⚠️" local qbit_ip
qbit_ip=$(get_ip_with_retries "qbittorrent") || log_error "Failed to get qBittorrent IP from any endpoint"
echo "qBittorrent IP: $qbit_ip"
local qbit_country
qbit_country=$(docker exec qbittorrent curl -s --connect-timeout 5 "https://am.i.mullvad.net/country") || log_warning "Couldn't determine qBittorrent country"
[ -n "$qbit_country" ] && echo "qBittorrent country is $qbit_country"
if [ "$qbit_ip" == "$your_ip" ]; then
log_error "⚠️ WARNING: Your IPs are the same! qBittorrent is exposing your IP!"
else else
send_success_message "Your IPs are different. qBittorrent is masking your IP! ✅ " log_success "✅ Success: Your IPs are different. qBittorrent is masking your IP!"
fi fi
else }
send_error_message "Failed to retrieve qBittorrent IP. Please check your setup. ⚠️"
backup_yams() {
local destination=$1
local backup_date
backup_date=$(date '+%Y-%m-%d-%s')
local backup_file="$destination/yams-backup-$backup_date.tar.gz"
echo "Stopping YAMS services..."
$DC stop > /dev/null 2>&1 || log_error "Failed to stop services"
echo -e "\nBacking up YAMS to $destination..."
echo "This may take a while depending on the size of your installation."
echo "Please wait... ⌛"
# Copy current yams script and create backup
cp "$(which yams)" "$INSTALL_DIRECTORY" || log_warning "Failed to backup yams script"
tar --exclude='transcoding-temp' -czf "$backup_file" -C "$INSTALL_DIRECTORY" . ||
log_error "Failed to create backup archive"
echo -e "\nStarting YAMS services..."
$DC start > /dev/null 2>&1 || log_warning "Failed to restart services"
log_success "Backup completed successfully! 🎉"
echo "Backup file: $backup_file"
}
destroy_yams() {
echo -e "\nWARNING: This will destroy all your YAMS services!"
read -p "Are you sure you want to continue? This is not recoverable! ⚠️ 🚨 [y/N]: " -r
if [[ ${REPLY,,} =~ ^y$ ]]; then
$DC down || log_error "Failed to destroy services"
echo -e "\nYAMS services were destroyed. To restart, run: yams start"
fi fi
fi }
if [ "$option" == "destroy" ]; then main() {
echo local command=${1:-"--help"}
echo local destination=${2:-.}
read -p "Are you sure you want to destroy all your yams services? THIS IS NOT RECOVERABLE! ⚠️ ️🚨 [y/N]: " destroy_now
destroy_now=${destroy_now:-"n"}
if [ "$destroy_now" == "y" ]; then
$dc down
echo
echo
echo "yams services were destroyed. To restart, run: "
echo "\$ yams start"
fi
fi
if [ "$option" == "update" ]; then # Validate and normalize destination path if provided
echo "Updating YAMS..." if [ "$command" = "backup" ]; then
$dc stop destination=$(realpath "$destination") || log_error "Invalid backup destination path"
rm -rf /tmp/yams && mkdir /tmp/yams
wget https://gitlab.com/rogs/yams/-/raw/master/docker-compose.example.yaml -O /tmp/yams/docker-compose.example.yml > /dev/null 2>&1
source $install_directory/.env
filename="$install_directory/docker-compose.yaml"
cp /tmp/yams/docker-compose.example.yml $filename
sed -i -e "s;<media_service>;$MEDIA_SERVICE;g" "$filename"
if [ "$MEDIA_SERVICE" == "plex" ]; then
sed -i -e "s|#network_mode: host # plex|network_mode: host # plex|g" "$filename" \
-e "s|ports: # plex|#ports: # plex|g" $filename \
-e "s|- 8096:8096 # plex|#- 8096:8096 # plex|g" $filename
fi fi
if [ "$VPN_ENABLED" == "y" ]; then case "$command" in
sed -i -e "s;#network_mode: \"service:gluetun\";network_mode: \"service:gluetun\";g" "$filename" \ --help)
-e "s;ports: # qbittorrent;#port: # qbittorrent;g" "$filename" \ show_help
-e "s;- 8080:8080 # qbittorrent;#- 8080:8080 # qbittorrent;g" "$filename" \ ;;
-e "s;#- 8080:8080/tcp # gluetun;- 8080:8080/tcp # gluetun;g" "$filename" restart)
fi $DC stop && $DC up -d
wait_for_services
;;
stop)
$DC stop || log_error "Failed to stop services"
log_success "Services stopped successfully"
;;
start)
$DC up -d || log_error "Failed to start services"
wait_for_services
;;
check-vpn)
check_vpn
;;
destroy)
destroy_yams
;;
backup)
backup_yams "$destination"
;;
*)
log_error "Unknown command: $command\nRun 'yams --help' for usage information"
;;
esac
}
$dc up -d main "$@"
echo "YAMS was updated and it is starting. Wait 1 min until all the services are up and running..."
fi