amauricunha commited on
Commit
96f9398
·
verified ·
1 Parent(s): 8b5f5d6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -37
app.py CHANGED
@@ -1,59 +1,139 @@
 
 
 
 
 
 
 
 
 
 
1
  import os
2
- import json
3
- import base64
4
  import io
5
- from gtts import gTTS
6
- from flask import Flask, request, jsonify, send_file
7
- import tempfile
8
 
9
- # Flask setup
10
- app = Flask(__name__, static_folder=".")
11
 
12
- # The language code 'en' for English
13
- TTS_LANG = 'en'
 
 
 
 
 
 
 
 
 
 
 
14
 
 
 
 
 
 
 
 
 
 
 
15
  @app.route('/tts-proxy', methods=['POST'])
16
  def tts_proxy():
17
- """
18
- Recebe o texto do frontend e usa gTTS para gerar áudio MP3.
19
- O áudio é retornado como um arquivo para o cliente.
20
- """
21
  try:
22
- data = request.json
23
- prompt = data.get('prompt')
24
- # gTTS does not support changing voice/accent like Gemini, so we ignore it.
25
- # It defaults to a neutral female voice.
26
-
27
- if not prompt:
28
- return jsonify({"error": "Parâmetro 'prompt' ausente. Cole o texto para gerar o áudio."}), 400
29
 
30
- # 1. Geração do áudio usando gTTS (requer internet)
31
- # Usamos tld='us' para um sotaque americano padrão (US English).
32
- tts = gTTS(text=prompt, lang=TTS_LANG, slow=False, tld='us')
 
 
 
33
 
34
- # 2. Salva o áudio em um buffer de memória
35
  mp3_fp = io.BytesIO()
36
  tts.write_to_fp(mp3_fp)
37
  mp3_fp.seek(0)
38
-
39
- # 3. Retorna o arquivo MP3 diretamente como uma resposta do Flask
40
- # O navegador tratará isso como um arquivo de áudio.
41
  return send_file(
42
  mp3_fp,
43
  mimetype='audio/mp3',
44
- as_attachment=False,
45
- download_name='generated_audio.mp3'
46
  )
47
 
48
  except Exception as e:
