#!/usr/bin/env python3 """ Startup Script for SyncMaster نقطة دخول موحدة تضمن تشغيل جميع المكونات المطلوبة """ import os import sys import time import logging import subprocess import signal import atexit from pathlib import Path # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) class SyncMasterLauncher: def __init__(self): self.recorder_process = None self.streamlit_process = None self.cleanup_registered = False def setup_cleanup(self): """Setup cleanup handlers""" if not self.cleanup_registered: atexit.register(self.cleanup) signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal.SIGTERM, self.signal_handler) self.cleanup_registered = True def signal_handler(self, signum, frame): """Handle termination signals""" logging.info(f"Received signal {signum}, cleaning up...") self.cleanup() sys.exit(0) def cleanup(self): """Clean up all processes""" logging.info("🧹 Cleaning up processes...") if self.recorder_process and self.recorder_process.poll() is None: try: self.recorder_process.terminate() self.recorder_process.wait(timeout=5) logging.info("✅ Recorder server terminated") except: try: self.recorder_process.kill() logging.info("⚠️ Recorder server killed") except: pass if self.streamlit_process and self.streamlit_process.poll() is None: try: self.streamlit_process.terminate() self.streamlit_process.wait(timeout=5) logging.info("✅ Streamlit server terminated") except: try: self.streamlit_process.kill() logging.info("⚠️ Streamlit server killed") except: pass def start_recorder_server(self): """Start the recorder server""" try: logging.info("🚀 Starting recorder server...") self.recorder_process = subprocess.Popen( [sys.executable, 'recorder_server.py'], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # Wait for server to start time.sleep(3) # Check if process is running if self.recorder_process.poll() is None: # Verify server is responding try: import requests response = requests.get('http://localhost:5001/record', timeout=5) if response.status_code == 200: logging.info("✅ Recorder server started successfully on port 5001") return True else: logging.warning(f"⚠️ Recorder server responded with status: {response.status_code}") except Exception as e: logging.warning(f"⚠️ Could not verify recorder server: {e}") # Server process is running even if verification failed return True else: logging.error("❌ Recorder server process failed to start") return False except Exception as e: logging.error(f"❌ Failed to start recorder server: {e}") return False def start_streamlit_app(self, port=5050, host="0.0.0.0"): """Start the Streamlit application""" try: logging.info(f"🚀 Starting Streamlit app on {host}:{port}...") cmd = [ sys.executable, '-m', 'streamlit', 'run', 'app.py', '--server.port', str(port), '--server.address', host, '--server.headless', 'true', '--browser.gatherUsageStats', 'false' ] self.streamlit_process = subprocess.Popen(cmd) # Wait a bit for Streamlit to start time.sleep(5) if self.streamlit_process.poll() is None: logging.info(f"✅ Streamlit app started successfully on http://{host}:{port}") return True else: logging.error("❌ Streamlit app failed to start") return False except Exception as e: logging.error(f"❌ Failed to start Streamlit app: {e}") return False def launch_integrated(self): """Launch with integrated server (recommended for HuggingFace)""" logging.info("🚀 Launching SyncMaster with integrated server...") self.setup_cleanup() # Start Streamlit with integrated server try: # Import to trigger integrated server startup import app # Run Streamlit import streamlit.web.cli as stcli import sys # Set command line arguments for Streamlit sys.argv = [ "streamlit", "run", "app.py", "--server.port", "5050", "--server.address", "0.0.0.0", "--server.headless", "true", "--browser.gatherUsageStats", "false" ] # Run Streamlit CLI stcli.main() except Exception as e: logging.error(f"❌ Failed to launch integrated mode: {e}") return False def launch_separate(self): """Launch with separate processes (development mode)""" logging.info("🚀 Launching SyncMaster with separate processes...") self.setup_cleanup() # Start recorder server first if not self.start_recorder_server(): logging.error("❌ Failed to start recorder server, aborting...") return False # Start Streamlit app if not self.start_streamlit_app(): logging.error("❌ Failed to start Streamlit app, aborting...") self.cleanup() return False logging.info("✅ All services started successfully!") logging.info("🌐 Access the application at: http://localhost:5050") logging.info("🎙️ Recorder API available at: http://localhost:5001") try: # Keep the main process alive while True: time.sleep(1) # Check if processes are still running if self.recorder_process and self.recorder_process.poll() is not None: logging.error("❌ Recorder server process died") break if self.streamlit_process and self.streamlit_process.poll() is not None: logging.error("❌ Streamlit app process died") break except KeyboardInterrupt: logging.info("👋 Shutting down...") finally: self.cleanup() def main(): """Main entry point""" launcher = SyncMasterLauncher() # Check if running in HuggingFace or similar environment if os.getenv('SPACE_ID') or '--integrated' in sys.argv: # Use integrated mode for cloud deployments launcher.launch_integrated() else: # Use separate processes for local development launcher.launch_separate() if __name__ == "__main__": main()