flaskdockernew / app4.py
MySafeCode's picture
Rename app.py to app4.py
1270c10 verified
Raw
History Blame
8.56 kB
import pygame
import time
import threading
import numpy as np
import base64
import zlib
import os
from flask import Flask, render_template_string
from flask_socketio import SocketIO, emit
# ===== PYGAME SETUP =====
os.environ['SDL_VIDEODRIVER'] = 'dummy'
pygame.init()
print("βœ… PyGame initialized with surfarray")
WIDTH, HEIGHT = 400, 300
# Create surface
screen = pygame.Surface((WIDTH, HEIGHT))
print(f"βœ… Surface: {WIDTH}x{HEIGHT}")
# Test surfarray
print("πŸ§ͺ Testing surfarray...")
test_arr = pygame.surfarray.array3d(screen)
print(f"βœ… Surfarray works! Shape: {test_arr.shape}, Type: {test_arr.dtype}")
# ===== FLASK/SOCKETIO =====
app = Flask(__name__)
app.config['SECRET_KEY'] = 'surfarray_stream'
socketio = SocketIO(app, async_mode='threading', cors_allowed_origins="*")
# Shared state
shared = {
"running": True,
"x": 200,
"frame_count": 0,
"clients": 0
}
# ===== SURFARRAY THREAD =====
def surfarray_thread():
"""Send raw pixel arrays via WebSocket"""
print("πŸ“Š Surfarray thread STARTED")
x, speed = 200, 5
fps_counter = 0
last_log = time.time()
while shared["running"]:
try:
start_time = time.time()
# 1. UPDATE POSITION
x += speed
if x < 30 or x > WIDTH-30:
speed *= -1
# 2. DRAW
screen.fill((25, 25, 45)) # Dark blue
pygame.draw.circle(screen, (255, 80, 80), (int(x), 150), 20)
pygame.draw.circle(screen, (255, 255, 255), (int(x), 150), 20, 2)
# 3. GET PIXEL ARRAY (FAST!)
pixel_array = pygame.surfarray.array3d(screen) # Shape: (400, 300, 3)
# 4. COMPRESS (optional but recommended)
# Flatten and compress the array
flat_data = pixel_array.flatten().tobytes()
compressed = zlib.compress(flat_data, level=1) # Fast compression
encoded = base64.b64encode(compressed).decode('utf-8')
# 5. BROADCAST
shared["x"] = x
shared["frame_count"] += 1
socketio.emit('pixel_data', {
'pixels': encoded,
'shape': [WIDTH, HEIGHT, 3], # Send dimensions
'frame': shared["frame_count"],
'x': x,
'compressed': True
})
# 6. LOGGING
fps_counter += 1
current_time = time.time()
if current_time - last_log >= 1.0:
print(f"πŸ“Š Sent {fps_counter} frames | Frame {shared['frame_count']} | X={x}")
fps_counter = 0
last_log = current_time
# 7. FRAME RATE
elapsed = time.time() - start_time
if elapsed < 1.0/30:
time.sleep(1.0/30 - elapsed)
except Exception as e:
print(f"❌ Surfarray thread error: {e}")
import traceback
traceback.print_exc()
break
print("πŸ›‘ Surfarray thread STOPPED")
# ===== START THREAD =====
thread = threading.Thread(target=surfarray_thread, daemon=True)
thread.start()
print(f"βœ… Thread alive: {thread.is_alive()}")
# ===== FLASK ROUTES =====
@app.route('/')
def index():
return render_template_string('''
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.socket.io/4.5.0/socket.io.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako.min.js"></script>
<style>
body { background: #0f172a; color: white; text-align: center; padding: 20px; }
canvas {
border: 3px solid #60a5fa;
width: 400px;
height: 300px;
image-rendering: pixelated;
}
.stats {
background: #1e293b;
padding: 15px;
border-radius: 10px;
margin: 20px auto;
width: 400px;
}
</style>
</head>
<body>
<h1>πŸ“Š Surfarray Stream</h1>
<canvas id="pixelCanvas" width="400" height="300"></canvas>
<div class="stats">
<div>Frame: <span id="frame">0</span></div>
<div>Circle X: <span id="pos">200</span></div>
<div>Status: <span id="status">Connecting...</span></div>
<div>FPS: <span id="fps">0</span></div>
<div>Data size: <span id="size">0 KB</span></div>
</div>
<script>
const socket = io();
const canvas = document.getElementById('pixelCanvas');
const ctx = canvas.getContext('2d');
let framesReceived = 0;
let lastFpsTime = Date.now();
let totalBytes = 0;
// WebSocket connection
socket.on('connect', () => {
console.log('βœ… Connected to surfarray stream');
document.getElementById('status').textContent = 'Connected';
document.getElementById('status').style.color = '#10b981';
});
// PIXEL DATA RECEIVED
socket.on('pixel_data', (data) => {
framesReceived++;
totalBytes += data.pixels.length;
// Decode base64
const binaryString = atob(data.pixels);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Decompress if needed
let pixelData;
if (data.compressed) {
try {
// Use pako for zlib decompression
pixelData = pako.inflate(bytes);
} catch (e) {
console.error('Decompression failed:', e);
return;
}
} else {
pixelData = bytes;
}
// Create ImageData
const [width, height] = data.shape;
const imageData = new ImageData(width, height);
// Copy pixel data (assuming RGB format)
for (let i = 0; i < pixelData.length; i++) {
imageData.data[i] = pixelData[i];
}
// Draw to canvas
ctx.putImageData(imageData, 0, 0);
// Update stats
document.getElementById('frame').textContent = data.frame;
document.getElementById('pos').textContent = data.x;
document.getElementById('size').textContent =
Math.round(data.pixels.length / 1024) + ' KB';
// Calculate FPS
const now = Date.now();
if (now - lastFpsTime >= 1000) {
document.getElementById('fps').textContent = framesReceived;
framesReceived = 0;
lastFpsTime = now;
}
// Visual feedback
canvas.style.borderColor = '#10b981';
setTimeout(() => canvas.style.borderColor = '#60a5fa', 50);
});
socket.on('disconnect', () => {
document.getElementById('status').textContent = 'Disconnected';
document.getElementById('status').style.color = '#ef4444';
});
console.log('Surfarray client ready');
</script>
</body>
</html>
''')
@socketio.on('connect')
def handle_connect():
shared['clients'] += 1
print(f"πŸ‘€ Client connected. Total: {shared['clients']}")
@socketio.on('disconnect')
def handle_disconnect():
shared['clients'] -= 1
print(f"πŸ‘€ Client disconnected. Total: {shared['clients']}")
if __name__ == '__main__':
print("πŸš€ Starting surfarray server...")
# Wait for thread
time.sleep(1)
print(f"πŸ“Š Initial: Thread alive={thread.is_alive()}, Frames={shared['frame_count']}")
socketio.run(
app,
host='0.0.0.0',
port=int(os.environ.get('PORT', 7860)),
debug=False,
use_reloader=False,
allow_unsafe_werkzeug=True
)