RSS Git Download  Clone
Raw Blame History 3kB 90 lines
"""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()