from flask import Blueprint, request, jsonify, send_file, current_app import subprocess import os import logging import uuid import traceback from .webhook import send_error_webhook bp = Blueprint('main', __name__) def _build_command_args(args): return [ args['voicepeak_path'], "-s", args['script'], "-n", args['narrator'], "-o", args['output_path'], "-e", args['emotions'], "--pitch", str(args['pitch']), ] def _synthesize_with_voicepeak(script, narrator, emotions, pitch, output_path, retries=3): args = { 'voicepeak_path': current_app.config['VOICEPEAK_PATH'], 'script': script, 'narrator': narrator, 'output_path': output_path, 'emotions': emotions, 'pitch': pitch, } command_args = _build_command_args(args) for attempt in range(retries): try: subprocess.run(command_args, check=True) if os.path.exists(output_path): return output_path except subprocess.CalledProcessError as e: error_message = f"VoicePeak synthesis failed on attempt {attempt + 1}: {e}" logging.error(error_message) if attempt < retries - 1: logging.info(f"Retrying... ({attempt + 2}/{retries})") else: raise RuntimeError(error_message) from e error_message = f"Expected audio file not found after {retries} attempts: {output_path}" logging.error(error_message) raise FileNotFoundError(error_message) @bp.route('/generate_voice', methods=['POST']) def generate_voice(): try: data = request.json text = data.get('text') narrator = data.get('narrator', 'SEKAI') emotion_happy = data.get('emotion_happy', '0') emotion_sad = data.get('emotion_sad', '0') emotion_angry = data.get('emotion_angry', '0') emotion_fun = data.get('emotion_fun', '0') pitch = data.get('pitch', '1') if not text: return jsonify({'error': 'Text is required'}), 400 emotions = f"happy={emotion_happy},sad={emotion_sad},angry={emotion_angry},fun={emotion_fun}" unique_id = str(uuid.uuid4()) output_file = os.path.join(current_app.config['PROJECT_PATH'], f'{unique_id}.wav') _synthesize_with_voicepeak(text, narrator, emotions, pitch, output_file) response = send_file(output_file, mimetype='audio/wav') os.remove(output_file) return response except Exception as e: error_message = str(e) error_type = type(e).__name__ stack_trace = traceback.format_exc() current_app.logger.error(f'Error in generate_voice: {error_message}') current_app.logger.error(f'Traceback: {stack_trace}') # Send webhook send_error_webhook(error_message, error_type, stack_trace) return jsonify({'error': error_message, 'traceback': stack_trace}), 500 @bp.route('/test_error') def test_error(): current_app.logger.info("Testing error webhook") raise Exception("This is a test error from routes.py") @bp.route('/test_500_error') def test_500_error(): current_app.logger.info("Testing 500 error webhook") raise InternalServerError("This is a test 500 error from routes.py")