49
- print(f"Erro geral no proxy TTS: {e}")
50
- return jsonify({"error": f"Erro interno durante a geração de áudio: {e}"}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- @app.route('/')
53
- def index_route():
54
- return app.send_static_file('index.html')
55
 
56
  if __name__ == '__main__':
57
- # Quando implantado no HF Spaces, a porta é gerenciada pelo ambiente
58
- port = int(os.environ.get('PORT', 7860))
59
- app.run(host='0.0.0.0', port=port)
 
1
+ # Importações necessárias.
2
+ # flask: Para criar o servidor web e as rotas.
3
+ # gTTS: Para gerar o áudio TTS gratuitamente (requer internet, mas não chave API).
4
+ # os: Para gerenciar arquivos temporários.
5
+ # google.genai: Para a contextualização e tradução da IA (requer GEMINI_API_KEY).
6
+
7
+ from flask import Flask, request, jsonify, send_file
8
+ from gtts import gTTS
9
+ from google import genai
10
+ from google.genai import types
11
  import os
 
 
12
  import io
13
+ import json
 
 
14
 
15
+ # Inicializa o Flask
16
+ app = Flask(__name__, static_folder='.', static_url_path='')
17
 
18
+ # Configuração da API Key (Deve ser lida do secret GEMINI_API_KEY no Hugging Face)
19
+ # O código abaixo tenta ler a chave da variável de ambiente.
20
+ # Se a chave não estiver configurada no Secret, o endpoint /explain-proxy falhará,
21
+ # mas o /tts-proxy (gTTS) ainda funcionará.
22
+ try:
23
+ API_KEY = os.environ.get("GEMINI_API_KEY")
24
+ if API_KEY:
25
+ client = genai.Client(api_key=API_KEY)
26
+ else:
27
+ print("AVISO: GEMINI_API_KEY não configurada. A funcionalidade de Tradução/Explicação (IA) não funcionará.")
28
+ except Exception as e:
29
+ print(f"Erro ao inicializar o cliente Gemini: {e}")
30
+ client = None
31
 
32
+
33
+ # Rota para servir o arquivo HTML principal (Frontend)
34
+ @app.route('/')
35
+ def serve_index():
36
+ """Serve o arquivo index.html, que contém a interface do usuário."""
37
+ # Garante que o index.html seja servido como a página inicial
38
+ return app.send_static_file('index.html')
39
+
40
+
41
+ # --- ROTA 1: TEXT-TO-SPEECH (TTS) - Usando gTTS ---
42
  @app.route('/tts-proxy', methods=['POST'])
43
  def tts_proxy():
44
+ """Gera áudio MP3 a partir do texto usando a biblioteca gTTS."""
 
 
 
45
  try:
46
+ data = request.get_json()
47
+ text = data.get('text', '').strip()
 
 
 
 
 
48
 
49
+ if not text:
50
+ return jsonify({"error": "Texto não fornecido."}), 400
51
+
52
+ # O gTTS não suporta chunks longos, por isso ele é chamado diretamente.
53
+ # Ele faz a requisição à internet por conta própria.
54
+ tts = gTTS(text=text, lang='en', tld='us')
55
 
56
+ # Salva o áudio em um buffer de bytes na memória (mais rápido que salvar em disco)
57
  mp3_fp = io.BytesIO()
58
  tts.write_to_fp(mp3_fp)
59
  mp3_fp.seek(0)
60
+
61
+ # Retorna o arquivo MP3 como uma resposta de streaming
 
62
  return send_file(
63
  mp3_fp,
64
  mimetype='audio/mp3',
65
+ as_attachment=True,
66
+ download_name='audio.mp3'
67
  )
68
 
69
  except Exception as e:
70
+ print(f"Erro no TTS gTTS: {e}")
71
+ return jsonify({"error": f"Erro interno ao gerar áudio: {e}"}), 500
72
+
73
+
74
+ # --- ROTA 2: EXPLICAÇÃO E TRADUÇÃO DA IA - Usando google-genai ---
75
+ @app.route('/explain-proxy', methods=['POST'])
76
+ def explain_proxy():
77
+ """Usa a IA Gemini para obter explicação em EN e tradução em PT de uma palavra."""
78
+ if not client:
79
+ return jsonify({
80
+ "explanation": "Funcionalidade desativada. A chave GEMINI_API_KEY não foi configurada no Hugging Face Secrets.",
81
+ "translation": "Funcionalidade desativada. Por favor, configure a chave API para usar este recurso."
82
+ }), 503
83
+
84
+ try:
85
+ data = request.get_json()
86
+ word = data.get('word', '').strip()
87
+ context = data.get('context', '').strip()
88
+
89
+ if not word:
90
+ return jsonify({"error": "Palavra não fornecida para análise."}), 400
91
+
92
+ # Prompt de IA estruturado para garantir a resposta em formato JSON
93
+ system_prompt = (
94
+ "Você é um tutor de inglês especializado em vocabulário e contexto. Sua tarefa é analisar uma "
95
+ "palavra e fornecer uma explicação em inglês e uma tradução em português baseada no contexto. "
96
+ "Sua resposta DEVE ser um objeto JSON com as chaves 'explanation' e 'translation'."
97
+ )
98
+
99
+ user_prompt = (
100
+ f"Analise a palavra: '{word}'. "
101
+ f"O contexto em que ela foi encontrada é: \"{context}\". "
102
+ "Forneça uma explicação concisa em inglês de como a palavra é usada e a melhor tradução para o português DENTRO DESSE CONTEXTO. "
103
+ "Responda APENAS com o objeto JSON."
104
+ )
105
+
106
+ response = client.models.generate_content(
107
+ model='gemini-2.5-flash',
108
+ contents=user_prompt,
109
+ config=types.GenerateContentConfig(
110
+ system_instruction=system_prompt,
111
+ response_mime_type="application/json",
112
+ response_schema={
113
+ "type": "OBJECT",
114
+ "properties": {
115
+ "explanation": {"type": "STRING", "description": "Explicação concisa em inglês sobre o uso e significado da palavra no contexto fornecido."},
116
+ "translation": {"type": "STRING", "description": "Tradução para o português da palavra ou frase no contexto fornecido."}
117
+ },
118
+ "propertyOrdering": ["explanation", "translation"]
119
+ }
120
+ )
121
+ )
122
+
123
+ # O modelo retorna uma string JSON, precisamos fazer o parse
124
+ json_content = response.candidates[0].content.parts[0].text
125
+ ai_result = json.loads(json_content)
126
+
127
+ return jsonify(ai_result)
128
+
129
+ except genai.errors.APIError as e:
130
+ print(f"Erro na API Gemini: {e}")
131
+ return jsonify({"explanation": "Erro ao conectar à API Gemini. Chave inválida ou limite excedido.", "translation": "Erro na conexão da IA."}), 500
132
+ except Exception as e:
133
+ print(f"Erro no explain-proxy: {e}")
134
+ return jsonify({"explanation": "Erro interno do servidor ao processar a solicitação.", "translation": "Erro interno do servidor."}), 500
135
 
 
 
 
136
 
137
  if __name__ == '__main__':
138
+ # Define o host para 0.0.0.0 para ser acessível dentro do ambiente Hugging Face
139
+ app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 7860)))