Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| # minimal_working.py - Simple guaranteed working version | |
| import pygame | |
| import time | |
| import threading | |
| import base64 | |
| import os | |
| from flask import Flask, Response | |
| import numpy | |
| # ===== 1. BASIC SETUP ===== | |
| os.environ['SDL_VIDEODRIVER'] = 'dummy' | |
| os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1' | |
| # Hugging Face uses port 7860 | |
| PORT = int(os.getenv('PORT', 7860)) | |
| # Simple shared state | |
| shared = { | |
| "latest_frame": None, | |
| "frame_count": 0, | |
| "streaming": True | |
| } | |
| # ===== 2. CREATE FLASK APP ===== | |
| app = Flask(__name__) | |
| # ===== 3. SIMPLE HTML ===== | |
| HTML = ''' | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>PyGame Stream</title> | |
| <style> | |
| body { margin: 0; padding: 20px; background: #111; color: white; font-family: Arial; } | |
| h1 { color: #4CAF50; } | |
| #streamImg { | |
| border: 3px solid #4CAF50; | |
| background: black; | |
| width: 400px; | |
| height: 300px; | |
| } | |
| .stats { margin-top: 20px; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>PyGame Stream - WORKING</h1> | |
| <img id="streamImg" src="/stream"> | |
| <div class="stats"> | |
| <div>Frames: <span id="frameCount">0</span></div> | |
| <div>Status: <span id="status">Loading...</span></div> | |
| </div> | |
| <script> | |
| function updateImage() { | |
| const img = document.getElementById('streamImg'); | |
| const timestamp = new Date().getTime(); | |
| // Force no cache | |
| img.src = '/stream?t=' + timestamp; | |
| // Update stats | |
| fetch('/stats') | |
| .then(r => r.json()) | |
| .then(data => { | |
| document.getElementById('frameCount').textContent = data.frame_count; | |
| document.getElementById('status').textContent = 'Streaming β'; | |
| document.getElementById('status').style.color = '#4CAF50'; | |
| }) | |
| .catch(e => { | |
| document.getElementById('status').textContent = 'Error: ' + e; | |
| document.getElementById('status').style.color = 'red'; | |
| }); | |
| } | |
| // Update every 100ms | |
| setInterval(updateImage, 100); | |
| updateImage(); | |
| </script> | |
| </body> | |
| </html> | |
| ''' | |
| def index(): | |
| return HTML | |
| def stats(): | |
| return {'frame_count': shared['frame_count']} | |
| def stream(): | |
| """Simple stream endpoint that definitely works""" | |
| if shared['latest_frame']: | |
| try: | |
| # Decode and serve | |
| image_data = base64.b64decode(shared['latest_frame']) | |
| return Response( | |
| image_data, | |
| mimetype='image/png', # Use PNG for reliability | |
| headers={ | |
| 'Cache-Control': 'no-cache, no-store, must-revalidate', | |
| 'Pragma': 'no-cache', | |
| 'Expires': '0' | |
| } | |
| ) | |
| except Exception as e: | |
| print(f"Stream error: {e}") | |
| # Return a simple test image | |
| return create_test_image() | |
| # No frame yet | |
| return create_test_image() | |
| def create_test_image(): | |
| """Create a simple test image that definitely works""" | |
| # Create a 2x2 pixel red image as fallback | |
| test_image = base64.b64decode('iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFElEQVR4nGJ89eoVAwMDIyMjAwMDAAY4AWWBtBcPAAAAAElFTkSuQmCC') | |
| return Response( | |
| test_image, | |
| mimetype='image/png', | |
| headers={'Cache-Control': 'no-cache'} | |
| ) | |
| # ===== 4. SIMPLE CAPTURE LOOP ===== | |
| def simple_capture(): | |
| """Simple guaranteed-working capture loop""" | |
| try: | |
| pygame.init() | |
| print("β PyGame initialized") | |
| except Exception as e: | |
| print(f"PyGame init error: {e}") | |
| return | |
| # Create surface | |
| W, H = 400, 300 | |
| surface = pygame.Surface((W, H)) | |
| # Simple animation | |
| x, y = W//2, H//2 | |
| dx, dy = 3, 2 | |
| print("π¬ Starting capture...") | |
| while shared["streaming"]: | |
| try: | |
| # Clear with black | |
| surface.fill((0, 0, 0)) | |
| # Update position | |
| x += dx | |
| y += dy | |
| # Bounce | |
| if x < 20 or x > W-20: | |
| dx = -dx | |
| if y < 20 or y > H-20: | |
| dy = -dy | |
| # Draw a moving rectangle | |
| pygame.draw.rect(surface, (255, 0, 0), (x-20, y-20, 40, 40)) | |
| # Draw frame number | |
| font = pygame.font.Font(None, 36) | |
| text = font.render(f"Frame: {shared['frame_count']}", True, (255, 255, 255)) | |
| surface.blit(text, (10, 10)) | |
| # Draw time | |
| time_text = font.render(time.strftime("%H:%M:%S"), True, (200, 200, 255)) | |
| surface.blit(time_text, (10, 50)) | |
| # Save to buffer | |
| import io | |
| buffer = io.BytesIO() | |
| pygame.image.save(surface, buffer, 'PNG') # Use PNG for reliability | |
| buffer.seek(0) | |
| # Encode and store | |
| shared["latest_frame"] = base64.b64encode(buffer.getvalue()).decode('utf-8') | |
| shared["frame_count"] += 1 | |
| # Log every 30 frames | |
| if shared['frame_count'] % 30 == 0: | |
| print(f"Frame {shared['frame_count']} captured") | |
| # Control frame rate | |
| time.sleep(1/30) # 30 FPS | |
| except Exception as e: | |
| print(f"Capture error: {e}") | |
| break | |
| pygame.quit() | |
| print("Capture stopped") | |
| # ===== 5. MAIN ===== | |
| def main(): | |
| print("="*50) | |
| print("π Starting PyGame + Flask (MINIMAL WORKING VERSION)") | |
| print(f"π‘ Port: {PORT}") | |
| print("="*50) | |
| # Start capture thread | |
| thread = threading.Thread(target=simple_capture, daemon=True) | |
| thread.start() | |
| # Give it time to start | |
| time.sleep(1) | |
| # Start Flask | |
| print("π Starting Flask server...") | |
| try: | |
| app.run( | |
| host='0.0.0.0', | |
| port=PORT, | |
| debug=False, | |
| threaded=True, | |
| use_reloader=False | |
| ) | |
| except Exception as e: | |
| print(f"Server error: {e}") | |
| finally: | |
| shared['streaming'] = False | |
| thread.join(timeout=2) | |
| print(f"Total frames: {shared['frame_count']}") | |
| print("β Done") | |
| if __name__ == "__main__": | |
| main() |