"""Meet Assistant CLI entry point — used both by the `p3x-meet-assistant` console script (when pip-installed) and by the top-level ./meet-assistant-web.py launcher (for git-clone dev workflow).""" import argparse import os import sys import threading import uvicorn import meet_assistant.state as state from meet_assistant import HAS_AUDIO, diarizer from meet_assistant.audio import find_monitor_source from meet_assistant.engines import AVAILABLE_ENGINES, ENGINE_CLASSES from meet_assistant.server import app def main(): parser = argparse.ArgumentParser(prog="p3x-meet-assistant", description="Meet Assistant Web GUI") parser.add_argument("--port", type=int, default=8088, help="Web server port (default: 8088)") parser.add_argument("--host", default="0.0.0.0", help="Web server host (default: 0.0.0.0)") parser.add_argument("--device", type=int, default=11, help="PyAudio device index for speaker monitor (default: 11)") parser.add_argument("--dev", action="store_true", help="Dev mode: auto-reload Python on file changes") args = parser.parse_args() if "openai" not in AVAILABLE_ENGINES: print("Error: OpenAI API key not found.") print(" Set OPENAI_API_KEY in .env (see .env.example)") print(" OR export OPENAI_API_KEY=... in your shell") print(" Get a key: https://platform.openai.com/api-keys") sys.exit(1) print(f"\n Loading engine: {AVAILABLE_ENGINES['openai']}...") state.current_engine = ENGINE_CLASSES["openai"]() state.current_engine_name = "openai" print(" Engine ready!") if diarizer.is_available(): print(f" Diarizer ready on {diarizer.device()}") else: print(" Diarizer unavailable - transcripts will not have speaker labels") print(" Install with: pip install resemblyzer") capture_mode = "browser" if HAS_AUDIO: from meet_assistant.audio import _suppress_stderr, _restore_stderr import speech_recognition as sr monitor = find_monitor_source() if monitor: capture_mode = "server" print(f" Speaker source: {monitor}") os.environ["PULSE_SOURCE"] = monitor old_err = _suppress_stderr() recognizer = sr.Recognizer() recognizer.pause_threshold = 2.0 recognizer.non_speaking_duration = 1.0 speaker = sr.Microphone(device_index=args.device) _restore_stderr(old_err) state.server_capture = True threading.Thread(target=state.speaker_capture_loop, args=(recognizer, speaker), daemon=True).start() if capture_mode == "browser": print(" No PulseAudio - browser capture mode (use MIC/TAB buttons)") print(f"\n {'=' * 50}") print(f" Meet Assistant Web") print(f" http://localhost:{args.port}") print(f" Capture: {capture_mode}") if args.dev: print(f" Dev mode: auto-reload ON") print(f" {'=' * 50}\n") if args.dev: pkg_dir = os.path.dirname(os.path.abspath(__file__)) uvicorn.run("meet_assistant.server:app", host=args.host, port=args.port, reload=True, reload_dirs=[pkg_dir]) else: uvicorn.run(app, host=args.host, port=args.port, log_level="warning") if __name__ == "__main__": main()