proti0070 commited on
Commit
67b521d
·
verified ·
1 Parent(s): 1bca427

Update start.sh

Browse files
Files changed (1) hide show
  1. start.sh +197 -190
start.sh CHANGED
@@ -12,21 +12,25 @@ rm -f /tmp/.X99-lock /tmp/.X11-unix/X99
12
  echo "Starting Xvfb..."
13
  Xvfb :99 -screen 0 1280x800x16 -ac &
14
 
15
- sleep 2
16
 
17
  echo "Starting Openbox..."
18
  openbox --replace &
19
 
20
- sleep 1
21
 
22
  echo "Starting Android Studio..."
23
  /opt/android-studio/bin/studio.sh &
24
 
 
 
25
  echo "Starting VNC..."
26
- x11vnc -display :99 -nopw -forever -shared -rfbport 5900 -defer 20 -wait 20 -q &
27
 
28
- # Create custom index.html for HF root path
29
- echo "Setting up web interface at root path..."
 
 
30
  cat > /usr/share/novnc/index.html << 'EOF'
31
  <!DOCTYPE html>
32
  <html>
@@ -34,117 +38,27 @@ cat > /usr/share/novnc/index.html << 'EOF'
34
  <title>Android Studio on Hugging Face</title>
35
  <meta charset="utf-8">
36
  <style>
37
- body {
38
- margin: 0;
39
- padding: 20px;
40
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
41
- background: #1e1e1e;
42
- color: #fff;
43
- height: 100vh;
44
- display: flex;
45
- flex-direction: column;
46
- }
47
- .header {
48
- display: flex;
49
- justify-content: space-between;
50
- align-items: center;
51
- padding: 10px 20px;
52
- background: #2d2d2d;
53
- border-radius: 8px;
54
- margin-bottom: 20px;
55
- border: 1px solid #3ddc84;
56
- }
57
- .title {
58
- display: flex;
59
- align-items: center;
60
- gap: 10px;
61
- }
62
- .title h1 {
63
- margin: 0;
64
- font-size: 1.5rem;
65
- color: #3ddc84;
66
- }
67
- .badge {
68
- background: #3ddc84;
69
- color: #000;
70
- padding: 4px 12px;
71
- border-radius: 20px;
72
- font-size: 0.8rem;
73
- font-weight: bold;
74
- }
75
- .controls {
76
- display: flex;
77
- gap: 10px;
78
- }
79
- button {
80
- background: #3ddc84;
81
- color: #000;
82
- border: none;
83
- padding: 8px 16px;
84
- border-radius: 4px;
85
- cursor: pointer;
86
- font-weight: bold;
87
- transition: all 0.3s;
88
- }
89
- button:hover {
90
- background: #2ba860;
91
- transform: translateY(-2px);
92
- }
93
- button.secondary {
94
- background: transparent;
95
- color: #3ddc84;
96
- border: 1px solid #3ddc84;
97
- }
98
- button.secondary:hover {
99
- background: rgba(61, 220, 132, 0.1);
100
- }
101
- .vnc-container {
102
- flex: 1;
103
- background: #2d2d2d;
104
- border-radius: 8px;
105
- overflow: hidden;
106
- border: 1px solid #444;
107
- position: relative;
108
- }
109
- #vnc {
110
- width: 100%;
111
- height: 100%;
112
- }
113
- .loading {
114
- position: absolute;
115
- top: 50%;
116
- left: 50%;
117
- transform: translate(-50%, -50%);
118
- text-align: center;
119
- color: #666;
120
- }
121
- .footer {
122
- margin-top: 20px;
123
- text-align: center;
124
- color: #666;
125
- font-size: 0.8rem;
126
- }
127
- .footer a {
128
- color: #3ddc84;
129
- text-decoration: none;
130
- }
131
- .footer a:hover {
132
- text-decoration: underline;
133
- }
134
- .status {
135
- display: inline-block;
136
- width: 10px;
137
- height: 10px;
138
- border-radius: 50%;
139
- margin-right: 8px;
140
- }
141
- .status.connected {
142
- background: #3ddc84;
143
- box-shadow: 0 0 10px #3ddc84;
144
- }
145
- .status.disconnected {
146
- background: #ff4444;
147
- }
148
  </style>
149
  </head>
150
  <body>
