flaskdockernew / app.py
MySafeCode's picture
Create app.py
9f8b91e verified
Raw
History Blame
7.03 kB
#!/usr/bin/env python3
# rotating_checkerboard.py - Simple rotating pattern
import numpy as np
import time
import threading
import os
from flask import Flask, Response
PORT = int(os.getenv('PORT', 7860))
WIDTH, HEIGHT = 400, 300
TILE_SIZE = 40
print(f"🚀 Starting rotating checkerboard on port {PORT}")
# Shared memory
shared = {
"pixels": np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8),
"frame_count": 0,
"streaming": True,
"lock": threading.Lock(),
"rotation": 0.0
}
app = Flask(__name__)
HTML = '''<!DOCTYPE html>
<html>
<head>
<title>Rotating Checkerboard</title>
<style>
body { margin: 0; padding: 20px; background: #111; color: white; font-family: Arial; text-align: center; }
canvas { border: 3px solid #0af; background: black; display: block; margin: 20px auto; }
.stats { background: #222; padding: 15px; border-radius: 8px; display: inline-block; margin: 10px; }
</style>
</head>
<body>
<h1>🔄 Rotating Checkerboard</h1>
<canvas id="canvas" width="400" height="300"></canvas>
<div class="stats">
<div>Frames: <span id="frameCount">0</span></div>
<div>Rotation: <span id="rotation">0°</span></div>
<div>Status: <span id="status">Loading...</span></div>
</div>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
function loadFrame() {
fetch('/frame')
.then(r => r.arrayBuffer())
.then(buffer => {
if (buffer.byteLength !== 400*300*3) {
console.error('Wrong size:', buffer.byteLength);
return;
}
const imageData = new ImageData(400, 300);
const rgba = imageData.data;
const rgb = new Uint8Array(buffer);
// Convert RGB to RGBA
for (let i = 0, j = 0; i < rgba.length; i += 4, j += 3) {
[i] = rgb[j];
rgba[i + 1] = rgb[j + 1];
rgba[i + 2] = rgb[j + 2];
rgba[i + 3] = 255;
}
ctx.putImageData(imageData, 0, 0);
// Update stats
fetch('/stats')
.then(r => r.json())
.then(data => {
document.getElementById('frameCount').textContent = data.frame_count;
document.getElementById('rotation').textContent = data.rotation.toFixed(1) + '°';
document.getElementById('status').textContent = 'Streaming ✓';
document.getElementById('status').style.color = '#0af';
});
})
.catch(err => {
console.error('Error:', err);
document.getElementById('status').textContent = 'Error';
document.getElementById('status').style.color = 'red';
});
}
setInterval(loadFrame, 33);
loadFrame();
</script>
</body>
</html>'''
@app.route('/')
def index():
return HTML
@app.route('/stats')
def stats():
with shared['lock']:
return {
'frame_count': shared['frame_count'],
'rotation': shared['rotation']
}
@app.route('/frame')
def get_frame():
"""Return rotating checkerboard"""
with shared['lock']:
# Create checkerboard with rotation
pixels = shared['pixels']
# Update rotation
shared['rotation'] = (shared['rotation'] + 1.0) % 360.0
rotation_rad = np.radians(shared['rotation'])
# Center coordinates
center_x = WIDTH / 2
center_y = HEIGHT / 2
# Create coordinate grids
y_coords, x_coords = np.mgrid[:HEIGHT, :WIDTH]
# Translate to center
x_rel = x_coords - center_x
y_rel = y_coords - center_y
# Apply rotation
x_rot = x_rel * np.cos(rotation_rad) - y_rel * np.sin(rotation_rad)
y_rot = x_rel * np.sin(rotation_rad) + y_rel * np.cos(rotation_rad)
# Translate back
x_rot += center_x
y_rot += center_y
# Create rotated checkerboard
tile_x = (x_rot // TILE_SIZE).astype(int)
tile_y = (y_rot // TILE_SIZE).astype(int)
# Checker pattern
checker = ((tile_x + tile_y) % 2 == 0)
# Set colors
pixels[:, :] = [30, 30, 60] # Dark blue background
# White tiles
pixels[checker, 0] = 255 # R
pixels[checker, 1] = 255 # G
pixels[checker, 2] = 255 # B
# Black tiles already set
# Add a colored rotating dot
dot_x = int(center_x + np.cos(rotation_rad * 2) * 100)
dot_y = int(center_y + np.sin(rotation_rad * 2) * 80)
# Draw dot
dot_mask = ((x_coords - dot_x)**2 + (y_coords - dot_y)**2) < 15**2
pixels[dot_mask, 0] = 255 # Red
pixels[dot_mask, 1] = 100 # Green
pixels[dot_mask, 2] = 100 # Blue
# Add frame counter text (simulated with colored pixels)
frame_num = shared['frame_count']
if frame_num < 100: # Simple text in top-left
for i in range(min(10, len(str(frame_num)))):
digit = int(str(frame_num)[i]) if i < len(str(frame_num)) else 0
for dy in range(7):
for dx in range(5):
if 10 + dy*10 < HEIGHT and 10 + i*30 + dx*5 < WIDTH:
pixels[10 + dy*10, 10 + i*30 + dx*5] = [100 + digit*15, 200, 100]
# Convert to bytes
pixel_bytes = pixels.tobytes()
shared['frame_count'] += 1
print(f"📤 Frame {shared['frame_count']}, Rotation: {shared['rotation']:.1f}°")
return Response(
pixel_bytes,
mimetype='application/octet-stream',
headers={'Cache-Control': 'no-cache'}
)
def update_pattern():
"""Thread that continuously updates the pattern"""
while shared['streaming']:
# The pattern is generated on-demand in /frame endpoint
# So we just sleep here
time.sleep(0.01)
def main():
print("="*60)
print("🔄 Rotating Checkerboard Server")
print("="*60)
print(f"📡 Port: {PORT}")
print(f"🎨 Resolution: {WIDTH}x{HEIGHT}")
print(f"🧱 Tile size: {TILE_SIZE}")
print("="*60)
# Start update thread
update_thread = threading.Thread(target=update_pattern, daemon=True)
update_thread.start()
# Start Flask
app.run(host='0.0.0.0', port=PORT, threaded=True, use_reloader=False)
if __name__ == "__main__":
main()