flaskdockernew / app.py
MySafeCode's picture
Update app.py
1e2288a verified
#!/usr/bin/env python3
# complete_fixed_demo.py - Fixed with PyGame init and animation
import os
import pygame
import numpy as np
import time
import threading
from flask import Flask, Response
# ===== CRITICAL FIXES =====
os.environ['SDL_VIDEODRIVER'] = 'dummy'
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'
PORT = int(os.getenv('PORT', 7860))
WIDTH, HEIGHT = 800, 600
app = Flask(__name__)
# Shared state
frame_data = {
"pixels": None,
"frame_id": 0,
"lock": threading.Lock()
}
@app.route('/')
def index():
return '''<!DOCTYPE html>
<html>
<head>
<title>Complete Demo</title>
<style>
body { margin: 0; padding: 20px; background: #000; color: white; font-family: monospace; text-align: center; }
canvas { border: 3px solid #0af; background: black; display: block; margin: 20px auto; }
.mode { background: #222; padding: 15px; border-radius: 8px; display: inline-block; margin: 10px; }
</style>
</head>
<body>
<h1 style="color: #0af;">⚡ Complete Demo</h1>
<div class="mode" id="mode">Mode: <span id="modeText">Loading...</span></div>
<canvas id="canvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let mode = 'pygame'; // 'canvas' or 'pygame'
let lastTime = 0;
let frameCount = 0;
// ===== CANVAS EFFECTS =====
function drawCanvasEffect(currentTime) {
const delta = (currentTime - lastTime) / 1000;
lastTime = currentTime;
// Clear with fade effect
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, 800, 600);
// Time in seconds
const t = currentTime / 1000;
// Draw multiple rotating shapes
for (let i = 0; i < 20; i++) {
const angle = t * (1 + i * 0.2);
const radius = 50 + i * 15;
const x = 400 + Math.cos(angle) * radius;
const y = 300 + Math.sin(angle) * radius;
const size = 10 + Math.sin(t * 3 + i) * 8;
// Rainbow colors
const hue = (t * 50 + i * 18) % 360;
ctx.fillStyle = `hsl(${hue}, 100%, 60%)`;
// Different shapes
if (i % 3 === 0) {
// Circle
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fill();
} else if (i % 3 === 1) {
// Square
ctx.fillRect(x - size, y - size, size * 2, size * 2);
} else {
// Triangle
ctx.beginPath();
ctx.moveTo(x, y - size);
ctx.lineTo(x - size, y + size);
ctx.lineTo(x + size, y + size);
ctx.closePath();
ctx.fill();
}
// Connecting lines
if (i > 0) {
const prevAngle = t * (1 + (i - 1) * 0.2);
const prevX = 400 + Math.cos(prevAngle) * radius;
const prevY = 300 + Math.sin(prevAngle) * radius;
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(x, y);
ctx.strokeStyle = `hsla(${hue}, 100%, 60%, 0.5)`;
ctx.lineWidth = 2;
ctx.stroke();
}
}
// Center pulsing circle
const pulseSize = 30 + Math.sin(t * 5) * 15;
ctx.fillStyle = `hsl(${t * 100 % 360}, 100%, 60%)`;
ctx.beginPath();
ctx.arc(400, 300, pulseSize, 0, Math.PI * 2);
ctx.fill();
// Add text
ctx.fillStyle = 'white';
ctx.font = 'bold 24px monospace';
ctx.textAlign = 'center';
ctx.fillText('CANVAS EFFECTS DEMO', 400, 50);
ctx.font = '16px monospace';
ctx.fillText(`Frame: ${frameCount} | Time: ${t.toFixed(1)}s`, 400, 80);
frameCount++;
}
// ===== PYGAME STREAM =====
function loadPyGameFrame() {
fetch('/pygame_frame')
.then(r => r.arrayBuffer())
.then(buffer => {
const imageData = new ImageData(800, 600);
const rgba = imageData.data;
const rgb = new Uint8Array(buffer);
for (let i = 0, j = 0; i < rgba.length; i += 4, j += 3) {
rgba[i] = rgb[j];
rgba[i + 1] = rgb[j + 1];
rgba[i + 2] = rgb[j + 2];
rgba[i + 3] = 255;
}
ctx.putImageData(imageData, 0, 0);
// Add overlay text
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.font = 'bold 24px monospace';
ctx.textAlign = 'center';
ctx.fillText('PYGAME STREAM', 400, 50);
})
.catch(err => console.error('PyGame error:', err));
}
// ===== RENDER LOOP =====
function render(currentTime) {
if (mode === 'canvas') {
drawCanvasEffect(currentTime);
} else {
loadPyGameFrame();
}
requestAnimationFrame(render);
}
// ===== CONTROLS =====
function switchToCanvas() {
mode = 'canvas';
document.getElementById('modeText').textContent = 'CANVAS EFFECTS';
document.getElementById('modeText').style.color = '#0af';
}
function switchToPyGame() {
mode = 'pygame';
document.getElementById('modeText').textContent = 'PYGAME STREAM';
document.getElementById('modeText').style.color = '#ff3366';
}
// Auto-switch every 5 seconds
setInterval(() => {
if (mode === 'canvas') {
switchToPyGame();
} else {
switchToCanvas();
}
}, 5000);
// Initialize
switchToCanvas();
requestAnimationFrame(render);
</script>
</body>
</html>'''
@app.route('/pygame_frame')
def pygame_frame():
"""PyGame animation endpoint"""
with frame_data["lock"]:
if frame_data["pixels"] is not None:
return Response(
frame_data["pixels"].tobytes(),
mimetype='application/octet-stream',
headers={'Cache-Control': 'no-cache'}
)
# Fallback
black = np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8).tobytes()
return Response(black, mimetype='application/octet-stream')
def pygame_animation():
"""PyGame animation with proper initialization"""
print("🎬 Starting PyGame animation...")
try:
# ===== FIX 1: Proper PyGame initialization =====
pygame.init()
# Initialize display (CRITICAL for headless)
pygame.display.init()
pygame.display.set_mode((1, 1), pygame.HIDDEN) # Hidden window
print("✅ PyGame display initialized")
# Create surface
surface = pygame.Surface((WIDTH, HEIGHT))
print(f"✅ Surface created: {WIDTH}x{HEIGHT}")
except Exception as e:
print(f"❌ PyGame init failed: {e}")
import traceback
traceback.print_exc()
return
# ===== FIX 2: Animated objects =====
particles = []
for _ in range(100):
particles.append({
'x': np.random.randint(0, WIDTH),
'y': np.random.randint(0, HEIGHT),
'dx': np.random.uniform(-3, 3),
'dy': np.random.uniform(-3, 3),
'size': np.random.randint(2, 10),
'color': (
np.random.randint(50, 255),
np.random.randint(50, 255),
np.random.randint(50, 255)
),
'life': np.random.randint(100, 300),
'type': np.random.choice(['circle', 'square', 'triangle'])
})
# Rotating geometric shapes
shapes = []
for i in range(8):
shapes.append({
'angle': i * np.pi / 4,
'radius': 100 + i * 20,
'speed': 0.5 + i * 0.1,
'size': 15 + i * 5,
'color': (
(i * 32) % 256,
(i * 64) % 256,
(i * 96) % 256
)
})
print(f"✅ Created {len(particles)} particles and {len(shapes)} shapes")
frame_id = 0
start_time = time.time()
while True:
frame_start = time.time()
# ===== FIX 3: Time-based animation =====
elapsed = time.time() - start_time
# Clear with gradient
for y in range(HEIGHT):
color = int(20 + y / HEIGHT * 30)
pygame.draw.line(surface, (color, color, color + 20),
(0, y), (WIDTH, y))
# Draw rotating shapes
for shape in shapes:
shape['angle'] += shape['speed'] * 0.05
x = WIDTH // 2 + np.cos(shape['angle']) * shape['radius']
y = HEIGHT // 2 + np.sin(shape['angle']) * shape['radius']
# Draw shape
if np.random.random() < 0.3: # 30% chance of triangle
points = [
(x, y - shape['size']),
(x - shape['size'], y + shape['size']),
(x + shape['size'], y + shape['size'])
]
pygame.draw.polygon(surface, shape['color'], points)
else:
pygame.draw.circle(surface, shape['color'],
(int(x), int(y)), shape['size'])
# Connect shapes with lines
pygame.draw.circle(surface, (255, 255, 255, 100),
(int(x), int(y)), shape['size'], 2)
# Update and draw particles
for p in particles:
p['x'] += p['dx']
p['y'] += p['dy']
p['life'] -= 1
# Bounce or respawn
if (p['x'] < 0 or p['x'] > WIDTH or
p['y'] < 0 or p['y'] > HEIGHT or
p['life'] <= 0):
p['x'] = np.random.randint(0, WIDTH)
p['y'] = np.random.randint(0, HEIGHT)
p['dx'] = np.random.uniform(-3, 3)
p['dy'] = np.random.uniform(-3, 3)
p['life'] = np.random.randint(100, 300)
p['color'] = (
np.random.randint(50, 255),
np.random.randint(50, 255),
np.random.randint(50, 255)
)
# Draw particle with trail
for i in range(3):
trail_x = p['x'] - p['dx'] * i * 0.3
trail_y = p['y'] - p['dy'] * i * 0.3
trail_size = p['size'] * (1 - i * 0.3)
trail_alpha = 200 - i * 60
pygame.draw.circle(
surface,
(*p['color'], trail_alpha),
(int(trail_x), int(trail_y)),
int(trail_size)
)
# Add frame info
font = pygame.font.Font(None, 28)
info = [
f"Frame: {frame_id}",
f"Time: {elapsed:.1f}s",
f"FPS: {int(1/(time.time()-frame_start)) if frame_id>0 else 0}",
f"Particles: {len(particles)}"
]
for i, text in enumerate(info):
text_surface = font.render(text, True, (255, 255, 200))
surface.blit(text_surface, (10, 10 + i * 30))
# Convert to NumPy array
pixels = pygame.surfarray.pixels3d(surface)
# Update shared memory
with frame_data["lock"]:
frame_data["pixels"] = pixels.copy()
frame_data["frame_id"] = frame_id
frame_id += 1
# Log every 30 frames
if frame_id % 30 == 0:
current_fps = 1 / (time.time() - frame_start) if frame_id > 0 else 0
print(f"📊 Frame {frame_id} | FPS: {current_fps:.1f}")
# ===== FIX 4: Frame rate control =====
frame_time = time.time() - frame_start
target_time = 1/30 # 30 FPS
if frame_time < target_time:
time.sleep(target_time - frame_time)
# Start PyGame thread
threading.Thread(target=pygame_animation, daemon=True).start()
# Wait for initialization
print("⏳ Waiting for PyGame to start...")
time.sleep(3)
print("🌐 Starting Flask server...")
print(f"📡 Port: {PORT}")
print("="*60)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=PORT, threaded=True, use_reloader=False)