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 #!/bin/bash
# Install dotnet
root=$(pwd)
echo ""
function INSTALL_YTDLP { if [ "$EUID" -ne 0 ]; then
wget https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -O ~/.local/bin/yt-dlp sudo_cmd="sudo "
chmod a+rx ~/.local/bin/yt-dlp 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 if command -v yt-dlp &>/dev/null; then
echo "${BLUE}Updating 'yt-dlp'...${NC}"
OS="${DETECTED[0]}" yt-dlp -U || {
VER="${DETECTED[1]}" echo "${RED}Failed to update 'yt-dlp'${NC}" >&2
ARCH="${DETECTED[2]}" }
SVER="${DETECTED[3]}" return
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. "
fi 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; # get correct yt-dlp based on arch
sudo apt-get install -y apt-transport-https && sudo apt-get update; local yt_dlp_url="https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux"
sudo apt-get install -y dotnet-sdk-8.0; 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..." [[ ! -d "$HOME/.local/bin" ]] && mkdir -p "$HOME/.local/bin"
sudo apt-get install git tmux -y
echo "Installing music prerequisites..." if [[ ! -f $YT_DLP_PATH ]]; then
sudo apt-get install libopus0 opus-tools libopus-dev libsodium-dev python ffmpeg -y echo "${BLUE}Installing 'yt-dlp'...${NC}"
echo "" curl -L "$yt_dlp_url" -o "$YT_DLP_PATH" || {
INSTALL_YTDLP echo "${RED}Failed to download 'yt-dlp'${NC}" >&2
return 1
}
fi
elif [ "$OS" = "Debian" ]; then echo "${BLUE}Modifying permissions for 'yt-dlp'...${NC}"
if [[ "$SVER" == "9" ]]; then chmod a+rx "$YT_DLP_PATH"
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 ####
# 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 exit 1
fi fi
echo "Installing dotnet..." install_yt_dlp
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
sudo apt-get update; \ echo -e "\n${GREEN}Finished installing music prerequisites${NC}"
sudo apt-get install -y apt-transport-https && \ }
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-8.0
echo "Installing Git and Tmux..." detect_sys_info
sudo apt-get install git tmux -y
echo "Installing music prerequisites..." if [[ $BITS == "32" ]]; then
sudo apt-get install libopus0 libopus-dev libsodium-dev ffmpeg -y echo "${RED}Your system is 32-bit, which is unsupported${NC}" >&2
echo "" exit 1
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
fi fi
echo case "$DISTRO" in
echo "EllieBot Prerequisites Installation completed..." ubuntu)
read -n 1 -s -p "Press any key to continue..." readonly pkgs=(ffmpeg curl)
sleep 2 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" # add rpm fusion repo
rm "$root/e-prereq.sh" repo_cmd=(
exit 0 "${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