- 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>
145 lines
5.0 KiB
Python
145 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Build script for creating standalone backend executable using PyInstaller.
|
|
Uses --onedir mode for faster startup compared to --onefile.
|
|
"""
|
|
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
import shutil
|
|
|
|
|
|
def clean_build_cache(script_dir):
|
|
"""Clean old build artifacts that may cause stale spec file issues."""
|
|
dirs_to_clean = [
|
|
os.path.join(script_dir, "build"),
|
|
os.path.join(script_dir, "__pycache__"),
|
|
]
|
|
files_to_clean = [
|
|
os.path.join(script_dir, "build", "backend.spec"),
|
|
]
|
|
|
|
for f in files_to_clean:
|
|
if os.path.exists(f):
|
|
print(f"Removing old spec file: {f}")
|
|
os.remove(f)
|
|
|
|
for d in dirs_to_clean:
|
|
pycache = os.path.join(d)
|
|
if os.path.exists(pycache) and "__pycache__" in pycache:
|
|
print(f"Removing cache: {pycache}")
|
|
shutil.rmtree(pycache)
|
|
|
|
|
|
def build():
|
|
"""Build the backend executable."""
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
# Clean old build cache to avoid stale spec file issues
|
|
clean_build_cache(script_dir)
|
|
|
|
# PyInstaller command with --onedir for faster startup
|
|
cmd = [
|
|
sys.executable, "-m", "PyInstaller",
|
|
"--onedir",
|
|
"--clean", # Clean PyInstaller cache before building
|
|
"--name", "backend",
|
|
"--distpath", "dist",
|
|
"--workpath", "build",
|
|
"--specpath", "build",
|
|
# FastAPI and web framework
|
|
"--hidden-import", "uvicorn",
|
|
"--hidden-import", "uvicorn.logging",
|
|
"--hidden-import", "uvicorn.loops",
|
|
"--hidden-import", "uvicorn.loops.auto",
|
|
"--hidden-import", "uvicorn.protocols",
|
|
"--hidden-import", "uvicorn.protocols.http",
|
|
"--hidden-import", "uvicorn.protocols.http.auto",
|
|
"--hidden-import", "uvicorn.protocols.websockets",
|
|
"--hidden-import", "uvicorn.protocols.websockets.auto",
|
|
"--hidden-import", "uvicorn.lifespan",
|
|
"--hidden-import", "uvicorn.lifespan.on",
|
|
"--hidden-import", "uvicorn.lifespan.off",
|
|
"--hidden-import", "fastapi",
|
|
"--hidden-import", "starlette",
|
|
"--hidden-import", "pydantic",
|
|
"--hidden-import", "pydantic_core",
|
|
# Database - MySQL
|
|
"--hidden-import", "mysql.connector",
|
|
"--hidden-import", "mysql.connector.pooling",
|
|
# Database - SQLite (built-in, but ensure it's included)
|
|
"--hidden-import", "sqlite3",
|
|
# HTTP client
|
|
"--hidden-import", "httpx",
|
|
"--hidden-import", "httpcore",
|
|
# Authentication
|
|
"--hidden-import", "jose",
|
|
"--hidden-import", "jose.jwt",
|
|
"--hidden-import", "cryptography",
|
|
# Excel export
|
|
"--hidden-import", "openpyxl",
|
|
# Multipart form handling
|
|
"--hidden-import", "multipart",
|
|
"--hidden-import", "python_multipart",
|
|
# Environment loading
|
|
"--hidden-import", "dotenv",
|
|
# Timezone data
|
|
"--hidden-import", "tzdata",
|
|
# Application modules - only include modules that exist
|
|
"--hidden-import", "app",
|
|
"--hidden-import", "app.main",
|
|
"--hidden-import", "app.config",
|
|
"--hidden-import", "app.database",
|
|
"--hidden-import", "app.routers",
|
|
"--hidden-import", "app.routers.auth",
|
|
"--hidden-import", "app.routers.meetings",
|
|
"--hidden-import", "app.routers.ai",
|
|
"--hidden-import", "app.routers.export",
|
|
"--hidden-import", "app.routers.sidecar",
|
|
"--hidden-import", "app.sidecar_manager",
|
|
"--hidden-import", "app.models",
|
|
"--hidden-import", "app.models.schemas",
|
|
# Collect package data
|
|
"--collect-data", "pydantic",
|
|
"--collect-data", "uvicorn",
|
|
"run_server.py"
|
|
]
|
|
|
|
print("Building backend executable...")
|
|
print(f"Command: {' '.join(cmd)}")
|
|
|
|
result = subprocess.run(cmd, cwd=script_dir)
|
|
|
|
if result.returncode != 0:
|
|
print("\nBuild failed!")
|
|
sys.exit(1)
|
|
|
|
# Copy template directory to dist
|
|
template_src = os.path.join(script_dir, "template")
|
|
template_dst = os.path.join(script_dir, "dist", "backend", "template")
|
|
|
|
if os.path.exists(template_src):
|
|
print(f"\nCopying template directory to {template_dst}...")
|
|
if os.path.exists(template_dst):
|
|
shutil.rmtree(template_dst)
|
|
shutil.copytree(template_src, template_dst)
|
|
print("Template directory copied successfully.")
|
|
else:
|
|
print(f"\nWarning: Template directory not found at {template_src}")
|
|
|
|
# Create empty record directory
|
|
record_dst = os.path.join(script_dir, "dist", "backend", "record")
|
|
os.makedirs(record_dst, exist_ok=True)
|
|
print(f"Created record directory at {record_dst}")
|
|
|
|
print("\nBuild successful!")
|
|
print(f"Executable created at: dist/backend/backend.exe (Windows) or dist/backend/backend (Linux)")
|
|
print("\nTo run:")
|
|
print(" 1. Copy config.json to dist/backend/")
|
|
print(" 2. Run: dist/backend/backend.exe (Windows) or ./dist/backend/backend (Linux)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
build()
|