2024-08-26 15:21:25 +00:00
|
|
|
from flask import Blueprint, request, jsonify, send_file, current_app
|
2024-08-03 11:12:30 +00:00
|
|
|
import subprocess
|
|
|
|
import os
|
|
|
|
import logging
|
|
|
|
import uuid
|
2024-08-26 15:21:25 +00:00
|
|
|
import traceback
|
|
|
|
from .webhook import send_error_webhook
|
2024-08-03 11:12:30 +00:00
|
|
|
|
2024-08-26 13:37:58 +00:00
|
|
|
bp = Blueprint('main', __name__)
|
2024-08-03 11:12:30 +00:00
|
|
|
|
|
|
|
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 = {
|
2024-08-26 13:37:58 +00:00
|
|
|
'voicepeak_path': current_app.config['VOICEPEAK_PATH'],
|
2024-08-03 11:12:30 +00:00
|
|
|
'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)
|
|
|
|
|
2024-08-26 13:37:58 +00:00
|
|
|
@bp.route('/generate_voice', methods=['POST'])
|
2024-08-03 11:12:30 +00:00
|
|
|
def generate_voice():
|
|
|
|
try:
|
|
|
|
data = request.json
|
|
|
|
text = data.get('text')
|
2024-08-26 15:21:25 +00:00
|
|
|
narrator = data.get('narrator', 'SEKAI')
|
2024-08-03 11:12:30 +00:00
|
|
|
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())
|
2024-08-26 13:37:58 +00:00
|
|
|
output_file = os.path.join(current_app.config['PROJECT_PATH'], f'{unique_id}.wav')
|
2024-08-03 11:12:30 +00:00
|
|
|
|
|
|
|
_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:
|
2024-08-26 15:21:25 +00:00
|
|
|
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")
|