@@ -154,66 +68,56 @@ cat > /usr/share/novnc/index.html << 'EOF'
154
  <h1>🤖 Android Studio</h1>
155
  </div>
156
  <div class="controls">
157
- <span style="display: flex; align-items: center; margin-right: 10px;">
158
- <span class="status disconnected" id="status-indicator"></span>
159
  <span id="status-text">Disconnected</span>
160
- </span>
161
- <button onclick="rfb.viewOnly(!rfb.viewOnly)" class="secondary" id="viewOnlyBtn">View Only: OFF</button>
162
- <button onclick="rfb.sendCtrlAltDel()" class="secondary">Ctrl+Alt+Del</button>
163
- <button onclick="rfb.disconnect(); setTimeout(() => connect(), 1000);">Reconnect</button>
164
  </div>
165
  </div>
166
 
167
  <div class="vnc-container">
168
  <div id="vnc"></div>
169
- <div class="loading" id="loading">Connecting to Android Studio...</div>
 
 
 
170
  </div>
171
 
172
  <div class="footer">
173
- <p>⚡ Running on Hugging Face Spaces | <a href="https://developer.android.com/studio" target="_blank">Android Studio Docs</a> | <a href="#" onclick="toggleFullscreen()">Fullscreen</a></p>
174
  </div>
175
 
176
  <script src="vnc.js"></script>
177
  <script>
178
  let rfb;
 
 
 
179
  const loading = document.getElementById('loading');
180
- const statusIndicator = document.getElementById('status-indicator');
 
181
  const statusText = document.getElementById('status-text');
182
  const viewOnlyBtn = document.getElementById('viewOnlyBtn');
 
 
183
 
184
- function connect() {
185
- loading.style.display = 'block';
186
-
187
- // Use relative path for HF Spaces
188
- const host = window.location.hostname;
189
- const port = window.location.port || (window.location.protocol === 'https:' ? 443 : 80);
190
- const path = window.location.pathname.replace(/\/$/, '') + '/websockify';
191
-
192
- rfb = new RFB(document.getElementById('vnc'), `wss://${host}:${port}${path}`, {
193
- credentials: { password: '' },
194
- shared: true,
195
- viewOnly: false,
196
- resizeSession: true
197
- });
198
-
199
- rfb.addEventListener('connect', () => {
200
- loading.style.display = 'none';
201
- statusIndicator.className = 'status connected';
202
- statusText.textContent = 'Connected';
203
- console.log('Connected to Android Studio');
204
- });
205
-
206
- rfb.addEventListener('disconnect', () => {
207
- loading.style.display = 'block';
208
- loading.textContent = 'Disconnected. Reconnecting...';
209
- statusIndicator.className = 'status disconnected';
210
- statusText.textContent = 'Disconnected';
211
- setTimeout(connect, 5000);
212
- });
213
-
214
- rfb.addEventListener('clipboard', (e) => {
215
- console.log('Clipboard event:', e.detail.text);
216
- });
217
  }
218
 
219
  function toggleFullscreen() {
@@ -225,58 +129,161 @@ cat > /usr/share/novnc/index.html << 'EOF'
225
  }
226
  }
227
 
228
- // Connect on page load
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  window.onload = connect;
230
 
231
- // Handle resize
232
  window.addEventListener('resize', () => {
233
- if (rfb) {
234
- rfb.resizeSession();
235
- }
236
  });
237
  </script>
238
  </body>
239
  </html>
240
  EOF
241
 
242
- # Create simple info page
243
- cat > /usr/share/novnc/info.html << 'EOF'
244
  <!DOCTYPE html>
245
  <html>
246
  <head>
247
- <title>Android Studio Info</title>
248
  <style>
