Akwbw commited on
Commit
8255c62
·
verified ·
1 Parent(s): 55ed029

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -130
app.py CHANGED
@@ -1,19 +1,25 @@
1
  import os
2
  import time
 
 
 
 
 
3
  import subprocess
4
- import threading
5
- from flask import Flask, request, Response
6
  import pyautogui
7
 
8
- # Set Display explicitly for PyAutoGUI
9
- os.environ["DISPLAY"] = ":99"
10
-
11
  app = Flask(__name__)
 
 
 
 
12
 
13
- # PyAutoGUI Failsafe Disable
14
  pyautogui.FAILSAFE = False
 
15
 
16
- # --- EMBEDDED HTML UI (Mobile Optimized) ---
17
  HTML_TEMPLATE = """
18
  <!DOCTYPE html>
19
  <html>
@@ -21,119 +27,42 @@ HTML_TEMPLATE = """
21
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
22
  <title>Cloud PC</title>
23
  <style>
24
- body { margin: 0; background: #111; height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
25
-
26
- /* SCREEN VIEW */
27
- .screen-container {
28
- flex: 1; display: flex; justify-content: center; align-items: flex-start;
29
- overflow: hidden; background: black; position: relative;
30
- }
31
- #desktop {
32
- width: 100%; height: auto; max-height: 100%;
33
- object-fit: contain; cursor: pointer;
34
- }
35
-
36
- /* FLOATING TOOLBAR */
37
- .toolbar {
38
- position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
39
- background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px);
40
- padding: 10px; border-radius: 30px; display: flex; gap: 15px;
41
- border: 1px solid rgba(255,255,255,0.2);
42
- }
43
- .btn {
44
- width: 40px; height: 40px; border-radius: 50%; background: #333; color: white;
45
- display: flex; align-items: center; justify-content: center; font-size: 18px;
46
- cursor: pointer; border: none;
47
- }
48
- .btn:active { background: #2563eb; }
49
-
50
- /* KEYBOARD INPUT */
51
- #kbdBar {
52
- display: none; padding: 10px; background: #222; border-top: 1px solid #444;
53
- }
54
- input { width: 100%; padding: 10px; background: #333; border: none; color: white; border-radius: 5px; }
55
  </style>
56
  </head>
57
  <body>
58
-
59
  <div class="screen-container">
60
  <img id="desktop" draggable="false">
61
-
62
  <div class="toolbar">
63
- <button class="btn" onclick="sendKey('win')">⊞</button>
64
- <button class="btn" onclick="toggleKeyboard()"></button>
65
- <button class="btn" onclick="sendKey('enter')"></button>
66
- <button class="btn" onclick="launchApp('terminal')">_></button>
67
- <button class="btn" onclick="launchApp('chrome')">🌐</button>
68
  </div>
69
  </div>
70
-
71
- <div id="kbdBar">
72
- <input type="text" id="typeInput" placeholder="Type here..." autocomplete="off">
73
- <button onclick="sendText()" style="padding: 10px; width: 100%; margin-top: 5px; background: blue; color: white; border: none; border-radius: 5px;">Send</button>
74
- </div>
75
-
76
  <script>
77
  const img = document.getElementById('desktop');
78
-
79
- // 1. Live Stream Loop
80
- function refresh() {
81
- img.src = '/snapshot?' + new Date().getTime();
82
- }
83
- setInterval(refresh, 800); // 0.8s Refresh Rate
84
 
85
- // 2. Click Handler (Accurate Mapping)
86
  img.addEventListener('click', async (e) => {
87
  const rect = img.getBoundingClientRect();
88
- // Ignore black bars
89
  if (e.clientX < rect.left || e.clientX > rect.right) return;
90
-
91
  const x = (e.clientX - rect.left) / rect.width;
92
  const y = (e.clientY - rect.top) / rect.height;
93
-
94
- await fetch('/interact', {
95
- method: 'POST',
96
- headers: {'Content-Type': 'application/json'},
97
- body: JSON.stringify({action: 'tap', x: x, y: y})
98
- });
99
  refresh();
100
  });
101
 
102
- // 3. Actions
103
  async function sendKey(k) {
104
- await fetch('/interact', {
105
- method: 'POST',
106
- headers: {'Content-Type': 'application/json'},
107
- body: JSON.stringify({action: 'key', key: k})
108
- });
109
  refresh();
110
  }
111
-
112
  async function launchApp(app) {
113
- await fetch('/interact', {
114
- method: 'POST',
115
- headers: {'Content-Type': 'application/json'},
116
- body: JSON.stringify({action: 'launch', app: app})
117
- });
118
- }
119
-
120
- function toggleKeyboard() {
121
- const bar = document.getElementById('kbdBar');
122
- bar.style.display = bar.style.display === 'block' ? 'none' : 'block';
123
- }
124
-
125
- async function sendText() {
126
- const txt = document.getElementById('typeInput').value;
127
- if(!txt) return;
128
- document.getElementById('typeInput').value = "";
129
- document.getElementById('kbdBar').style.display = 'none';
130
-
131
- await fetch('/interact', {
132
- method: 'POST',
133
- headers: {'Content-Type': 'application/json'},
134
- body: JSON.stringify({action: 'type', text: txt})
135
- });
136
- refresh();
137
  }
138
  </script>
139
  </body>
@@ -141,50 +70,27 @@ HTML_TEMPLATE = """
141
  """
142
 
143
  @app.route('/')
144
- def home():
145
- return HTML_TEMPLATE
146
 
147
- @app.route('/snapshot')
148
- def snapshot():
149
  try:
150
- # Use SCROT (Linux Native Tool) - 100% Reliable
151
- subprocess.run(["scrot", "/tmp/shot.jpg", "-q", "20", "-o"], check=True)
152
- with open("/tmp/shot.jpg", "rb") as f:
153
- img_data = f.read()
154
- return Response(img_data, mimetype='image/jpeg')
155
- except Exception as e:
156
- return str(e), 500
157
 
158
  @app.route('/interact', methods=['POST'])
159
  def interact():
160
  data = request.json
161
  action = data.get('action')
162
-
163
  try:
164
  if action == 'tap':
165
- # Map Percentage to Screen Resolution (1024x768)
166
- x = int(data.get('x') * 1024)
167
- y = int(data.get('y') * 768)
168
  pyautogui.click(x, y)
169
-
170
- elif action == 'type':
171
- pyautogui.write(data.get('text'))
172
-
173
  elif action == 'key':
174
  k = data.get('key')
175
  if k == 'win': pyautogui.press('super')
176
  else: pyautogui.press(k)
177
-
178
- elif action == 'launch':
179
- app_name = data.get('app')
180
- if app_name == 'terminal':
181
- subprocess.Popen("xfce4-terminal &", shell=True)
182
- elif app_name == 'chrome':
183
- subprocess.Popen("chromium-browser --no-sandbox &", shell=True)
184
-
185
- return "OK"
186
- except Exception as e:
187
- return str(e), 500
188
-
189
- if __name__ == '__main__':
190
- app.run(host='0.0.0.0', port=7860, threaded=True)
 
1
  import os
2
  import time
3
+ # Set Display FIRST
4
+ os.environ["DISPLAY"] = ":99"
5
+ os.environ["XAUTHORITY"] = "/root/.Xauthority"
6
+
7
+ import base64
8
  import subprocess
9
+ from flask import Flask, request, jsonify
10
+ from flask_cors import CORS
11
  import pyautogui
12
 
 
 
 
13
  app = Flask(__name__)
14
+ CORS(app)
15
+
16
+ SCREEN_WIDTH = 1024
17
+ SCREEN_HEIGHT = 768
18
 
 
19
  pyautogui.FAILSAFE = False
20
+ pyautogui.PAUSE = 0.1
21
 
22
+ # EMBEDDED HTML UI
23
  HTML_TEMPLATE = """
24
  <!DOCTYPE html>
25
  <html>
 
27
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
28
  <title>Cloud PC</title>
29
  <style>
30
+ body { margin: 0; background: #111; height: 100vh; display: flex; flex-direction: column; overflow: hidden; user-select: none; }
31
+ .screen-container { flex: 1; display: flex; justify-content: center; background: black; }
32
+ #desktop { width: 100%; height: 100%; object-fit: contain; }
33
+ .toolbar { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(255,255,255,0.2); padding: 10px; border-radius: 30px; display: flex; gap: 15px; }
34
+ .btn { width: 40px; height: 40px; border-radius: 50%; background: #333; color: white; display: flex; align-items: center; justify-content: center; cursor: pointer; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  </style>
36
  </head>
37
  <body>
 
38
  <div class="screen-container">
39
  <img id="desktop" draggable="false">
 
40
  <div class="toolbar">
41
+ <div class="btn" onclick="sendKey('win')">⊞</div>
42
+ <div class="btn" onclick="sendKey('enter')"></div>
43
+ <div class="btn" onclick="launchApp('chrome')">🌐</div>
 
 
44
  </div>
45
  </div>
 
 
 
 
 
 
46
  <script>
47
  const img = document.getElementById('desktop');
48
+ function refresh() { img.src = '/snapshot?' + new Date().getTime(); }
49
+ setInterval(refresh, 800);
 
 
 
 
50
 
 
51
  img.addEventListener('click', async (e) => {
52
  const rect = img.getBoundingClientRect();
 
53
  if (e.clientX < rect.left || e.clientX > rect.right) return;
 
54
  const x = (e.clientX - rect.left) / rect.width;
55
  const y = (e.clientY - rect.top) / rect.height;
56
+ await fetch('/interact', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({action: 'tap', x: x, y: y}) });
 
 
 
 
 
57
  refresh();
58
  });
59
 
 
60
  async function sendKey(k) {
61
+ await fetch('/interact', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({action: 'key', key: k}) });
 
 
 
 
62
  refresh();
63
  }
 
64
  async function launchApp(app) {
65
+ await fetch('/interact', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({action: 'launch', app: app}) });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
67
  </script>
68
  </body>
 
70
  """
71
 
72
  @app.route('/')
73
+ def home(): return HTML_TEMPLATE
 
74
 
75
+ @app.route('/snapshot', methods=['GET'])
76
+ def get_snapshot():
77
  try:
78
+ subprocess.run(["scrot", "/tmp/screen.jpg", "-q", "20", "-o"], check=True)
79
+ with open("/tmp/screen.jpg", "rb") as image_file:
80
+ return Response(image_file.read(), mimetype='image/jpeg')
81
+ except: return "Error", 500
 
 
 
82
 
83
  @app.route('/interact', methods=['POST'])
84
  def interact():
85
  data = request.json
86
  action = data.get('action')
 
87
  try:
88
  if action == 'tap':
89
+ x = int(data.get('x') * SCREEN_WIDTH)
90
+ y = int(data.get('y') * SCREEN_HEIGHT)
 
91
  pyautogui.click(x, y)
 
 
 
 
92
  elif action == 'key':
93
  k = data.get('key')
94
  if k == 'win': pyautogui.press('super')
95
  else: pyautogui.press(k)
96
+ elif action == 'launch':