File size: 7,857 Bytes
f93a960
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/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()