- Add sidecar management to backend (sidecar_manager.py) - Add sidecar API router for browser mode (/api/sidecar/*) - Add browser-api.js polyfill for running in Chrome/Edge - Add "Open in Browser" button when audio access fails - Update build scripts with new sidecar modules - Add start-browser.sh for development browser mode Browser mode allows users to open the app in their system browser when Electron's audio access is blocked by security software. The backend manages the sidecar process in browser mode (BROWSER_MODE=true). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
261 lines
7.2 KiB
Bash
Executable File
261 lines
7.2 KiB
Bash
Executable File
#!/bin/bash
|
||
#
|
||
# Meeting Assistant - Browser Mode Startup Script
|
||
# 使用瀏覽器運行 Meeting Assistant(完整功能,包含即時語音轉寫)
|
||
#
|
||
# 此模式下:
|
||
# - 後端會自動啟動並管理 Sidecar(Whisper 語音轉寫引擎)
|
||
# - 前端在 Chrome/Edge 瀏覽器中運行
|
||
# - 所有功能皆可正常使用
|
||
#
|
||
|
||
set -e
|
||
|
||
# 顏色定義
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 專案路徑
|
||
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
BACKEND_DIR="$PROJECT_DIR/backend"
|
||
SIDECAR_DIR="$PROJECT_DIR/sidecar"
|
||
|
||
# Server Configuration (can be overridden by .env)
|
||
BACKEND_HOST="${BACKEND_HOST:-0.0.0.0}"
|
||
BACKEND_PORT="${BACKEND_PORT:-8000}"
|
||
|
||
# Whisper Configuration (can be overridden by .env)
|
||
export WHISPER_MODEL="${WHISPER_MODEL:-medium}"
|
||
export WHISPER_DEVICE="${WHISPER_DEVICE:-cpu}"
|
||
export WHISPER_COMPUTE="${WHISPER_COMPUTE:-int8}"
|
||
|
||
# Browser mode flag - tells backend to manage sidecar
|
||
export BROWSER_MODE="true"
|
||
|
||
# 函數:印出訊息
|
||
log_info() {
|
||
echo -e "${BLUE}[INFO]${NC} $1"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}[OK]${NC} $1"
|
||
}
|
||
|
||
log_warn() {
|
||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
# Load environment variables from .env file if it exists
|
||
if [ -f "$BACKEND_DIR/.env" ]; then
|
||
log_info "Loading backend environment from $BACKEND_DIR/.env"
|
||
export $(grep -v '^#' "$BACKEND_DIR/.env" | grep -v '^$' | xargs)
|
||
fi
|
||
|
||
# 函數:檢查 port 是否被佔用
|
||
check_port() {
|
||
local port=$1
|
||
if lsof -i :$port > /dev/null 2>&1; then
|
||
return 0 # port 被佔用
|
||
else
|
||
return 1 # port 可用
|
||
fi
|
||
}
|
||
|
||
# 函數:開啟瀏覽器
|
||
open_browser() {
|
||
local url=$1
|
||
log_info "Opening browser at $url"
|
||
|
||
# Try different browser commands
|
||
if command -v xdg-open &> /dev/null; then
|
||
xdg-open "$url" &
|
||
elif command -v wslview &> /dev/null; then
|
||
wslview "$url" &
|
||
elif command -v explorer.exe &> /dev/null; then
|
||
# WSL: use Windows browser
|
||
explorer.exe "$url" &
|
||
elif command -v open &> /dev/null; then
|
||
# macOS
|
||
open "$url" &
|
||
else
|
||
log_warn "Could not find a browser to open. Please manually visit: $url"
|
||
fi
|
||
}
|
||
|
||
# 函數:檢查環境
|
||
check_environment() {
|
||
local all_ok=true
|
||
|
||
# 檢查後端虛擬環境
|
||
if [ ! -d "$BACKEND_DIR/venv" ]; then
|
||
log_error "Backend virtual environment not found"
|
||
log_error "Please run: cd $BACKEND_DIR && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt"
|
||
all_ok=false
|
||
fi
|
||
|
||
# 檢查 Sidecar 虛擬環境
|
||
if [ ! -d "$SIDECAR_DIR/venv" ]; then
|
||
log_warn "Sidecar virtual environment not found"
|
||
log_warn "即時語音轉寫功能將無法使用"
|
||
log_warn "To enable: cd $SIDECAR_DIR && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt"
|
||
else
|
||
log_success "Sidecar environment found - 即時語音轉寫功能可用"
|
||
fi
|
||
|
||
if [ "$all_ok" = false ]; then
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 函數:啟動後端(包含 Sidecar)
|
||
start_backend() {
|
||
log_info "Checking backend status..."
|
||
|
||
# Check if backend is already running
|
||
if check_port $BACKEND_PORT; then
|
||
# Verify it's our backend by checking health endpoint
|
||
if curl -s http://localhost:$BACKEND_PORT/api/health > /dev/null 2>&1; then
|
||
log_success "Backend is already running on port $BACKEND_PORT"
|
||
return 0
|
||
else
|
||
log_warn "Port $BACKEND_PORT is in use but not by our backend"
|
||
log_error "Please stop the process using port $BACKEND_PORT and try again"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
log_info "Starting backend server (with Sidecar management)..."
|
||
log_info "Whisper config: model=$WHISPER_MODEL, device=$WHISPER_DEVICE, compute=$WHISPER_COMPUTE"
|
||
|
||
cd "$BACKEND_DIR"
|
||
source venv/bin/activate
|
||
|
||
# Start uvicorn in background
|
||
nohup uvicorn app.main:app --host $BACKEND_HOST --port $BACKEND_PORT > "$PROJECT_DIR/backend-browser.log" 2>&1 &
|
||
local backend_pid=$!
|
||
|
||
# Wait for backend to be ready
|
||
log_info "Waiting for backend and sidecar to start..."
|
||
log_info "(This may take a minute if Whisper model needs to download)"
|
||
local max_wait=120 # 2 minutes for model download
|
||
local waited=0
|
||
|
||
while [ $waited -lt $max_wait ]; do
|
||
sleep 2
|
||
waited=$((waited + 2))
|
||
|
||
if curl -s http://localhost:$BACKEND_PORT/api/health > /dev/null 2>&1; then
|
||
log_success "Backend started (PID: $backend_pid)"
|
||
|
||
# Check sidecar status
|
||
local sidecar_status=$(curl -s http://localhost:$BACKEND_PORT/api/sidecar/status 2>/dev/null)
|
||
if echo "$sidecar_status" | grep -q '"ready":true'; then
|
||
log_success "Sidecar (Whisper) ready"
|
||
elif echo "$sidecar_status" | grep -q '"available":false'; then
|
||
log_warn "Sidecar not available - transcription disabled"
|
||
else
|
||
log_info "Sidecar loading... (model may be downloading)"
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
# Show progress every 10 seconds
|
||
if [ $((waited % 10)) -eq 0 ]; then
|
||
log_info "Still waiting... ($waited seconds)"
|
||
fi
|
||
done
|
||
|
||
log_error "Backend failed to start. Check $PROJECT_DIR/backend-browser.log for details"
|
||
exit 1
|
||
}
|
||
|
||
# 函數:停止服務
|
||
stop_services() {
|
||
log_info "Stopping services..."
|
||
pkill -f "uvicorn app.main:app" 2>/dev/null || true
|
||
sleep 1
|
||
log_success "Services stopped"
|
||
}
|
||
|
||
# 主程式
|
||
main() {
|
||
echo ""
|
||
echo "=========================================="
|
||
echo " Meeting Assistant - Browser Mode"
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
# Check environment
|
||
check_environment
|
||
|
||
# Start backend (which manages sidecar)
|
||
start_backend
|
||
|
||
# Give it a moment
|
||
sleep 1
|
||
|
||
# Open browser
|
||
local url="http://localhost:$BACKEND_PORT"
|
||
open_browser "$url"
|
||
|
||
echo ""
|
||
echo "=========================================="
|
||
log_success "Browser mode started!"
|
||
echo "=========================================="
|
||
echo ""
|
||
echo " Access URL: $url"
|
||
echo " API Docs: $url/docs"
|
||
echo ""
|
||
echo " Features:"
|
||
echo " - 即時語音轉寫(透過後端 Sidecar)"
|
||
echo " - 上傳音訊轉寫"
|
||
echo " - AI 摘要"
|
||
echo " - 匯出 Excel"
|
||
echo ""
|
||
echo " To stop: $0 stop"
|
||
echo ""
|
||
log_info "Press Ctrl+C to exit (backend will keep running)"
|
||
echo ""
|
||
|
||
# Keep script running
|
||
trap 'echo ""; log_info "Exiting (backend still running)"; exit 0' INT TERM
|
||
|
||
while true; do
|
||
sleep 60
|
||
done
|
||
}
|
||
|
||
# 處理命令
|
||
case "${1:-start}" in
|
||
start)
|
||
main
|
||
;;
|
||
stop)
|
||
stop_services
|
||
;;
|
||
restart)
|
||
stop_services
|
||
sleep 2
|
||
main
|
||
;;
|
||
status)
|
||
if check_port $BACKEND_PORT; then
|
||
log_success "Backend running on port $BACKEND_PORT"
|
||
curl -s http://localhost:$BACKEND_PORT/api/sidecar/status | python3 -m json.tool 2>/dev/null || echo "(Could not parse sidecar status)"
|
||
else
|
||
log_warn "Backend not running"
|
||
fi
|
||
;;
|
||
*)
|
||
echo "Usage: $0 {start|stop|restart|status}"
|
||
exit 1
|
||
;;
|
||
esac
|