249
- body { font-family: Arial; background: #1e1e1e; color: #fff; padding: 40px; }
250
- .info { background: #2d2d2d; padding: 20px; border-radius: 8px; border-left: 4px solid #3ddc84; }
251
- h1 { color: #3ddc84; }
252
- code { background: #000; padding: 2px 6px; border-radius: 4px; }
 
253
  </style>
254
  </head>
255
  <body>
256
- <h1>📱 Android Studio on Hugging Face</h1>
257
- <div class="info">
258
- <h2>Space Information</h2>
259
- <ul>
260
- <li><strong>Status:</strong> Running</li>
261
- <li><strong>Android Studio:</strong> 2023.3.1.18</li>
262
- <li><strong>VNC Port:</strong> 5900</li>
263
- <li><strong>WebSocket:</strong> /websockify</li>
264
- </ul>
265
 
266
- <h2>Quick Tips</h2>
267
- <ul>
268
- <li>Use <code>Ctrl+Alt+Del</code> button for system menu</li>
269
- <li>Toggle view-only mode to prevent accidental input</li>
270
- <li>Fullscreen available for better experience</li>
271
- <li>Clipboard support is enabled</li>
272
- </ul>
273
 
274
- <p><a href="/" style="color: #3ddc84;">← Back to main interface</a></p>
275
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  </body>
277
  </html>
278
  EOF
279
 
280
- echo "Starting noVNC with root path interface..."
281
  cd /usr/share/novnc
282
- websockify --web=/usr/share/novnc 7860 localhost:5900
 
 
 
12
  echo "Starting Xvfb..."
13
  Xvfb :99 -screen 0 1280x800x16 -ac &
14
 
15
+ sleep 3
16
 
17
  echo "Starting Openbox..."
18
  openbox --replace &
19
 
20
+ sleep 2
21
 
22
  echo "Starting Android Studio..."
23
  /opt/android-studio/bin/studio.sh &
24
 
25
+ sleep 5
26
+
27
  echo "Starting VNC..."
28
+ x11vnc -display :99 -nopw -forever -shared -rfbport 5900 -defer 10 -wait 10 -q &
29
 
30
+ # Wait for VNC to be ready
31
+ sleep 3
32
+
33
+ # Create the main index.html with correct HF Spaces WebSocket path
34
  cat > /usr/share/novnc/index.html << 'EOF'
35
  <!DOCTYPE html>
36
  <html>
 
38
  <title>Android Studio on Hugging Face</title>
39
  <meta charset="utf-8">
40
  <style>
41
+ body { margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #1e1e1e; color: #fff; height: 100vh; display: flex; flex-direction: column; }
42
+ .header { display: flex; justify-content: space-between; align-items: center; padding: 10px 20px; background: #2d2d2d; border-bottom: 2px solid #3ddc84; }
43
+ .title h1 { margin: 0; font-size: 1.3rem; color: #3ddc84; }
44
+ .badge { background: #3ddc84; color: #000; padding: 2px 8px; border-radius: 12px; font-size: 0.7rem; font-weight: bold; margin-left: 10px; }
45
+ .controls { display: flex; gap: 8px; align-items: center; }
46
+ button { background: #3ddc84; color: #000; border: none; padding: 6px 12px; border-radius: 4px; cursor: pointer; font-weight: bold; font-size: 0.8rem; }
47
+ button.secondary { background: transparent; color: #3ddc84; border: 1px solid #3ddc84; }
48
+ button:disabled { opacity: 0.5; cursor: not-allowed; }
49
+ .vnc-container { flex: 1; background: #000; position: relative; overflow: hidden; }
50
+ #vnc { width: 100%; height: 100%; }
51
+ .status { display: flex; align-items: center; gap: 5px; margin-right: 15px; font-size: 0.8rem; }
52
+ .status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
53
+ .status-dot.connected { background: #3ddc84; animation: pulse 2s infinite; }
54
+ .status-dot.connecting { background: #ffaa00; }
55
+ .status-dot.disconnected { background: #ff4444; }
56
+ @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(61, 220, 132, 0.7); } 70% { box-shadow: 0 0 0 6px rgba(61, 220, 132, 0); } }
57
+ .loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; z-index: 10; background: rgba(30,30,30,0.9); padding: 20px; border-radius: 8px; border: 1px solid #3ddc84; }
58
+ .spinner { border: 3px solid #f3f3f3; border-top: 3px solid #3ddc84; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 0 auto 10px; }
59
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
60
+ .footer { text-align: center; padding: 5px; background: #2d2d2d; font-size: 0.7rem; color: #888; }
61
+ .footer a { color: #3ddc84; text-decoration: none; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  </style>
63
  </head>
64
  <body>
 
68
  <h1>🤖 Android Studio</h1>
69
  </div>
70
  <div class="controls">
71
+ <div class="status">
72
+ <span class="status-dot disconnected" id="status-dot"></span>
73
  <span id="status-text">Disconnected</span>
74
+ </div>
75
+ <button onclick="toggleViewOnly()" class="secondary" id="viewOnlyBtn" disabled>View Only: OFF</button>
76
+ <button onclick="sendCtrlAltDel()" class="secondary" id="ctrlAltDelBtn" disabled>Ctrl+Alt+Del</button>
77
+ <button onclick="reconnect()" id="reconnectBtn">Reconnect</button>
78
  </div>
79
  </div>
80
 
81
  <div class="vnc-container">
82
  <div id="vnc"></div>
83
+ <div class="loading" id="loading">
84
+ <div class="spinner"></div>
85
+ <div id="loading-text">Connecting to Android Studio...</div>
86
+ </div>
87
  </div>
88
 
89
  <div class="footer">
90
+ ⚡ Running on Hugging Face Spaces | <a href="#" onclick="toggleFullscreen()">Fullscreen</a> | <span id="ws-info"></span>
91
  </div>
92
 
93
  <script src="vnc.js"></script>
94
  <script>
95
  let rfb;
96
+ let retryCount = 0;
97
+ const maxRetries = 10;
98
+
99
  const loading = document.getElementById('loading');
100
+ const loadingText = document.getElementById('loading-text');
101
+ const statusDot = document.getElementById('status-dot');
102
  const statusText = document.getElementById('status-text');
103
  const viewOnlyBtn = document.getElementById('viewOnlyBtn');
104
+ const ctrlAltDelBtn = document.getElementById('ctrlAltDelBtn');
105
+ const wsInfo = document.getElementById('ws-info');
106
 
107
+ function setStatus(state, message) {
108
+ statusDot.className = 'status-dot ' + state;
109
+ statusText.textContent = message;
110
+ }
111
+
112
+ function toggleViewOnly() {
113
+ if (rfb) {
114
+ rfb.viewOnly = !rfb.viewOnly;
115
+ viewOnlyBtn.textContent = 'View Only: ' + (rfb.viewOnly ? 'ON' : 'OFF');
116
+ }
117
+ }
118
+
119
+ function sendCtrlAltDel() {
120
+ if (rfb) rfb.sendCtrlAltDel();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
122
 
123
  function toggleFullscreen() {
 
129
  }
130
  }
131
 
132
+ function reconnect() {
133
+ if (rfb) rfb.disconnect();
134
+ setTimeout(connect, 1000);
135
+ }
136
+
137
+ function connect() {
138
+ loading.style.display = 'block';
139
+ setStatus('connecting', 'Connecting...');
140
+ viewOnlyBtn.disabled = true;
141
+ ctrlAltDelBtn.disabled = true;
142
+
143
+ // CORRECT WebSocket URL for Hugging Face Spaces
144
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
145
+ const host = window.location.host;
146
+
147
+ // For HF Spaces, the WebSocket endpoint is at /proxy/7860/websockify
148
+ const wsUrl = `${protocol}//${host}/proxy/7860/websockify`;
149
+
150
+ wsInfo.textContent = `Endpoint: /proxy/7860/websockify`;
151
+ console.log('Connecting to:', wsUrl);
152
+
153
+ try {
154
+ rfb = new RFB(document.getElementById('vnc'), wsUrl, {
155
+ credentials: { password: '' },
156
+ shared: true,
157
+ viewOnly: false,
158
+ resizeSession: true,
159
+ wsProtocols: ['binary']
160
+ });
161
+
162
+ rfb.addEventListener('connect', () => {
163
+ loading.style.display = 'none';
164
+ setStatus('connected', 'Connected');
165
+ viewOnlyBtn.disabled = false;
166
+ ctrlAltDelBtn.disabled = false;
167
+ retryCount = 0;
168
+ console.log('Connected successfully');
169
+ });
170
+
171
+ rfb.addEventListener('disconnect', (e) => {
172
+ loading.style.display = 'block';
173
+ setStatus('disconnected', 'Disconnected');
174
+ viewOnlyBtn.disabled = true;
175
+ ctrlAltDelBtn.disabled = true;
176
+
177
+ if (retryCount < maxRetries) {
178
+ retryCount++;
179
+ const delay = Math.min(1000 * retryCount, 10000);
180
+ loadingText.textContent = `Reconnecting in ${delay/1000}s... (${retryCount}/${maxRetries})`;
181
+ setTimeout(connect, delay);
182
+ } else {
183
+ loadingText.textContent = 'Failed to connect. Click Reconnect.';
184
+ }
185
+ });
186
+
187
+ rfb.addEventListener('securityfailure', (e) => {
188
+ console.error('Security failure:', e.detail);
189
+ loadingText.textContent = 'Security error: ' + (e.detail.message || 'Unknown');
190
+ });
191
+
192
+ } catch (error) {
193
+ console.error('Connection error:', error);
194
+ loadingText.textContent = 'Connection error: ' + error.message;
195
+ setStatus('disconnected', 'Error');
196
+ }
197
+ }
198
+
199
  window.onload = connect;
200
 
 
201
  window.addEventListener('resize', () => {
202
+ if (rfb) setTimeout(() => rfb.resizeSession(), 100);
 
 
203
  });
204
  </script>
205
  </body>
206
  </html>
207
  EOF
208
 
209
+ # Create a diagnostic page
210
+ cat > /usr/share/novnc/diagnostic.html << 'EOF'
211
  <!DOCTYPE html>
212
  <html>
213
  <head>
214
+ <title>HF Space Diagnostic</title>
215
  <style>
216
+ body { background: #1e1e1e; color: #fff; font-family: monospace; padding: 20px; }
217
+ .success { color: #3ddc84; }
218
+ .error { color: #ff4444; }
219
+ pre { background: #2d2d2d; padding: 15px; border-radius: 5px; overflow: auto; }
220
+ button { background: #3ddc84; color: #000; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; margin: 5px; }
221
  </style>
222
  </head>
223
  <body>
224
+ <h1>🔧 Hugging Face Space Diagnostic Tool</h1>
225
+ <button onclick="testWebSocket()">Test WebSocket Connection</button>
226
+ <button onclick="checkVNC()">Check VNC Server</button>
227
+ <div id="output"></div>
228
+
229
+ <script>
230
+ const output = document.getElementById('output');
 
 
231
 
232
+ function log(msg, isError = false) {
233
+ const div = document.createElement('div');
234
+ div.className = isError ? 'error' : 'success';
235
+ div.textContent = msg;
236
+ output.appendChild(div);
237
+ }
 
238
 
239
+ async function testWebSocket() {
240
+ output.innerHTML = '<pre>Testing WebSocket connections...</pre>';
241
+
242
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
243
+ const host = window.location.host;
244
+
245
+ const endpoints = [
246
+ `/proxy/7860/websockify`,
247
+ `/websockify`,
248
+ `wss://${host}:7860/websockify`
249
+ ];
250
+
251
+ for (const endpoint of endpoints) {
252
+ const url = endpoint.startsWith('ws') ? endpoint : `${protocol}//${host}${endpoint}`;
253
+ log(`\nTesting: ${url}`);
254
+
255
+ try {
256
+ const ws = new WebSocket(url);
257
+ const result = await new Promise((resolve, reject) => {
258
+ ws.onopen = () => { ws.close(); resolve('✅ SUCCESS'); };
259
+ ws.onerror = () => { reject('❌ FAILED'); };
260
+ setTimeout(() => reject('⏱️ TIMEOUT'), 3000);
261
+ });
262
+ log(` ${result}`);
263
+ } catch (e) {
264
+ log(` ${e}`, true);
265
+ }
266
+ }
267
+ }
268
+
269
+ async function checkVNC() {
270
+ output.innerHTML += '<pre>\nChecking VNC server...</pre>';
271
+ log('Checking if VNC is running on port 5900...');
272
+
273
+ try {
274
+ const response = await fetch('/proxy/5900/');
275
+ log('✅ VNC port is accessible');
276
+ } catch (e) {
277
+ log('❌ VNC port not accessible', true);
278
+ }
279
+ }
280
+ </script>
281
  </body>
282
  </html>
283
  EOF
284
 
285
+ echo "Starting noVNC with HF Spaces configuration..."
286
  cd /usr/share/novnc
287
+
288
+ # Bind to all interfaces and use correct port
289
+ websockify --web=/usr/share/novnc 0.0.0.0:7860 localhost:5900