Spaces:
Sleeping
Sleeping
| from flask import Flask, request, jsonify | |
| from flask_cors import CORS | |
| import uuid | |
| import os | |
| from datetime import datetime | |
| from config import config | |
| from concurrent.futures import ThreadPoolExecutor | |
| import threading | |
| from audio_processor import get_processor | |
| app = Flask(__name__) | |
| CORS(app) # Enable CORS for Streamlit | |
| # Thread pool for background processing | |
| executor = ThreadPoolExecutor(max_workers=4) | |
| # In-memory storage for job status | |
| jobs = {} | |
| jobs_lock = threading.Lock() | |
| # Preload model on startup | |
| print("=" * 60) | |
| print("INITIALIZING APPLICATION...") | |
| print("=" * 60) | |
| try: | |
| print("Preloading emotion detection model...") | |
| processor = get_processor() | |
| processor.load_model() | |
| print("✅ Model preloaded successfully!") | |
| print("=" * 60) | |
| except Exception as e: | |
| print(f"⚠️ Warning: Failed to preload model: {e}") | |
| print("Model will be loaded on first request.") | |
| print("=" * 60) | |
| # Upload folder for temporary audio files | |
| UPLOAD_FOLDER = 'uploads' | |
| os.makedirs(UPLOAD_FOLDER, exist_ok=True) | |
| app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER | |
| app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 # 50MB max file size | |
| # Allowed audio extensions | |
| ALLOWED_EXTENSIONS = {'wav', 'mp3', 'ogg', 'flac', 'm4a'} | |
| def allowed_file(filename): | |
| """Check if file extension is allowed""" | |
| return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS | |
| def health_check(): | |
| """Health check endpoint""" | |
| return jsonify({ | |
| "status": "healthy", | |
| "timestamp": datetime.now().isoformat(), | |
| "model": config.MODEL_NAME, | |
| "version": "1.0.0" | |
| }) | |
| def get_config(): | |
| """Get current configuration""" | |
| return jsonify({ | |
| "model_name": config.MODEL_NAME, | |
| "chunk_duration": config.CHUNK_DURATION, | |
| "sample_rate": config.SAMPLE_RATE, | |
| "emotions": config.EMOTIONS | |
| }) | |
| def upload_audio(): | |
| """ | |
| Upload audio file and start processing | |
| Returns job_id for tracking progress | |
| """ | |
| # Check if file is present in request | |
| if 'file' not in request.files: | |
| return jsonify({"error": "No file provided"}), 400 | |
| file = request.files['file'] | |
| # Check if file is selected | |
| if file.filename == '': | |
| return jsonify({"error": "No file selected"}), 400 | |
| # Check if file type is allowed | |
| if not allowed_file(file.filename): | |
| return jsonify({ | |
| "error": f"Invalid file type. Allowed: {', '.join(ALLOWED_EXTENSIONS)}" | |
| }), 400 | |
| # Generate unique job ID | |
| job_id = str(uuid.uuid4()) | |
| # Save file | |
| filename = f"{job_id}_{file.filename}" | |
| filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) | |
| file.save(filepath) | |
| # Initialize job status | |
| with jobs_lock: | |
| jobs[job_id] = { | |
| "status": "queued", | |
| "progress": 0, | |
| "message": "Audio file uploaded, waiting to process...", | |
| "filename": file.filename, | |
| "filepath": filepath, | |
| "created_at": datetime.now().isoformat() | |
| } | |
| # Submit background processing task | |
| executor.submit(process_audio, job_id, filepath) | |
| return jsonify({ | |
| "job_id": job_id, | |
| "message": "File uploaded successfully, processing started" | |
| }), 202 | |
| def get_status(job_id): | |
| """ | |
| Get processing status for a job | |
| Returns progress and results when complete | |
| """ | |
| if job_id not in jobs: | |
| return jsonify({"error": "Job not found"}), 404 | |
| job = jobs[job_id] | |
| response = { | |
| "job_id": job_id, | |
| "status": job["status"], | |
| "progress": job["progress"], | |
| "message": job["message"] | |
| } | |
| # If completed, include results | |
| if job["status"] == "completed": | |
| response["results"] = job.get("results", {}) | |
| # If failed, include error | |
| if job["status"] == "failed": | |
| response["error"] = job.get("error", "Unknown error") | |
| return jsonify(response) | |
| def process_audio(job_id, filepath): | |
| """ | |
| Process audio file and extract emotions | |
| This runs in a background thread | |
| """ | |
| try: | |
| # Get audio processor | |
| processor = get_processor() | |
| # Progress callback function | |
| def update_progress(progress, message): | |
| with jobs_lock: | |
| jobs[job_id]["progress"] = progress | |
| jobs[job_id]["message"] = message | |
| # Update status to processing | |
| with jobs_lock: | |
| jobs[job_id]["status"] = "processing" | |
| # Process audio file with real ML model | |
| results = processor.process_audio_file(filepath, progress_callback=update_progress) | |
| # Mark as completed | |
| with jobs_lock: | |
| jobs[job_id]["progress"] = 100 | |
| jobs[job_id]["status"] = "completed" | |
| jobs[job_id]["message"] = "Analysis complete!" | |
| jobs[job_id]["results"] = results | |
| # Clean up uploaded file after processing | |
| try: | |
| os.remove(filepath) | |
| except: | |
| pass | |
| except Exception as e: | |
| with jobs_lock: | |
| jobs[job_id]["status"] = "failed" | |
| jobs[job_id]["progress"] = 0 | |
| jobs[job_id]["message"] = f"Processing failed" | |
| jobs[job_id]["error"] = str(e) | |
| if __name__ == '__main__': | |
| app.run( | |
| debug=config.FLASK_DEBUG, | |
| host=config.FLASK_HOST, | |
| port=config.FLASK_PORT, | |
| use_reloader=False # Disable auto-reload to prevent socket errors | |
| ) | |