Compare commits

...
Sign in to create a new pull request.

6 commits
v6 ... v6

Author SHA1 Message Date
c7ce3cb463
I am a little dumb 2025-02-24 22:04:04 +13:00
6339920b8c
Updated e-bin.sh 2025-02-24 21:59:50 +13:00
e2dd9a6f11
Using the link to this repo 2025-02-24 13:59:39 +13:00
a8b71e26f1
Hopefully nailed down why the e-bin script keeps on failing 2025-02-24 12:40:26 +13:00
bea13dd124
Slight adjustments. 2025-01-30 23:28:25 +13:00
0209679e97
Added e-bin.sh 2025-01-30 22:04:20 +13:00
3 changed files with 566 additions and 145 deletions

318
e-bin.sh Normal file
View file

@ -0,0 +1,318 @@
#!/bin/bash
## Colors for output
YELLOW="$(printf '\033[0;33m')"
GREEN="$(printf '\033[0;32m')"
BLUE="$(printf '\033[0;34m')"
CYAN="$(printf '\033[0;36m')"
RED="$(printf '\033[1;31m')"
NC="$(printf '\033[0m')" # No color.
readonly YELLOW GREEN BLUE CYAN RED NC
## Other constants.
readonly BOT_EXECUTABLE="EllieBot"
readonly BIN_DIR="ellie"
readonly BACKUP_DIR="ellie_backups"
readonly CREDS_FILE="$BIN_DIR/data/creds.yml"
readonly CREDS_EXAMPLE_FILE="$BIN_DIR/data/creds_example.yml"
####
# Identify the system architecture and OS, and return a string that represents it.
#
# RETURNS:
# - linux-x64: For Linux x86_64.
# - linux-arm64: For Linux aarch64 or arm64.
# - osx-x64: For macOS x86_64.
# - osx-arm64: For macOS arm64.
# - unsupported: For all other architectures or operating systems.
get_arch() {
local os
os=$(uname -s)
local arch
arch=$(uname -m)
case "$os" in
Linux)
case "$arch" in
x86_64) echo "linux-x64" ;;
aarch64 | arm64) echo "linux-arm64" ;;
*) echo "unsupported" ;;
esac
;;
Darwin)
case "$arch" in
x86_64) echo "osx-x64" ;;
arm64) echo "osx-arm64" ;;
*) echo "unsupported" ;;
esac
;;
*)
echo "unsupported"
;;
esac
}
backup_bot() {
if [ -d $BIN_DIR/data/ ]; then
if [ ! -d $BACKUP_DIR/ ]; then
mkdir $BACKUP_DIR
fi
date_now=$(date +%s)
cp -r $BIN_DIR/data "$BACKUP_DIR/$date_now-data"
echo "${BLUE}Your current data "
fi
}
# TODO: Still needs to move data from the old directory to the new one.
####
# Downloads the latest release, extracts it, and sets up EllieBot's directory. If
# the directory already exists, it will be renamed to `ellie.old`. If
# `ellie.old` already exists, it will be deleted.
#
# PARAMETERS:
# - $1: version (Required)
# - The release version to download.
#
# RETURNS:
# - 1: On failure of some operation.
install_bot() {
local version="$1"
local arch
arch=$(get_arch)
local output="ellie-new"
local tar_url="https://toastielab.dev/toastie_t0ast/elliebot/releases/download/${version}/ellie-${arch}.tar.gz"
[[ $arch == "unsupported" ]] && { echo "${RED}ERROR: Unsupported architecture${NC}" >&2; return 1; }
echo "${BLUE}Downloading '${version}' for '${arch}'...${NC}"
[[ -d $output ]] && rm -r ./$output
mkdir ./$output
if ! curl -L "$tar_url" | tar -xzf - -C ./$output --strip-components=1; then
echo "${RED}ERROR: Failed to download or extract the archive${NC}" >&2
rm -r ./$output
return 1
fi
if [[ -d $BIN_DIR ]]; then
backup_bot
mv "$BIN_DIR" "${BIN_DIR}-old"
else
echo "${BLUE}EllieBot not installed. Installing for the first time.${NC}"
fi
mv ./$output $BIN_DIR
if [[ -d "${BIN_DIR}-old" ]]; then
echo "${BLUE}Copying over data folder...${NC}"
[[ -d "${BIN_DIR}-old/data/" ]] && cp -rf "${BIN_DIR}-old/data/"* "$BIN_DIR/data/"
fi
chmod +x "${BIN_DIR}/${BOT_EXECUTABLE}"
echo "${GREEN}Installation complete!${NC}"
# Clean up any leftover folders
[[ -d $output ]] && rm -r ./$output
[[ -d "${BIN_DIR}-old" ]] && rm -r "${BIN_DIR}-old"
}
# TODO: Somehow get the bot version, and
# paint available versions with color
# RED: older (downgrade), not recommended
# GREEN: newer (upgrade)
# BLUE: same (reinstall)
####
# Display a list of available versions, and prompt the user to select one to install.
install_submenu() {
local versions
# get versions from /repos/tags toastielab endpoint
mapfile -t versions < <(curl -s https://toastielab.dev/api/v1/repos/toastie_t0ast/elliebot/tags | grep -oP '"ref": "refs/tags/\K[^"]+')
echo "${CYAN}Select version to install:${NC}"
select version in "${versions[@]}"; do
if [[ -n $version ]]; then
install_bot "$version"
break
else
echo "${RED}ERROR: Invalid selection${NC}"
fi
done
}
####
# Determines whether the 'token' field in the credentials file is set.
#
# NOTE:
# This is not a comprehensive check for the validity of the token; it only verifies
# that the token field is not empty.
#
# RETURNS:
# - 0: If the token is set.
# - 1: If the token is not set.
is_token_set() {
if grep -Eq '^token: '\'\''' "$CREDS_FILE"; then
return 1
else
return 0
fi
}
####
# Verify that the bot is installed, the token in the 'creds.yml' file is set, and then
# run the bot.
#
# RETURNS:
# - 1: If the bot is not installed, the executable is not found, or the token is not
# set.
run_bot() {
## Ensure that the bot is installed.
if [[ ! -d $BIN_DIR ]]; then
echo "${RED}ERROR: EllieBot not installed. Please install it first.${NC}" >&2
return 1
fi
## Ensures that the executable exists.
if [[ ! -f $BIN_DIR/$BOT_EXECUTABLE ]]; then
echo "${RED}ERROR: EllieBot executable not found${NC}" >&2
return 1
fi
## Create the creds file if it doesn't exist.
if [[ ! -f $CREDS_FILE ]]; then
if [[ ! -f $CREDS_EXAMPLE_FILE ]]; then
echo "${RED}ERROR: 'creds_xample.yml'not found. Make sure the bot is installed${NC}" >&2
return 1
fi
cp -f "$CREDS_EXAMPLE_FILE" "$CREDS_FILE"
fi
## Ensure that the token is set in the creds file or check if env var bot_token is set
if ! is_token_set && [[ -z "${bot_token}" ]]; then
echo "${RED}ERROR: Bot token not set. Please set it in the credentials file or as an environment variable.${NC}" >&2
return 1
fi
echo "${BLUE}Attempting to run EllieBot...${NC}"
pushd "$BIN_DIR" >/dev/null || return 1
./$BOT_EXECUTABLE
popd >/dev/null || return 1
}
install_music_dependencies() {
curl -L -o e-musicreq.sh https://toastielab.dev/toastie_t0ast/ellie-bash-installer/raw/branch/v6/e-prereq.sh || {
echo "ERROR: Failed to download the music dependencies installer"
return 1
}
if [[ ! -f e-musicreq.sh ]]; then
echo "${RED}ERROR: Failed to download the music dependencies installer${NC}" >&2
return 1
fi
chmod +x e-musicreq.sh
./e-musicreq.sh
rm e-musicreq.sh
}
edit_creds() {
if [[ ! -f $CREDS_FILE ]]; then
cp $CREDS_EXAMPLE_FILE $CREDS_FILE
fi
# ask the user to input the token
echo "Please input your token: "
echo ""
read -r token
# check if the token is not empty
if [[ -z "$token" ]]; then
echo "ERROR: Invalid token." >&2
return 1
fi
# replace the token in the creds file
# by finding a line which starts with 'token: ' and replacing it
sed -i "s/token: .*/token: \"$token\"/" $CREDS_FILE
}
migrate_from_v5() {
# 0. Ensure that the bot is installed
if [[ ! -d $BIN_DIR ]]; then
echo "${RED}ERROR: EllieBot v6 not installed. Please install it first and then you'll be able to migrate.${NC}"
return 1
fi
# 1. Check if there is a elliebot/output folder
if [ ! -d "elliebot/output" ]; then
echo "${RED}ERROR: No v5 installation found.${NC}"
return 1
fi
# 2. Copy over the entirety of the data folder and overwrite
if [ -d "elliebot/output/data/" ]; then
# strings are no longer there, nor do we need them
rm -rf elliebot/output/data/strings
cp -rf elliebot/output/data/* ellie/data/
# move the data folder to avoid double migration
mv elliebot/output/data elliebot/output/data.old
fi
# Done
echo "${GREEN}Migration successful! Your v6 bot is now in the 'ellie' folder.${NC}"
MIGRATED=1
}
####
# Display the main menu options.
display_menu() {
clear
echo "${BLUE}===== EllieBot Release Installer =====${NC}"
echo "1. Install"
echo "2. Run"
echo "3. Add Token"
echo "4. Install music dependencies"
echo "5. Exit"
if [[ ! $MIGRATED -eq 1 && -d "elliebot/output/data" ]]; then
echo "6. Migrate v5 from-source version"
fi
echo -n "${CYAN}Enter your choice:${NC} "
}
MIGRATED=1
# check if there is a elliebot/output/data folder
if [ ! -d "elliebot/output/data" ]; then
MIGRATED=0
fi
while true; do
display_menu
read -r choice
case $choice in
1) install_submenu ;;
2) run_bot ;;
3) edit_creds ;;
4) install_music_dependencies ;;
5)
echo "${GREEN}Exiting...${NC}"
exit 0
;;
6)
if [[ $MIGRATED -eq 1 || ! -d "elliebot/output/data" ]]; then
echo "${YELLOW}WARNING: Nothing to migrate. You must have a v5 elliebot/output folder!${NC}" >&2
break
fi
migrate_from_v5
;;
*) echo "${RED}ERROR: Invalid option${NC}" ;;
esac
echo "${CYAN}Press [Enter] to continue${NC}"
read -r
done

13
e-install.sh Normal file
View file

@ -0,0 +1,13 @@
#!/bin/sh
echo ""
echo "Welcome to EllieBot."
echo "Downloading the latest installer..."
root=$(pwd)
rm "$root/e-bin.sh" 1>/dev/null 2>&1
curl -L -o "$root/e-bin.sh" https://toastielab.dev/toastie_t0ast/ellie-bash-installer/raw/branch/v6/e-bin.sh
bash e-bin.sh
cd "$root"
rm "$root/e-bin.sh"
exit 0

View file

@ -1,163 +1,253 @@
#!/bin/bash -e
# Install dotnet
root=$(pwd)
echo ""
#!/bin/bash
function INSTALL_YTDLP {
wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O ~/.local/bin/yt-dlp
chmod a+rx ~/.local/bin/yt-dlp
if [ "$EUID" -ne 0 ]; then
sudo_cmd="sudo "
fi
declare -A -r APT_CMD=(
["update"]="${sudo_cmd}apt update"
["install"]="${sudo_cmd}apt install -y"
)
declare -A -r DNF_CMD=(
["update"]="${sudo_cmd}dnf update -y"
["install"]="${sudo_cmd}dnf install -y"
)
declare -A -r ZYPPER_CMD=(
["update"]="${sudo_cmd}zypper refresh"
["install"]="${sudo_cmd}zypper install -y"
)
declare -A -r BREW_CMD=(
["update"]="${sudo_cmd}brew update"
["install"]="${sudo_cmd}brew install"
)
declare -A -r PACMAN_CMD=(
["update"]="${sudo_cmd}pacman -Sy"
["install"]="${sudo_cmd}pacman -Sy --noconfirm"
)
readonly YT_DLP_PATH="$HOME/.local/bin/yt-dlp"
####
# Identify the system's distribution, version, and architecture.
#
# NOTE:
# The 'os-release' file is used to determine the distribution and version. This file
# is present on almost every distributions running systemd.
#
# NEW GLOBALS:
# - DISTRO: The distribution name.
# - VER: The distribution version.
# - SVER: The distribution version without the minor version.
# - ARCH: The system architecture.
# - BITS: The system architecture in bits.
detect_sys_info() {
if [[ $(uname -s) == "Darwin" ]]; then
DISTRO="darwin"
VER=$(sw_vers -productVersion)
elif [[ -f /etc/os-release ]]; then
. /etc/os-release
DISTRO="$ID"
VER="$VERSION_ID" # Version: x.x.x...
SVER=${VER//.*/} # Version: x
else
DISTRO=$(uname -s)
VER=$(uname -r)
fi
case $(uname -m) in
x86_64) BITS="64"; ARCH="x64" ;;
aarch64) BITS="64"; ARCH="arm64" ;;
armv*) BITS="32"; ARCH="arm32" ;; # Generic ARM 32-bit.
i*86) BITS="32"; ARCH="x86" ;;
*) BITS="?"; ARCH="$(uname -m)" ;; # Fallback to uname output.
esac
}
wget -q -N https://toastielab.dev/EllieBotDevs/ellie-bash-installer/raw/branch/v5/detectOS.sh
declare DETECTED=($(bash detectOS.sh))
####
# Install 'yt-dlp' at '~/.local/bin/yt-dlp'.
#
# EXITS:
# - 1: Failed to download 'yt-dlp'.
install_yt_dlp() {
if [[ ${DETECTED[0]} = "" ]]; then exit 1; fi
OS="${DETECTED[0]}"
VER="${DETECTED[1]}"
ARCH="${DETECTED[2]}"
SVER="${DETECTED[3]}"
echo "This installer will download all of the required packages for EllieBot. It will use about 350MB of space. This might take awhile to download if you do not have a good internet connection.\n"
echo -e "Would you like to continue? \nYour OS: $OS \nOS Version: $VER \nArchitecture: $ARCH"
while true; do
read -p "[y/n]: " yn
case $yn in
[Yy]* ) clear; echo Running EllieBot Auto-Installer; sleep 2; break;;
[Nn]* ) echo Quitting...; rm e-prereq.sh && exit;;
* ) echo "Couldn't get that please type [y] for Yes or [n] for No.";;
esac
done
echo ""
if [ "$OS" = "Ubuntu" ]; then
if [ "$VER" = "23.10" ]; then
echo -e "*Ubuntu 23.10 will reach End Of Life (EOL) on July 01, 2024. For more information, see the official Ubuntu EOL page. "
if command -v yt-dlp &>/dev/null; then
echo "${BLUE}Updating 'yt-dlp'...${NC}"
yt-dlp -U || {
echo "${RED}Failed to update 'yt-dlp'${NC}" >&2
}
return
fi
echo "Installing dotnet"
wget "https://packages.microsoft.com/config/ubuntu/$VER/packages-microsoft-prod.deb" -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update;
sudo apt-get install -y apt-transport-https && sudo apt-get update;
sudo apt-get install -y dotnet-sdk-8.0;
# get correct yt-dlp based on arch
local yt_dlp_url="https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux"
case $ARCH in
x64) yt_dlp_url="${yt_dlp_url}" ;;
arm64) yt_dlp_url="${yt_dlp_url}_aarch64" ;;
arm32) yt_dlp_url="${yt_dlp_url}_armv7l" ;;
*) echo "${RED}Unsupported architecture: $ARCH${NC}" >&2; return 1 ;;
esac
echo "Installing Git and Tmux..."
sudo apt-get install git tmux -y
[[ ! -d "$HOME/.local/bin" ]] && mkdir -p "$HOME/.local/bin"
echo "Installing music prerequisites..."
sudo apt-get install libopus0 opus-tools libopus-dev libsodium-dev python ffmpeg -y
echo ""
INSTALL_YTDLP
if [[ ! -f $YT_DLP_PATH ]]; then
echo "${BLUE}Installing 'yt-dlp'...${NC}"
curl -L "$yt_dlp_url" -o "$YT_DLP_PATH" || {
echo "${RED}Failed to download 'yt-dlp'${NC}" >&2
return 1
}
fi
elif [ "$OS" = "Debian" ]; then
if [[ "$SVER" == "9" ]]; then
echo "Support for Debian 9 has reached End of Life (EOL) as of August 9, 2022"
echo "Please upgrade to Debian 10 or newer"
rm e-prereq.sh
echo "${BLUE}Modifying permissions for 'yt-dlp'...${NC}"
chmod a+rx "$YT_DLP_PATH"
}
####
# Installs music prerequisites.
#
# PARAMETERS:
# - $1: cmd_array (Required)
# - The associative array containing the package manager commands.
# - $2: pkg_list (Required)
# - The array containing the list of packages to install.
install_music_prereqs() {
local -n cmd_array="$1"
local -n pkg_list="$2"
echo -e "${YELLOW}About to run: ${cmd_array["update"]}${NC}"
read -p "${CYAN}Continue? [y/N]: ${NC}" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
${cmd_array["update"]} || {
echo "${RED}Failed to update${NC}" >&2
exit 1
}
else
echo "${RED}Update skipped${NC}"
fi
echo -e "${YELLOW}About to run: ${cmd_array["install"]} ${pkg_list[*]}${NC}"
read -p "${CYAN}Continue? [y/N]: ${NC}" -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
${cmd_array["install"]} "${pkg_list[@]}" || {
echo "${RED}Failed to install music prerequisites${NC}" >&2
exit 1
}
else
echo "${RED}Installation cancelled${NC}"
exit 1
fi
echo "Installing dotnet..."
wget https://packages.microsoft.com/config/debian/"$SVER"/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
install_yt_dlp
sudo apt-get update; \
sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-8.0
echo -e "\n${GREEN}Finished installing music prerequisites${NC}"
}
echo "Installing Git and Tmux..."
sudo apt-get install git tmux -y
detect_sys_info
echo "Installing music prerequisites..."
sudo apt-get install libopus0 libopus-dev libsodium-dev ffmpeg -y
echo ""
INSTALL_YTDLP
elif [ "$OS" = "Fedora" ]; then
sudo dnf -y install dotnet-sdk-8.0
sudo dnf -y install git tmux
sudo dnf -y install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
sudo dnf -y install ffmpeg
sudo dnf -y install opus-tools opus libsodium
INSTALL_YTDLP
elif [ "$OS" = "openSUSE Leap" ] || [ "$OS" = "openSUSE Tumbleweed" ]; then
echo -e "Installing dotnet..."
sudo zypper install -y libicu wget
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
wget https://packages.microsoft.com/config/opensuse/15/prod.repo
sudo mv prod.repo /etc/zypp/repos.d/microsoft-prod.repo
sudo chown root:root /etc/zypp/repos.d/microsoft-prod.repo
sudo zypper install -y dotnet-sdk-8.0
echo -e "\nInstalling git, tmux..."
sudo zypper install -y git tmux
echo -e "\nInstalling music prerequisites..."
sudo zypper install -y ffmpeg libopus0 yt-dlp
elif [ "$OS" = "LinuxMint" ]; then
echo "Installing Git and Tmux..."
sudo apt-get update;
sudo apt-get install -y git tmux
echo "Installing dotnet..."
if [ "$SVER" = "19" ] || [ "$SVER" = "20" ]; then
wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
elif [ "$SVER" = "21" ]; then
wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
fi
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-8.0
sudo apt-get install -y apt-transport-https && \
sudo apt-get update;
echo "Installing music prerequisites..."
sudo apt-get install -y libopus0 opus-tools libopus-dev libsodium-dev ffmpeg
INSTALL_YTDLP
elif [ "$OS" = "AlmaLinux" ] || [ "$OS" = "Rocky Linux" ]; then
echo "Installing dotnet..."
sudo dnf install -y dotnet-sdk-8.0
echo "Installing Git and Tmux..."
sudo dnf install -y wget git opus tmux python3.11
echo "Installing music prerequisites..."
if [ "$SVER" = "8" ]; then
sudo dnf -y install https://download.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
sudo yum install yum-utils -y
sudo yum-config-manager --enable powertools
fi
sudo dnf install -y --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm
sudo dnf install -y ffmpeg
INSTALL_YTDLP
elif [ "$OS" = "Darwin" ]; then
brew update
brew install wget git ffmpeg openssl opus opus-tools opusfile libffi libsodium tmux python yt-dlp
brew install mono-libgdiplus
if [[ $BITS == "32" ]]; then
echo "${RED}Your system is 32-bit, which is unsupported${NC}" >&2
exit 1
fi
echo
echo "EllieBot Prerequisites Installation completed..."
read -n 1 -s -p "Press any key to continue..."
sleep 2
case "$DISTRO" in
ubuntu)
readonly pkgs=(ffmpeg curl)
case "$VER" in
22.04|24.04) install_music_prereqs APT_CMD pkgs ;;
*) unsupported ;;
esac
;;
debian)
readonly pkgs=(ffmpeg curl)
case "$SVER" in
1[1-3]) install_music_prereqs APT_CMD pkgs ;;
*) unsupported ;;
esac
;;
linuxmint)
readonly pkgs=(ffmpeg curl)
case "$SVER" in
2[0-3]) install_music_prereqs APT_CMD pkgs ;;
*) unsupported ;;
esac
;;
fedora)
readonly pkgs=(ffmpeg-free curl)
case "$SVER" in
38|39|40|41|42|43) install_music_prereqs DNF_CMD pkgs ;;
*) unsupported ;;
esac
;;
almalinux|rocky)
el_ver=$(rpm -E %rhel)
cd "$root"
rm "$root/e-prereq.sh"
exit 0
# add rpm fusion repo
repo_cmd=(
"${sudo_cmd}dnf install -y epel-release"
"sudo dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-${el_ver}.noarch.rpm -y"
)
if (( el_ver == 8 )); then
repo_cmd+=("${sudo_cmd}dnf config-manager --set-enabled powertools")
elif (( el_ver == 9 )); then
repo_cmd+=("${sudo_cmd}dnf config-manager --set-enabled crb")
fi
echo "${YELLOW}In order to install ffmpeg, you need to enable the RPM Fusion repository.${NC}"
echo "${YELLOW}The following commands will be run:${NC}"
for cmd in "${repo_cmd[@]}"; do
echo " ${YELLOW}-> $cmd${NC}"
done
# ask the user whether it's ok to run this command
read -p "${CYAN}Do you want to continue with these commands? [y/N]: ${NC}" -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
for cmd in "${repo_cmd[@]}"; do
echo "${BLUE}Running: $cmd${NC}"
$cmd || {
echo "${RED}Failed to run: $cmd${NC}" >&2
exit 1
}
done
else
echo "${RED}User opted to cancel the command execution.${NC}" >&2
exit 1
fi
readonly pkgs=(ffmpeg curl)
install_music_prereqs DNF_CMD pkgs
;;
opensuse-leap)
readonly pkgs=(ffmpeg curl)
case "$VER" in
15.5|15.6|15.7) install_music_prereqs ZYPPER_CMD pkgs ;;
*) unsupported ;;
esac
;;
opensuse-tumbleweed)
readonly pkgs=(ffmpeg curl)
install_music_prereqs ZYPPER_CMD pkgs ;;
arch|artix)
readonly pkgs=(ffmpeg pipewire-jack curl)
install_music_prereqs PACMAN_CMD pkgs
;;
darwin)
if ! command -v brew &>/dev/null; then
echo "${RED}Homebrew is not installed. Please install Homebrew first.${NC}" >&2
exit 1
fi
readonly pkgs=(ffmpeg python curl)
install_music_prereqs BREW_CMD pkgs
;;
*)
echo "${RED}Unsupported distribution: $DISTRO${NC}" >&2
exit 1
;;
esac