188 lines
4.9 KiB
Bash
Executable File
188 lines
4.9 KiB
Bash
Executable File
#!/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 "$@"
|