Files
transcriber/mac.sh
T

219 lines
7.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Local AI Image Generator - macOS Launcher
# Double-click or run: ./mac.sh
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_DIR="$SCRIPT_DIR/app"
PLATFORM="$(uname -s)"
if [[ "$PLATFORM" != "Darwin" ]]; then
echo "[ERROR] This script is for macOS only. Please run ./linux.sh on Linux." >&2
exit 1
fi
NODE_DIR="$APP_DIR/tools/node-mac"
NODE_BIN="$NODE_DIR/bin/node"
BACKEND_PATH="$APP_DIR/backend/mac/sd"
if [[ "$(uname -m)" == "arm64" ]]; then
LLM_BACKEND_PATH="$APP_DIR/llm-backend/mac/arm64/llama-server"
else
LLM_BACKEND_PATH="$APP_DIR/llm-backend/mac/x64/llama-server"
fi
TTS_RUNTIME_PATH="$APP_DIR/tts-runtime/node_modules/kokoro-js"
PLATFORM_LABEL="macOS"
DIST_INDEX="$APP_DIR/dist/index.html"
SETUP_SCRIPT="$SCRIPT_DIR/scripts/setup.sh"
SERVE_SCRIPT="$SCRIPT_DIR/scripts/serve.cjs"
FRONTEND_PORT="${FRONTEND_PORT:-1420}"
LLM_PORT="${LLM_PORT:-10086}"
SETUP_REASON=""
SETUP_MODE="Repair"
# ── Setup node_modules to avoid OS conflicts ────────────────────────────────
FRONTEND_NODE_MODULES="$APP_DIR/frontend/node_modules"
MAC_NODE_MODULES="$APP_DIR/frontend/node_modules_mac"
ACTIVE_OS_FILE="$APP_DIR/frontend/.active_modules_os"
# Attempt to create a test symlink to check if filesystem supports symlinks
USE_SYMLINKS=true
TEST_LINK="$APP_DIR/frontend/.test_symlink"
rm -f "$TEST_LINK"
if ln -s "node_modules_mac" "$TEST_LINK" 2>/dev/null; then
rm -f "$TEST_LINK"
else
USE_SYMLINKS=false
fi
if [ "$USE_SYMLINKS" = true ]; then
if [[ -d "$FRONTEND_NODE_MODULES" && ! -L "$FRONTEND_NODE_MODULES" ]]; then
echo " >> Migrating existing node_modules to node_modules_mac..."
mv "$FRONTEND_NODE_MODULES" "$MAC_NODE_MODULES"
fi
rm -f "$FRONTEND_NODE_MODULES"
mkdir -p "$MAC_NODE_MODULES"
ln -sf "node_modules_mac" "$FRONTEND_NODE_MODULES"
else
# Fallback: Filesystem does not support symlinks (e.g. FAT32/exFAT)
echo " >> Filesystem does not support symlinks. Using directory swapping fallback..."
if [[ -L "$FRONTEND_NODE_MODULES" || -f "$FRONTEND_NODE_MODULES" ]]; then
rm -f "$FRONTEND_NODE_MODULES"
fi
PREV_OS=""
if [[ -f "$ACTIVE_OS_FILE" ]]; then
PREV_OS=$(cat "$ACTIVE_OS_FILE")
fi
if [[ -d "$FRONTEND_NODE_MODULES" && "$PREV_OS" != "mac" ]]; then
if [[ -n "$PREV_OS" ]]; then
echo " >> Swapping out node_modules to node_modules_$PREV_OS..."
rm -rf "$APP_DIR/frontend/node_modules_$PREV_OS"
mv "$FRONTEND_NODE_MODULES" "$APP_DIR/frontend/node_modules_$PREV_OS"
else
echo " >> Saving node_modules as node_modules_windows..."
rm -rf "$APP_DIR/frontend/node_modules_windows"
mv "$FRONTEND_NODE_MODULES" "$APP_DIR/frontend/node_modules_windows"
fi
fi
if [[ -d "$MAC_NODE_MODULES" && ! -d "$FRONTEND_NODE_MODULES" ]]; then
echo " >> Swapping in node_modules_mac..."
mv "$MAC_NODE_MODULES" "$FRONTEND_NODE_MODULES"
elif [[ ! -d "$FRONTEND_NODE_MODULES" ]]; then
mkdir -p "$FRONTEND_NODE_MODULES"
fi
echo "mac" > "$ACTIVE_OS_FILE"
fi
# ── First-time setup check ─────────────────────────────────────────────────
if [[ ! -d "$NODE_DIR" ]]; then
SETUP_MODE="First-Time Setup"
fi
if [[ ! -x "$NODE_BIN" ]]; then
SETUP_REASON="Portable Node.js for macOS is missing."
fi
if [[ ! -f "$DIST_INDEX" ]]; then
SETUP_REASON="Frontend build is missing."
fi
if [[ ! -x "$BACKEND_PATH" ]]; then
SETUP_REASON="No macOS Metal backend binary is installed."
fi
if [[ ! -x "$LLM_BACKEND_PATH" ]]; then
SETUP_REASON="No macOS llama.cpp text backend is installed."
fi
if [[ ! -d "$TTS_RUNTIME_PATH" ]]; then
SETUP_REASON="Kokoro text-to-speech runtime is missing."
fi
if [[ -n "$SETUP_REASON" ]]; then
echo ""
echo " ============================================================"
echo " LOCAL AI IMAGE GENERATOR | $PLATFORM_LABEL $SETUP_MODE"
echo " ============================================================"
echo ""
if [[ "$SETUP_MODE" == "First-Time Setup" ]]; then
echo " This looks like your first run on macOS. Setting up automatically..."
else
echo " Local AI Image Generator needs a quick repair before launch."
fi
echo " Reason: $SETUP_REASON"
echo " Models are not downloaded during setup. Download or import them in the app."
echo ""
read -rp " Press Enter to continue, or Ctrl+C to cancel."
# Clear any existing frontend and backend server processes
if command -v lsof >/dev/null 2>&1; then
lsof -t -i:"${FRONTEND_PORT}" -i:8080 -i:"${LLM_PORT}" | xargs kill -9 >/dev/null 2>&1 || true
elif command -v fuser >/dev/null 2>&1; then
fuser -k "${FRONTEND_PORT}/tcp" >/dev/null 2>&1 || true
fuser -k "8080/tcp" >/dev/null 2>&1 || true
fuser -k "${LLM_PORT}/tcp" >/dev/null 2>&1 || true
fi
if ! bash "$SETUP_SCRIPT"; then
echo ""
echo " [ERROR] Setup failed. Please check the output above."
read -rp " Press Enter to close..."
exit 1
fi
fi
# ── Launch ─────────────────────────────────────────────────────────────────
clear 2>/dev/null || true
echo ""
echo " ============================================================"
echo " LOCAL AI IMAGE GENERATOR | Launching..."
echo " ============================================================"
echo ""
# Clear frontend and backend ports
if command -v lsof >/dev/null 2>&1; then
lsof -t -i:"${FRONTEND_PORT}" -i:8080 -i:"${LLM_PORT}" | xargs kill -9 >/dev/null 2>&1 || true
elif command -v fuser >/dev/null 2>&1; then
fuser -k "${FRONTEND_PORT}/tcp" >/dev/null 2>&1 || true
fuser -k "8080/tcp" >/dev/null 2>&1 || true
fuser -k "${LLM_PORT}/tcp" >/dev/null 2>&1 || true
fi
# Start the server
echo " Starting Local AI Image Generator..."
export PATH="$NODE_DIR/bin:$PATH"
export FRONTEND_PORT="$FRONTEND_PORT"
# Run server in background and capture PID
"$NODE_BIN" "$SERVE_SCRIPT" &
SERVER_PID=$!
# Wait for server to be ready
sleep 2
# Open browser
if command -v open >/dev/null 2>&1; then
echo " Opening browser at http://localhost:${FRONTEND_PORT}"
open "http://localhost:${FRONTEND_PORT}" >/dev/null 2>&1 &
else
echo " Open your browser to: http://localhost:${FRONTEND_PORT}"
fi
echo ""
echo " ============================================================"
echo " Running!"
echo " Web UI: http://localhost:${FRONTEND_PORT}"
echo " GPU API: Auto-selected by the app (starts at 8080)"
echo " Text API: Starts when a GGUF model is loaded (port ${LLM_PORT})"
echo " Speech: Managed locally by the app"
echo " TTS: Managed locally by the app"
echo ""
echo " Press Ctrl+C in this window to stop all services."
echo " ============================================================"
echo ""
# Cleanup on exit
cleanup() {
echo ""
echo " Shutting down..."
if kill -0 "$SERVER_PID" >/dev/null 2>&1; then
kill -TERM "$SERVER_PID" >/dev/null 2>&1 || true
sleep 1
kill -KILL "$SERVER_PID" >/dev/null 2>&1 || true
fi
echo " Done. Goodbye!"
exit 0
}
trap cleanup SIGINT SIGTERM
# Keep script alive
wait "$SERVER_PID" || true
cleanup