import logging from flask import Flask, render_template, request, jsonify, after_this_request from functools import wraps from io import BytesIO import base64 import subprocess import os import random import string import re import sys # Configuración del registro logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') app = Flask(__name__) # Define el directorio donde se guardan los archivos file_folder = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__))) #temp_audio_folder = os.path.join(file_folder, 'temp_audio') temp_audio_folder= "/home/app/temp_audio/" #model_folder = os.path.join(file_folder, 'models') model_folder = "/home/app/" #piper_binary_path = os.path.join(file_folder, 'piper', 'piper') piper_binary_path = "/home/app/piper" # Define los nombres asignados a modelos específicos model_names = { "Español México | Claude": "es_MX-claude-14947-epoch-high.onnx", "Español México | Cortana v1": "es_MX-cortana-19669-epoch-high.onnx", "Español México | TheGevy": "es_MX-gevy-10196-epoch-high.onnx", "English United States Example": "en_US-ljspeech-high.onnx" } def filter_text(text): filtered_text = text.replace('(', ',').replace(')', ',').replace('?', ',').replace('¿', ',').replace(':', ',') filtered_text = re.sub(r'[^\w\s,.\(\):\u00C0-\u00FF]', '', filtered_text) filtered_text = filtered_text.replace('\n', ' ') return filtered_text def convert_text_to_speech(text, model): filtered_text = filter_text(text) random_name = ''.join(random.choices(string.ascii_letters + string.digits, k=8)) + '.wav' output_file = os.path.join(temp_audio_folder, random_name) if os.path.isfile(piper_binary_path): if model in model_names: model_path = os.path.join(model_folder, model_names[model]) if os.path.isfile(model_path): # Construye el comando para ejecutar Piper command = f'echo "{filtered_text}" | "{piper_binary_path}" -m {model_path} -f {output_file}' try: subprocess.run(command, shell=True, check=True) return output_file except subprocess.CalledProcessError as e: logging.error(f"Error al ejecutar el comando: {e}") return None else: logging.error(f"Modelo '{model}' no encontrado en la ubicación especificada.") return None else: logging.error(f"No se ha asignado un modelo para el nombre '{model}'.") return None else: logging.error(f"No se encontró el binario de Piper en la ubicación especificada.") return None # Define una función decoradora para restringir el acceso a la ruta /convert def restrict_access(func): @wraps(func) def wrapper(*args, **kwargs): # Verifica si la solicitud se hizo desde la página index.html referer = request.headers.get("Referer") if referer and referer.endswith("/"): # Permite el acceso a la función si la solicitud proviene de la página index.html return func(*args, **kwargs) else: # Devuelve un mensaje de error o redirecciona a otra página return "Acceso no autorizado", 403 # Código de respuesta HTTP 403 - Forbidden return wrapper @app.route('/') def index(): model_options = list(model_names.keys()) # Registra el contenido de la carpeta actual logging.info("Contents of current folder: %s", os.listdir(file_folder)) return render_template('index.html', model_options=model_options) @app.route('/convert', methods=['POST']) @restrict_access def convert_text(): text = request.form['text'] model = request.form['model'] output_file = convert_text_to_speech(text, model) @after_this_request def remove_file(response): try: os.remove(output_file) logging.info("Audio file deleted: %s", output_file) except Exception as error: logging.error("Error deleting file: %s", error) return response if output_file is not None: with open(output_file, 'rb') as audio_file: audio_content = audio_file.read() audio_base64 = base64.b64encode(audio_content).decode('utf-8') response = jsonify({'audio_base64': audio_base64}) else: response = jsonify({'error': 'Error al convertir texto a voz'}) return response if __name__ == '__main__': logging.info("Se está iniciando la aplicación.") app.run(host='0.0.0.0', port=7860, debug=False)