flaskdockernew / app3.py
MySafeCode's picture
Rename app.py to app3.py
2ddc21d verified
import pygame
import time
import threading
import os
from flask import Flask, Response, render_template_string
os.environ['SDL_VIDEODRIVER'] = 'dummy'
pygame.init()
print("✅ PyGame initialized")
WIDTH, HEIGHT = 400, 300
shared = {
"streaming": True,
"x": 200,
"y": 150,
"frame_count": 0
}
def game_loop():
"""Generate frames continuously"""
screen = pygame.Surface((WIDTH, HEIGHT))
x, speed = 200, 5
while shared["streaming"]:
start_time = time.time()
# Update position
x += speed
if x < 30 or x > WIDTH-30:
speed *= -1
# Draw frame
screen.fill((25, 25, 45))
pygame.draw.circle(screen, (255, 80, 80), (int(x), int(shared["y"])), 20)
pygame.draw.circle(screen, (255, 255, 255), (int(x), int(shared["y"])), 20, 2)
# Store latest frame as RGB bytes
shared["current_frame"] = pygame.image.tostring(screen, 'RGB')
shared["x"] = x
shared["frame_count"] += 1
# Log every 30 frames
if shared["frame_count"] % 30 == 0:
print(f"Frame {shared['frame_count']}: X={x}")
# Maintain 30 FPS
elapsed = time.time() - start_time
if elapsed < 1.0/30:
time.sleep(1.0/30 - elapsed)
app = Flask(__name__)
HTML = '''<!DOCTYPE html>
<html>
<head>
<style>
body {
background: #0f172a;
color: white;
text-align: center;
padding: 20px;
font-family: monospace;
margin: 0;
overflow: hidden;
}
canvas {
border: 3px solid #60a5fa;
image-rendering: pixelated;
background: black;
}
.stats {
background: #1e293b;
padding: 10px;
border-radius: 5px;
margin-top: 10px;
display: inline-block;
}
</style>
</head>
<body>
<h1>🎮 Binary Stream</h1>
<canvas id="canvas" width="400" height="300"></canvas>
<div class="stats">
Frame: <span id="counter">0</span> |
Position: X=<span id="pos">200</span> |
FPS: <span id="fps">0</span>
</div>
<div style="margin-top: 10px; color: #94a3b8;">
Streaming raw RGB data • 30 FPS • No caching
</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let frameCounter = 0;
let fpsCounter = 0;
let lastFpsUpdate = Date.now();
let connectionActive = false;
// Connect to binary stream
function connectStream() {
const eventSource = new EventSource('/stream');
eventSource.onopen = () => {
console.log('✅ Connected to stream');
connectionActive = true;
document.title = '🎮 Stream • Connected';
};
eventSource.onmessage = (event) => {
if (!connectionActive) return;
try {
// Parse the binary frame data
const data = JSON.parse(event.data);
// Convert base64 to binary
const binaryString = atob(data.frame);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Create ImageData from RGB bytes
const imageData = new ImageData(400, 300);
for (let i = 0; i < bytes.length; i++) {
imageData.data[i] = bytes[i];
}
// Draw to canvas
ctx.putImageData(imageData, 0, 0);
// Update stats
frameCounter++;
fpsCounter++;
document.getElementById('counter').textContent = data.frame_count;
document.getElementById('pos').textContent = data.x;
// Calculate FPS
const now = Date.now();
if (now - lastFpsUpdate >= 1000) {
const fps = fpsCounter / ((now - lastFpsUpdate) / 1000);
document.getElementById('fps').textContent = fps.toFixed(1);
fpsCounter = 0;
lastFpsUpdate = now;
}
} catch (err) {
console.error('Frame error:', err);
}
};
eventSource.onerror = (err) => {
console.log('❌ Stream error, reconnecting...');
connectionActive = false;
document.title = '🎮 Stream • Reconnecting...';
eventSource.close();
// Show error on canvas
ctx.fillStyle = '#5c1a1a';
ctx.fillRect(0, 0, 400, 300);
ctx.fillStyle = 'white';
ctx.font = '20px monospace';
ctx.fillText('Reconnecting...', 120, 150);
// Reconnect after 1 second
setTimeout(connectStream, 1000);
};
return eventSource;
}
// Start connection
let streamConnection = connectStream();
// Manual reconnect button (optional)
window.reconnect = function() {
if (streamConnection) streamConnection.close();
streamConnection = connectStream();
};
console.log('Binary stream client started');
</script>
</body>
</html>'''
@app.route('/')
def index():
return render_template_string(HTML)
@app.route('/stream')
def stream():
"""Server-Sent Events stream with base64 encoded frames"""
import json
import base64
def generate():
last_sent = 0
while shared.get("streaming", True):
# Only send if frame has updated
if shared.get("frame_count", 0) > last_sent and "current_frame" in shared:
frame_data = {
'frame': base64.b64encode(shared["current_frame"]).decode('utf-8'),
'frame_count': shared["frame_count"],
'x': shared["x"],
'timestamp': time.time()
}
yield f"data: {json.dumps(frame_data)}\n\n"
last_sent = shared["frame_count"]
# Check for updates 30 times per second
time.sleep(1.0/30)
return Response(
generate(),
mimetype='text/event-stream',
headers={
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no'
}
)
if __name__ == "__main__":
print("🚀 Starting binary stream server...")
print("Expecting 30 FPS updates via SSE")
# Create initial frame
screen = pygame.Surface((WIDTH, HEIGHT))
screen.fill((25, 25, 45))
pygame.draw.circle(screen, (255, 80, 80), (200, 150), 20)
shared["current_frame"] = pygame.image.tostring(screen, 'RGB')
# Start game thread
thread = threading.Thread(target=game_loop, daemon=True)
thread.start()
# Give it a moment
time.sleep(0.5)
# Start server
app.run(
host='0.0.0.0',
port=int(os.environ.get('PORT', 7860)),
debug=False,
threaded=True
)