Spaces:
Running
Running
| """ | |
| ENDPOINT /api/predict para recibir trabajos desde tu servidor PHP | |
| """ | |
| from flask import Flask, request, jsonify | |
| import json | |
| import time | |
| import threading | |
| import requests | |
| import hashlib | |
| import hmac | |
| app = Flask(__name__) | |
| # Configuraci贸n - CAMBIAR ESTE VALOR | |
| WEBHOOK_SECRET = "AB7k9mJlVQ8xR5vL3wE1sT26Y4oI0pZ9c2V26Bn5M8k7" # EL MISMO que pusiste en webhook.php | |
| JOBS = {} # Almacenar estado de trabajos en memoria | |
| def create_webhook_signature(payload, secret): | |
| """Crear firma HMAC para webhook""" | |
| return 'sha256=' + hmac.new( | |
| secret.encode(), | |
| payload.encode(), | |
| hashlib.sha256 | |
| ).hexdigest() | |
| def send_webhook(webhook_url, data): | |
| """Enviar webhook a tu servidor""" | |
| try: | |
| payload = json.dumps(data) | |
| signature = create_webhook_signature(payload, WEBHOOK_SECRET) | |
| headers = { | |
| 'Content-Type': 'application/json', | |
| 'X-Signature': signature | |
| } | |
| response = requests.post(webhook_url, data=payload, headers=headers) | |
| print(f"Webhook enviado: {response.status_code}") | |
| return response.status_code == 200 | |
| except Exception as e: | |
| print(f"Error enviando webhook: {e}") | |
| return False | |
| def process_audiobook_background(job_id, text, voice, language, webhook_url): | |
| """Procesar audiolibro en background""" | |
| try: | |
| # Actualizar estado: iniciado | |
| JOBS[job_id]['status'] = 'processing' | |
| JOBS[job_id]['stage'] = 'preprocessing' | |
| JOBS[job_id]['progress'] = 0 | |
| # Notificar inicio | |
| send_webhook(webhook_url, { | |
| 'event_type': 'job_started', | |
| 'job_id': job_id, | |
| 'estimated_time': len(text) // 10 # Estimaci贸n simple | |
| }) | |
| # Simular progreso (reemplazar con tu l贸gica real) | |
| stages = [ | |
| ('preprocessing', 10), | |
| ('text_analysis', 25), | |
| ('voice_synthesis', 60), | |
| ('audio_processing', 85), | |
| ('finalizing', 95) | |
| ] | |
| for stage, progress in stages: | |
| JOBS[job_id]['stage'] = stage | |
| JOBS[job_id]['progress'] = progress | |
| # Enviar progreso | |
| send_webhook(webhook_url, { | |
| 'event_type': 'job_progress', | |
| 'job_id': job_id, | |
| 'progress': progress, | |
| 'stage': stage | |
| }) | |
| # Simular tiempo de procesamiento | |
| time.sleep(10) # En producci贸n, aqu铆 va tu l贸gica real | |
| # AQU脥 VA TU L脫GICA REAL DE PROCESAMIENTO | |
| # audio_file = tu_funcion_de_tts(text, voice, language) | |
| # Por ahora, simular archivo de audio | |
| audio_url = f"https://tu-space.hf.space/audio/{job_id}.mp3" | |
| # Completar trabajo | |
| JOBS[job_id]['status'] = 'completed' | |
| JOBS[job_id]['progress'] = 100 | |
| JOBS[job_id]['audio_url'] = audio_url | |
| JOBS[job_id]['duration'] = len(text) // 5 # Estimaci贸n de duraci贸n | |
| # Notificar completaci贸n | |
| send_webhook(webhook_url, { | |
| 'event_type': 'job_completed', | |
| 'job_id': job_id, | |
| 'audio_url': audio_url, | |
| 'duration': JOBS[job_id]['duration'] | |
| }) | |
| except Exception as e: | |
| # Error en procesamiento | |
| JOBS[job_id]['status'] = 'failed' | |
| JOBS[job_id]['error'] = str(e) | |
| # Notificar error | |
| send_webhook(webhook_url, { | |
| 'event_type': 'job_failed', | |
| 'job_id': job_id, | |
| 'error': str(e), | |
| 'error_code': 'PROCESSING_ERROR' | |
| }) | |
| def predict(): | |
| """Endpoint principal para recibir trabajos""" | |
| try: | |
| data = request.get_json() | |
| # Validar datos requeridos | |
| required_fields = ['job_id', 'text', 'voice', 'language', 'webhook_url'] | |
| for field in required_fields: | |
| if field not in data: | |
| return jsonify({'error': f'Campo requerido: {field}'}), 400 | |
| job_id = data['job_id'] | |
| text = data['text'] | |
| voice = data['voice'] | |
| language = data['language'] | |
| webhook_url = data['webhook_url'] | |
| # Validaciones | |
| if len(text) < 10: | |
| return jsonify({'error': 'Texto demasiado corto'}), 400 | |
| if len(text) > 500000: # 500KB | |
| return jsonify({'error': 'Texto demasiado largo'}), 400 | |
| # Crear registro del trabajo | |
| JOBS[job_id] = { | |
| 'job_id': job_id, | |
| 'status': 'pending', | |
| 'text_length': len(text), | |
| 'voice': voice, | |
| 'language': language, | |
| 'created_at': time.time(), | |
| 'progress': 0, | |
| 'stage': 'pending' | |
| } | |
| # Iniciar procesamiento en background | |
| thread = threading.Thread( | |
| target=process_audiobook_background, | |
| args=(job_id, text, voice, language, webhook_url) | |
| ) | |
| thread.daemon = True | |
| thread.start() | |
| return jsonify({ | |
| 'success': True, | |
| 'job_id': job_id, | |
| 'status': 'started', | |
| 'estimated_time': len(text) // 10, | |
| 'message': 'Procesamiento iniciado' | |
| }) | |
| except Exception as e: | |
| return jsonify({'error': str(e)}), 500 | |
| def get_status(job_id): | |
| """Obtener estado de un trabajo""" | |
| if job_id not in JOBS: | |
| return jsonify({'error': 'Trabajo no encontrado'}), 404 | |
| job = JOBS[job_id] | |
| return jsonify({ | |
| 'job_id': job_id, | |
| 'status': job['status'], | |
| 'progress': job.get('progress', 0), | |
| 'stage': job.get('stage', 'pending'), | |
| 'created_at': job['created_at'], | |
| 'audio_url': job.get('audio_url'), | |
| 'duration': job.get('duration'), | |
| 'error': job.get('error') | |
| }) | |
| def get_voices(): | |
| """Obtener lista de voces disponibles""" | |
| voices = [ | |
| {'id': 'maria', 'name': 'Mar铆a', 'language': 'es', 'gender': 'female'}, | |
| {'id': 'carlos', 'name': 'Carlos', 'language': 'es', 'gender': 'male'}, | |
| {'id': 'laura', 'name': 'Laura', 'language': 'es', 'gender': 'female'}, | |
| {'id': 'john', 'name': 'John', 'language': 'en', 'gender': 'male'}, | |
| {'id': 'sarah', 'name': 'Sarah', 'language': 'en', 'gender': 'female'}, | |
| {'id': 'james', 'name': 'James', 'language': 'en', 'gender': 'male'}, | |
| {'id': 'marie', 'name': 'Marie', 'language': 'fr', 'gender': 'female'}, | |
| {'id': 'pierre', 'name': 'Pierre', 'language': 'fr', 'gender': 'male'}, | |
| {'id': 'hans', 'name': 'Hans', 'language': 'de', 'gender': 'male'}, | |
| {'id': 'greta', 'name': 'Greta', 'language': 'de', 'gender': 'female'}, | |
| {'id': 'marco', 'name': 'Marco', 'language': 'it', 'gender': 'male'}, | |
| {'id': 'sofia', 'name': 'Sofia', 'language': 'it', 'gender': 'female'}, | |
| ] | |
| return jsonify({ | |
| 'success': True, | |
| 'voices': voices | |
| }) | |
| def health_check(): | |
| """Health check endpoint""" | |
| return jsonify({ | |
| 'status': 'healthy', | |
| 'active_jobs': len([j for j in JOBS.values() if j['status'] in ['pending', 'processing']]), | |
| 'total_jobs': len(JOBS) | |
| }) | |
| if __name__ == '__main__': | |
| app.run(host='0.0.0.0', port=7860) |