build_linux.sh: WITH_AI builds a onedir VoiceGrab-AI (instant start)

Onedir avoids re-extracting ~1.4 GB of torch on every startup that the
onefile layout would require (plan §5 option B: the AI build is the
isolated sidecar artifact). Core build stays onefile/dist/VoiceGrab.
This commit is contained in:
VoiceGrab 2026-08-25 21:08:19 -06:00
parent c2126f2045
commit 4e2a997ec3
2 changed files with 29 additions and 10 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ __pycache__/
dist/ dist/
build/ build/
third_party/ third_party/
VoiceGrab.spec
VoiceGrab-AI.spec

View File

@ -1,6 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# VoiceGrab — Linux build (produces dist/VoiceGrab-<arch> binary; AppImage if linuxdeploy available) # VoiceGrab — Linux build
# WITH_AI=1 ./installer/build_linux.sh → also bundle torch+demucs (bigger build) # ./installer/build_linux.sh → core app, onefile (dist/VoiceGrab)
# WITH_AI=1 ./installer/build_linux.sh → AI voice separation (torch+demucs)
# onedir (dist/VoiceGrab-AI/)
#
# Why onedir for the AI build: bundling torch into a onefile blob makes every
# startup re-extract ~2 GB (60120 s). A onedir app starts instantly and the
# directory doubles as the plan's "sidecar" artifact.
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")/.." cd "$(dirname "$0")/.."
@ -8,13 +14,18 @@ PY="${PYTHON:-.venv/bin/python}"
[ -x "$PY" ] || { echo "Create the venv first: python3 -m venv .venv && .venv/bin/pip install -r requirements.txt"; exit 1; } [ -x "$PY" ] || { echo "Create the venv first: python3 -m venv .venv && .venv/bin/pip install -r requirements.txt"; exit 1; }
command -v ffmpeg >/dev/null || { echo "ffmpeg not found in PATH (needed to resolve the binary)"; exit 1; } command -v ffmpeg >/dev/null || { echo "ffmpeg not found in PATH (needed to resolve the binary)"; exit 1; }
"$PY" -m pip install pyinstaller "$PY" -m pip install -q pyinstaller
if [ "${WITH_AI:-0}" = "1" ]; then if [ "${WITH_AI:-0}" = "1" ]; then
echo "Building with AI voice separation (torch+demucs)…" echo "Building WITH AI voice separation (torch+demucs, onedir)…"
"$PY" -m pip install -r requirements-ai.txt "$PY" -m pip install -q -r requirements-ai.txt
AI_FLAGS=(--collect-all demucs --collect-all torch) AI_FLAGS=(--collect-all demucs --collect-all torch)
LAYOUT=--onedir
NAME="VoiceGrab-AI"
else else
AI_FLAGS=() AI_FLAGS=()
LAYOUT=--onefile
NAME="VoiceGrab"
fi fi
FFMPEG_BIN="$(command -v ffmpeg)" FFMPEG_BIN="$(command -v ffmpeg)"
@ -25,17 +36,23 @@ if [ -n "${FFPROBE_BIN:-}" ]; then
EXTRA+=(--add-binary "$FFPROBE_BIN:.") EXTRA+=(--add-binary "$FFPROBE_BIN:.")
fi fi
"$PY" -m PyInstaller --noconfirm --onefile --windowed --name VoiceGrab \ "$PY" -m PyInstaller --noconfirm --clean --windowed \
--name "$NAME" $LAYOUT \
--add-binary "$FFMPEG_BIN:." \ --add-binary "$FFMPEG_BIN:." \
"${EXTRA[@]}" \ ${EXTRA[@]+"${EXTRA[@]}"} \
"${AI_FLAGS[@]}" \ ${AI_FLAGS[@]+"${AI_FLAGS[@]}"} \
--add-data "assets/icon.png:assets" \ --add-data "assets/icon.png:assets" \
--icon assets/icon.ico \ --icon assets/icon.ico \
voicegrab.py voicegrab.py
echo echo
echo "Binary: dist/VoiceGrab" if [ "$LAYOUT" = "--onedir" ]; then
echo "Test: ./dist/VoiceGrab" echo "App dir: dist/$NAME/"
echo "Test: dist/$NAME/$NAME"
else
echo "Binary: dist/$NAME"
echo "Test: ./dist/$NAME"
fi
# Optional: wrap in an AppImage # Optional: wrap in an AppImage
if command -v linuxdeploy >/dev/null 2>&1; then if command -v linuxdeploy >/dev/null 2>&1; then