#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" PROJECT_DIR="${PROJECT_DIR:-$SCRIPT_DIR}" VENV_DIR="${VENV_DIR:-$PROJECT_DIR/venv}" REQ_IN="${REQ_IN:-$PROJECT_DIR/requirements.in}" REQ_TXT="${REQ_TXT:-$PROJECT_DIR/requirements.txt}" PYTHON_BIN="${PYTHON_BIN:-python3}" BEETS_CONFIG="${BEETS_CONFIG:-$PROJECT_DIR/config/config.yaml}" LASTGENRE_WHITELIST="${LASTGENRE_WHITELIST:-$PROJECT_DIR/config/lastgenre-whitelist.txt}" CHECK_AUDIO_DECODE="${CHECK_AUDIO_DECODE:-$PROJECT_DIR/scripts/audit/check-audio-decode}" GAPLESS_CONVERT="${GAPLESS_CONVERT:-$PROJECT_DIR/scripts/convert/gapless-convert}" die() { echo "error: $*" >&2 exit 1 } warn() { echo "warning: $*" >&2 } have_cmd() { command -v "$1" >/dev/null 2>&1 } need_file() { [[ -f "$1" ]] || die "missing required file: $1" } need_executable_file() { [[ -x "$1" ]] || die "missing required executable: $1" } find_system_pip_compile() { local path_entry candidate local -a path_parts IFS=':' read -ra path_parts <<< "$PATH" for path_entry in "${path_parts[@]}"; do [[ -z "$path_entry" ]] && continue candidate="$path_entry/pip-compile" [[ -x "$candidate" ]] || continue # Do not use pip-compile from this project's venv. if [[ "$candidate" == "$VENV_DIR/bin/pip-compile" ]]; then continue fi # Do not use pip-compile from the currently active venv either. if [[ -n "${VIRTUAL_ENV:-}" && "$candidate" == "$VIRTUAL_ENV/bin/pip-compile" ]]; then continue fi printf '%s\n' "$candidate" return 0 done return 1 } check_system_dependencies() { local missing=0 local cmd for cmd in python3 ffmpeg ffprobe fpcalc flac mp3val; do if ! have_cmd "$cmd"; then echo "missing command: $cmd" >&2 missing=1 fi done # ImageMagick: # # Modern ImageMagick usually exposes "magick". # Beets documentation historically says it looks for "convert". # Accept either, but make the exact situation visible. if have_cmd magick; then : elif have_cmd convert; then : else echo "missing command: magick or convert from ImageMagick" >&2 missing=1 fi if have_cmd magick && ! have_cmd convert; then warn "found 'magick' but not 'convert'; if beets cannot use ImageMagick, install/enable the ImageMagick 'convert' compatibility command or rely on Pillow where possible" fi if (( missing != 0 )); then die "missing required system dependencies" fi } check_ffmpeg_capabilities() { if ! ffmpeg -hide_banner -encoders 2>/dev/null \ | awk '$2 == "libopus" { found = 1 } END { exit found ? 0 : 1 }' then die "ffmpeg is installed, but it does not appear to have the libopus encoder required by your convert.opus command" fi } check_project_files() { need_file "$REQ_IN" # These are referenced directly by your beets config. need_executable_file "$CHECK_AUDIO_DECODE" need_executable_file "$GAPLESS_CONVERT" need_file "$LASTGENRE_WHITELIST" if [[ ! -f "$BEETS_CONFIG" ]]; then warn "beets config not found at $BEETS_CONFIG; skipping beets config validation" fi } create_venv_if_missing() { if [[ ! -d "$VENV_DIR" ]]; then echo "Creating virtualenv: $VENV_DIR" "$PYTHON_BIN" -m venv "$VENV_DIR" fi if [[ ! -x "$VENV_DIR/bin/python" ]]; then die "venv exists but does not contain executable Python: $VENV_DIR/bin/python" fi } compile_requirements() { local pip_compile_bin pip_compile_bin="${PIP_COMPILE_BIN:-}" if [[ -z "$pip_compile_bin" ]]; then pip_compile_bin="$(find_system_pip_compile || true)" fi if [[ -z "$pip_compile_bin" ]]; then die "could not find system-wide pip-compile; install pip-tools outside this project venv" fi if [[ ! -x "$pip_compile_bin" ]]; then die "configured PIP_COMPILE_BIN is not executable: $pip_compile_bin" fi echo "Using pip-compile: $pip_compile_bin" "$pip_compile_bin" --upgrade "$REQ_IN" -o "$REQ_TXT" } install_requirements() { echo "Installing requirements into: $VENV_DIR" "$VENV_DIR/bin/python" -m pip install --upgrade pip setuptools wheel "$VENV_DIR/bin/python" -m pip install -r "$REQ_TXT" "$VENV_DIR/bin/python" -m pip check } validate_beets() { if [[ ! -f "$BEETS_CONFIG" ]]; then return 0 fi if [[ ! -x "$VENV_DIR/bin/beet" ]]; then die "beet was not installed into the venv: $VENV_DIR/bin/beet" fi echo "Validating beets config: $BEETS_CONFIG" "$VENV_DIR/bin/beet" -c "$BEETS_CONFIG" config >/dev/null } main() { check_system_dependencies check_ffmpeg_capabilities check_project_files create_venv_if_missing compile_requirements install_requirements validate_beets echo "Done." } main "$@"