Files
Meeting_Assistant/backend/build.py
egg 7d3fc72bd2 feat: Add browser mode fallback for Kaspersky audio blocking
- 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>
2025-12-22 16:41:25 +08:00

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()