vicmanmkt commited on
Commit
dc35a3a
·
verified ·
1 Parent(s): 3d8050d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -145
app.py CHANGED
@@ -6,14 +6,10 @@ import google.generativeai as genai
6
  import time
7
  import tempfile
8
  from PIL import Image
9
- import logging
10
 
11
  class PDFTranscriberGradio:
12
  def __init__(self):
13
  self.temp_files = []
14
- # Configurar logging para debug
15
- logging.basicConfig(level=logging.INFO)
16
- self.logger = logging.getLogger(__name__)
17
 
18
  def get_selected_model(self, model_choice):
19
  """Retorna o ID do modelo selecionado"""
@@ -22,13 +18,11 @@ class PDFTranscriberGradio:
22
  def validate_pdf_file(self, file_path):
23
  """Valida se o arquivo é um PDF usando múltiplos métodos"""
24
  try:
25
- self.logger.info(f"Validando arquivo: {file_path}")
26
-
27
  # Método 1: Verificar se o arquivo existe
28
  if not os.path.exists(file_path):
29
  return False, "Arquivo não encontrado"
30
 
31
- # Método 2: Verificar tamanho do arquivo
32
  file_size = os.path.getsize(file_path)
33
  if file_size == 0:
34
  return False, "Arquivo está vazio"
@@ -36,24 +30,25 @@ class PDFTranscriberGradio:
36
  # Método 3: Tentar abrir com PyMuPDF
37
  try:
38
  doc = fitz.open(file_path)
39
- if doc.page_count > 0:
40
- doc.close()
41
- return True, f"PDF válido com {doc.page_count} página(s)"
 
42
  else:
43
  return False, "PDF não contém páginas"
44
  except Exception as e:
45
- self.logger.warning(f"PyMuPDF falhou: {e}")
46
 
47
  # Método 4: Verificar header do arquivo
48
  try:
49
  with open(file_path, 'rb') as f:
50
  header = f.read(8)
51
  if header.startswith(b'%PDF-'):
52
- return True, "PDF detectado por header (pode ter problemas estruturais)"
53
  except Exception as e:
54
- self.logger.warning(f"Verificação de header falhou: {e}")
55
 
56
- return False, "Arquivo não é um PDF válido ou está corrompido"
57
 
58
  except Exception as e:
59
  return False, f"Erro na validação: {str(e)}"
@@ -62,80 +57,66 @@ class PDFTranscriberGradio:
62
  """Converte uma página do PDF em imagem e salva temporariamente"""
63
  try:
64
  page = pdf_document[page_number]
65
- # Usar matriz de alta resolução para melhor qualidade
66
  pix = page.get_pixmap(matrix=fitz.Matrix(2, 2))
67
  img_data = pix.tobytes("png")
68
 
69
- # Criar arquivo temporário com nome único
70
  temp_filename = f'page_{page_number}_{int(time.time()*1000)}_{os.getpid()}.png'
71
  temp_path = os.path.join(tempfile.gettempdir(), temp_filename)
72
 
73
  with open(temp_path, 'wb') as temp_file:
74
  temp_file.write(img_data)
75
 
76
- # Adicionar à lista de arquivos temporários para limpeza posterior
77
  self.temp_files.append(temp_path)
78
- self.logger.info(f"Página {page_number} convertida para: {temp_path}")
79
  return temp_path
80
-
81
  except Exception as e:
82
- self.logger.error(f"Erro ao converter página {page_number}: {e}")
83
  raise Exception(f"Erro ao converter página {page_number}: {str(e)}")
84
 
85
  def analyze_page_with_gemini(self, image_path, page_num, model_name, temperature, top_p, top_k):
86
  """Analisa uma página usando o modelo Gemini selecionado"""
87
  try:
88
- # Carregar e verificar a imagem
89
  img = Image.open(image_path)
90
- self.logger.info(f"Analisando página {page_num} com modelo {model_name}")
91
 
92
  # Configurar o modelo Gemini
93
  model = genai.GenerativeModel(model_name)
94
 
95
- # Prompt otimizado para transcrição detalhada
96
- prompt = """Analise esta página do documento PDF em detalhes. Forneça uma transcrição completa e organizada incluindo:
97
 
98
- 1. **TEXTO COMPLETO**: Transcreva todo o texto visível, mantendo a formatação e organização original
99
- 2. **ESTRUTURA DO DOCUMENTO**: Descreva títulos, subtítulos, parágrafos, listas, etc.
100
- 3. **ELEMENTOS VISUAIS**: Descreva tabelas, gráficos, imagens, logotipos ou elementos gráficos
101
- 4. **FORMATAÇÃO**: Mencione textos em negrito, itálico, cores especiais, tamanhos de fonte diferentes
102
- 5. **LAYOUT**: Descreva como o conteúdo está organizado na página (colunas, seções, etc.)
103
- 6. **INFORMAÇÕES IMPORTANTES**: Destaque dados relevantes, números, datas, nomes, certificações, etc.
104
 
105
- Seja preciso e detalhado na transcrição do texto."""
106
 
107
- # Configuração de geração otimizada
108
- generation_config = {
109
  'temperature': temperature,
110
- 'max_output_tokens': 8192, # Aumentado para respostas mais completas
111
  'top_p': top_p,
112
  'top_k': int(top_k),
113
- }
114
 
115
- # Gerar resposta
116
- response = model.generate_content(
117
- [prompt, img],
118
- generation_config=generation_config
119
- )
120
-
121
- # Fechar a imagem explicitamente para liberar memória
122
  img.close()
123
 
124
- self.logger.info(f"Análise da página {page_num} concluída")
125
  return response.text
126
 
127
  except Exception as e:
128
- error_msg = f"Erro ao analisar página {page_num}: {str(e)}"
129
- self.logger.error(error_msg)
130
- return error_msg
131
 
132
  def cleanup_temp_files(self):
133
- """Limpa todos os arquivos temporários criados durante o processamento"""
134
  cleaned_count = 0
135
  for temp_file in self.temp_files:
136
  try:
137
  if os.path.exists(temp_file):
138
- # Tentar remover o arquivo com retry em caso de uso
139
  for attempt in range(3):
140
  try:
141
  os.unlink(temp_file)
@@ -144,17 +125,17 @@ Seja preciso e detalhado na transcrição do texto."""
144
  except PermissionError:
145
  time.sleep(0.5)
146
  except Exception as e:
147
- self.logger.warning(f"Tentativa {attempt + 1} falhou para {temp_file}: {e}")
148
  if attempt == 2: # Última tentativa
 
149
  break
150
  except Exception as e:
151
- self.logger.warning(f"Erro ao remover arquivo temporário {temp_file}: {e}")
152
 
153
  self.temp_files = []
154
- self.logger.info(f"Limpeza concluída: {cleaned_count} arquivos removidos")
155
 
156
  def extract_text_from_pdf(self, pdf_path):
157
- """Extrai texto diretamente do PDF usando PyMuPDF"""
158
  try:
159
  doc = fitz.open(pdf_path)
160
  text_content = ""
@@ -162,7 +143,7 @@ Seja preciso e detalhado na transcrição do texto."""
162
  for page_num in range(len(doc)):
163
  page = doc[page_num]
164
  page_text = page.get_text()
165
- if page_text.strip(): # Só adiciona se a página tiver texto
166
  text_content += f"\n=== PÁGINA {page_num + 1} - TEXTO EXTRAÍDO ===\n"
167
  text_content += page_text
168
  text_content += "\n" + "="*50 + "\n"
@@ -175,21 +156,21 @@ Seja preciso e detalhado na transcrição do texto."""
175
 
176
  def process_pdf(self, pdf_file, api_key, model_choice, temperature, top_p, top_k):
177
  """Função principal para processar o PDF"""
178
- status_updates = ["🔄 Iniciando processamento do PDF..."]
179
  yield "\n".join(status_updates), None, None
180
 
181
  try:
182
  # Verificar se o arquivo foi fornecido
183
  if pdf_file is None:
184
- raise Exception("❌ Nenhum arquivo foi enviado")
185
 
186
- # Obter o caminho do arquivo
187
  file_path = pdf_file.name if hasattr(pdf_file, 'name') else pdf_file
188
 
189
- status_updates.append(f"📁 Arquivo recebido: {os.path.basename(file_path)}")
190
  yield "\n".join(status_updates), None, None
191
 
192
- # Validar o arquivo PDF
193
  is_valid, validation_msg = self.validate_pdf_file(file_path)
194
  if not is_valid:
195
  raise Exception(f"❌ {validation_msg}")
@@ -204,39 +185,36 @@ Seja preciso e detalhado na transcrição do texto."""
204
  # Configurar API Gemini
205
  genai.configure(api_key=api_key.strip())
206
 
207
- # Obter o modelo selecionado
208
  model_name = self.get_selected_model(model_choice)
209
- status_updates.append(f"🤖 Modelo selecionado: {model_name}")
210
  yield "\n".join(status_updates), None, None
211
 
212
- # Abrir o PDF
213
  doc = fitz.open(file_path)
214
  total_pages = len(doc)
215
 
216
- status_updates.append(f"📄 PDF aberto: {total_pages} página(s) encontrada(s)")
217
  yield "\n".join(status_updates), None, None
218
 
219
- # Extrair texto tradicional primeiro
220
- status_updates.append("📝 Extraindo texto diretamente do PDF...")
221
  yield "\n".join(status_updates), None, None
222
 
223
  extracted_text = self.extract_text_from_pdf(file_path)
224
 
225
- # Analisar cada página como imagem com IA
226
- status_updates.append("🔍 Iniciando análise visual com IA...")
227
  yield "\n".join(status_updates), None, None
228
 
229
  page_analyses = []
230
 
231
  for page_num in range(total_pages):
232
- status_updates.append(f"🖼️ Processando página {page_num + 1} de {total_pages}...")
233
  yield "\n".join(status_updates), None, None
234
 
235
  try:
236
- # Converter página para imagem
237
  image_path = self.convert_pdf_page_to_image(doc, page_num)
238
-
239
- # Analisar com Gemini
240
  analysis = self.analyze_page_with_gemini(
241
  image_path,
242
  page_num + 1,
@@ -245,18 +223,17 @@ Seja preciso e detalhado na transcrição do texto."""
245
  top_p,
246
  top_k
247
  )
248
-
249
- page_analyses.append(f"\n{'='*60}\n📄 ANÁLISE DETALHADA DA PÁGINA {page_num + 1}\n{'='*60}\n{analysis}")
250
 
251
  except Exception as e:
252
- error_analysis = f"❌ Erro ao processar página {page_num + 1}: {str(e)}"
253
- page_analyses.append(f"\n{'='*60}\n📄 PÁGINA {page_num + 1} - ERRO\n{'='*60}\n{error_analysis}")
254
 
255
- # Fechar o documento
256
  doc.close()
257
 
258
- # Combinar todos os resultados
259
- status_updates.append("📋 Compilando resultado final...")
260
  yield "\n".join(status_updates), None, None
261
 
262
  final_output = f"""{'='*80}
@@ -264,9 +241,9 @@ Seja preciso e detalhado na transcrição do texto."""
264
  {'='*80}
265
 
266
  📄 ARQUIVO: {os.path.basename(file_path)}
267
- 🤖 MODELO USADO: {model_name}
268
- 📊 TOTAL DE PÁGINAS: {total_pages}
269
- ⏰ PROCESSADO EM: {time.strftime('%Y-%m-%d %H:%M:%S')}
270
 
271
  {'='*80}
272
  📝 TEXTO EXTRAÍDO DIRETAMENTE
@@ -274,7 +251,7 @@ Seja preciso e detalhado na transcrição do texto."""
274
  {extracted_text}
275
 
276
  {'='*80}
277
- 🔍 ANÁLISE VISUAL DETALHADA POR IA
278
  {'='*80}
279
  {chr(10).join(page_analyses)}
280
 
@@ -283,56 +260,44 @@ Seja preciso e detalhado na transcrição do texto."""
283
  {'='*80}
284
  """
285
 
286
- # Salvar resultado em arquivo
287
  output_filename = f"transcricao_pdf_{int(time.time())}.txt"
288
  output_path = os.path.join(tempfile.gettempdir(), output_filename)
289
 
290
  with open(output_path, 'w', encoding='utf-8') as f:
291
  f.write(final_output)
292
 
293
- status_updates.append("✅ Processamento concluído com sucesso!")
294
- status_updates.append(f"💾 Arquivo salvo: {output_filename}")
295
 
296
- # Retornar resultado final
297
  yield "\n".join(status_updates), final_output, output_path
298
 
299
  except Exception as e:
300
  error_msg = f"❌ ERRO: {str(e)}"
301
  status_updates.append(error_msg)
302
- self.logger.error(error_msg)
303
  yield "\n".join(status_updates), None, None
304
 
305
  finally:
306
- # Sempre limpar arquivos temporários
307
  self.cleanup_temp_files()
308
 
309
  def create_interface(self):
310
- """Cria a interface Gradio"""
311
  with gr.Blocks(
312
  title="Transcritor de PDF com Gemini AI",
313
- theme=gr.themes.Soft(),
314
- css="""
315
- .gradio-container {
316
- max-width: 1200px !important;
317
- margin: auto;
318
- }
319
- """
320
  ) as interface:
321
 
322
  gr.Markdown("""
323
  # 🔍 Transcritor de PDF com Gemini AI
324
 
325
- **Ferramenta avançada para transcrição e análise de documentos PDF**
326
 
327
  ✨ **Recursos:**
328
- - 📝 Extração de texto nativo do PDF
329
- - 🖼️ Análise visual inteligente com IA
330
- - 📊 Descrição de tabelas, gráficos e elementos visuais
331
- - 💾 Download do resultado completo
332
-
333
- ⚠️ **Requisitos:**
334
- - API Key válida do Google Gemini AI
335
- - Arquivo PDF (máximo recomendado: 20 páginas)
336
  """)
337
 
338
  with gr.Row():
@@ -342,38 +307,34 @@ Seja preciso e detalhado na transcrição do texto."""
342
  api_key = gr.Textbox(
343
  label="🔑 API Key Gemini",
344
  type="password",
345
- placeholder="Insira sua API key do Google Gemini AI",
346
- info="Obtenha em: https://ai.google.dev/"
347
  )
348
 
 
349
  pdf_file = gr.File(
350
- label="📄 Arquivo PDF",
351
- file_types=[".pdf"],
352
- info="Selecione o arquivo PDF para transcrever"
353
  )
354
 
355
  model_choice = gr.Dropdown(
356
- label="🤖 Modelo Gemini",
357
  choices=[
358
  "gemini-2.0-flash-exp - Última geração (Experimental)",
359
  "gemini-1.5-flash - Rápido e versátil",
360
  "gemini-1.5-flash-8b - Alto volume",
361
  "gemini-1.5-pro - Raciocínio complexo"
362
  ],
363
- value="gemini-1.5-pro - Raciocínio complexo",
364
- info="Escolha o modelo mais adequado para sua necessidade"
365
  )
366
 
367
- gr.Markdown("#### 🎛️ Parâmetros Avançados")
368
-
369
  with gr.Row():
370
  temperature = gr.Slider(
371
  label="🌡️ Temperatura",
372
  minimum=0.0,
373
  maximum=1.0,
374
  step=0.1,
375
- value=0.1,
376
- info="Controla a criatividade (0.0 = mais preciso)"
377
  )
378
 
379
  with gr.Row():
@@ -382,8 +343,7 @@ Seja preciso e detalhado na transcrição do texto."""
382
  minimum=0.0,
383
  maximum=1.0,
384
  step=0.1,
385
- value=0.9,
386
- info="Controla a diversidade das respostas"
387
  )
388
 
389
  top_k = gr.Slider(
@@ -391,40 +351,34 @@ Seja preciso e detalhado na transcrição do texto."""
391
  minimum=1,
392
  maximum=40,
393
  step=1,
394
- value=40,
395
- info="Número de tokens considerados"
396
  )
397
 
398
  process_button = gr.Button(
399
- "🚀 Transcrever e Analisar",
400
- variant="primary",
401
- size="lg"
402
  )
403
 
404
  with gr.Column(scale=2):
405
  gr.Markdown("### 📊 Resultados")
406
 
407
  status_output = gr.Textbox(
408
- label="📋 Status do Processamento",
409
  lines=8,
410
- max_lines=15,
411
- info="Acompanhe o progresso em tempo real"
412
  )
413
 
414
  result_output = gr.Textbox(
415
- label="📄 Transcrição Completa",
416
  lines=20,
417
- max_lines=30,
418
- show_copy_button=True,
419
- info="Resultado completo da transcrição"
420
  )
421
 
422
  file_output = gr.File(
423
- label="💾 Download do Resultado",
424
- info="Baixe o arquivo completo da transcrição"
425
  )
426
 
427
- # Conectar o botão à função
428
  process_button.click(
429
  fn=self.process_pdf,
430
  inputs=[pdf_file, api_key, model_choice, temperature, top_p, top_k],
@@ -433,15 +387,10 @@ Seja preciso e detalhado na transcrição do texto."""
433
  )
434
 
435
  gr.Markdown("""
436
- ### 💡 Dicas de Uso
437
-
438
- 1. **API Key**: Obtenha gratuitamente em [Google AI Studio](https://ai.google.dev/)
439
- 2. **Qualidade**: PDFs com texto nativo produzem melhores resultados
440
- 3. **Tamanho**: Para PDFs grandes (>20 páginas), considere dividir em partes
441
- 4. **Modelos**:
442
- - **Gemini 1.5 Pro**: Melhor para análise complexa
443
- - **Gemini 1.5 Flash**: Mais rápido para documentos simples
444
- 5. **Parâmetros**: Use temperatura baixa (0.1-0.3) para maior precisão
445
  """)
446
 
447
  return interface
@@ -452,11 +401,9 @@ Seja preciso e detalhado na transcrição do texto."""
452
  interface.launch(
453
  server_name="0.0.0.0",
454
  server_port=7860,
455
- share=False,
456
- show_error=True
457
  )
458
 
459
- # Ponto de entrada da aplicação
460
  if __name__ == "__main__":
461
  app = PDFTranscriberGradio()
462
  app.run()
 
6
  import time
7
  import tempfile
8
  from PIL import Image
 
9
 
10
  class PDFTranscriberGradio:
11
  def __init__(self):
12
  self.temp_files = []
 
 
 
13
 
14
  def get_selected_model(self, model_choice):
15
  """Retorna o ID do modelo selecionado"""
 
18
  def validate_pdf_file(self, file_path):
19
  """Valida se o arquivo é um PDF usando múltiplos métodos"""
20
  try:
 
 
21
  # Método 1: Verificar se o arquivo existe
22
  if not os.path.exists(file_path):
23
  return False, "Arquivo não encontrado"
24
 
25
+ # Método 2: Verificar tamanho
26
  file_size = os.path.getsize(file_path)
27
  if file_size == 0:
28
  return False, "Arquivo está vazio"
 
30
  # Método 3: Tentar abrir com PyMuPDF
31
  try:
32
  doc = fitz.open(file_path)
33
+ page_count = len(doc)
34
+ doc.close()
35
+ if page_count > 0:
36
+ return True, f"PDF válido com {page_count} página(s)"
37
  else:
38
  return False, "PDF não contém páginas"
39
  except Exception as e:
40
+ pass
41
 
42
  # Método 4: Verificar header do arquivo
43
  try:
44
  with open(file_path, 'rb') as f:
45
  header = f.read(8)
46
  if header.startswith(b'%PDF-'):
47
+ return True, "PDF detectado por header"
48
  except Exception as e:
49
+ pass
50
 
51
+ return False, "Arquivo não é um PDF válido"
52
 
53
  except Exception as e:
54
  return False, f"Erro na validação: {str(e)}"
 
57
  """Converte uma página do PDF em imagem e salva temporariamente"""
58
  try:
59
  page = pdf_document[page_number]
 
60
  pix = page.get_pixmap(matrix=fitz.Matrix(2, 2))
61
  img_data = pix.tobytes("png")
62
 
63
+ # Criar arquivo temporário com nome único baseado no timestamp
64
  temp_filename = f'page_{page_number}_{int(time.time()*1000)}_{os.getpid()}.png'
65
  temp_path = os.path.join(tempfile.gettempdir(), temp_filename)
66
 
67
  with open(temp_path, 'wb') as temp_file:
68
  temp_file.write(img_data)
69
 
70
+ # Adicionar à lista de arquivos temporários
71
  self.temp_files.append(temp_path)
 
72
  return temp_path
 
73
  except Exception as e:
 
74
  raise Exception(f"Erro ao converter página {page_number}: {str(e)}")
75
 
76
  def analyze_page_with_gemini(self, image_path, page_num, model_name, temperature, top_p, top_k):
77
  """Analisa uma página usando o modelo Gemini selecionado"""
78
  try:
79
+ # Carregar a imagem
80
  img = Image.open(image_path)
 
81
 
82
  # Configurar o modelo Gemini
83
  model = genai.GenerativeModel(model_name)
84
 
85
+ # Prompt otimizado para transcrição
86
+ prompt = """Analise esta página do documento PDF em detalhes e forneça uma transcrição completa. Inclua:
87
 
88
+ 1. **TEXTO COMPLETO**: Transcreva todo o texto visível, mantendo a formatação original
89
+ 2. **ESTRUTURA**: Descreva títulos, subtítulos, parágrafos, listas
90
+ 3. **ELEMENTOS VISUAIS**: Descreva tabelas, gráficos, imagens, logotipos
91
+ 4. **FORMATAÇÃO**: Mencione textos em negrito, itálico, cores especiais
92
+ 5. **LAYOUT**: Como o conteúdo está organizado na página
93
+ 6. **INFORMAÇÕES IMPORTANTES**: Destaque dados relevantes, números, datas, certificações
94
 
95
+ Seja preciso e detalhado na transcrição."""
96
 
97
+ # Gerar resposta
98
+ response = model.generate_content([prompt, img], generation_config={
99
  'temperature': temperature,
100
+ 'max_output_tokens': 8192,
101
  'top_p': top_p,
102
  'top_k': int(top_k),
103
+ })
104
 
105
+ # Fechar a imagem explicitamente
 
 
 
 
 
 
106
  img.close()
107
 
 
108
  return response.text
109
 
110
  except Exception as e:
111
+ return f"Erro ao analisar página {page_num}: {str(e)}"
 
 
112
 
113
  def cleanup_temp_files(self):
114
+ """Limpa todos os arquivos temporários"""
115
  cleaned_count = 0
116
  for temp_file in self.temp_files:
117
  try:
118
  if os.path.exists(temp_file):
119
+ # Tentar várias vezes em caso de arquivo em uso
120
  for attempt in range(3):
121
  try:
122
  os.unlink(temp_file)
 
125
  except PermissionError:
126
  time.sleep(0.5)
127
  except Exception as e:
 
128
  if attempt == 2: # Última tentativa
129
+ print(f"Aviso: Não foi possível remover {temp_file}: {e}")
130
  break
131
  except Exception as e:
132
+ print(f"Erro ao limpar arquivo temporário {temp_file}: {e}")
133
 
134
  self.temp_files = []
135
+ print(f"Limpeza concluída: {cleaned_count} arquivos removidos")
136
 
137
  def extract_text_from_pdf(self, pdf_path):
138
+ """Extrai texto diretamente do PDF"""
139
  try:
140
  doc = fitz.open(pdf_path)
141
  text_content = ""
 
143
  for page_num in range(len(doc)):
144
  page = doc[page_num]
145
  page_text = page.get_text()
146
+ if page_text.strip():
147
  text_content += f"\n=== PÁGINA {page_num + 1} - TEXTO EXTRAÍDO ===\n"
148
  text_content += page_text
149
  text_content += "\n" + "="*50 + "\n"
 
156
 
157
  def process_pdf(self, pdf_file, api_key, model_choice, temperature, top_p, top_k):
158
  """Função principal para processar o PDF"""
159
+ status_updates = ["🔄 Iniciando processamento..."]
160
  yield "\n".join(status_updates), None, None
161
 
162
  try:
163
  # Verificar se o arquivo foi fornecido
164
  if pdf_file is None:
165
+ raise Exception("❌ Nenhum arquivo enviado")
166
 
167
+ # Obter caminho do arquivo
168
  file_path = pdf_file.name if hasattr(pdf_file, 'name') else pdf_file
169
 
170
+ status_updates.append(f"📁 Arquivo: {os.path.basename(file_path)}")
171
  yield "\n".join(status_updates), None, None
172
 
173
+ # Validar arquivo PDF
174
  is_valid, validation_msg = self.validate_pdf_file(file_path)
175
  if not is_valid:
176
  raise Exception(f"❌ {validation_msg}")
 
185
  # Configurar API Gemini
186
  genai.configure(api_key=api_key.strip())
187
 
188
+ # Obter modelo selecionado
189
  model_name = self.get_selected_model(model_choice)
190
+ status_updates.append(f"🤖 Modelo: {model_name}")
191
  yield "\n".join(status_updates), None, None
192
 
193
+ # Abrir PDF
194
  doc = fitz.open(file_path)
195
  total_pages = len(doc)
196
 
197
+ status_updates.append(f"📄 PDF: {total_pages} página(s)")
198
  yield "\n".join(status_updates), None, None
199
 
200
+ # Extrair texto tradicional
201
+ status_updates.append("📝 Extraindo texto...")
202
  yield "\n".join(status_updates), None, None
203
 
204
  extracted_text = self.extract_text_from_pdf(file_path)
205
 
206
+ # Analisar páginas com IA
207
+ status_updates.append("🔍 Analisando com IA...")
208
  yield "\n".join(status_updates), None, None
209
 
210
  page_analyses = []
211
 
212
  for page_num in range(total_pages):
213
+ status_updates.append(f"🖼️ Página {page_num + 1}/{total_pages}...")
214
  yield "\n".join(status_updates), None, None
215
 
216
  try:
 
217
  image_path = self.convert_pdf_page_to_image(doc, page_num)
 
 
218
  analysis = self.analyze_page_with_gemini(
219
  image_path,
220
  page_num + 1,
 
223
  top_p,
224
  top_k
225
  )
226
+ page_analyses.append(f"\n{'='*60}\n📄 ANÁLISE DA PÁGINA {page_num + 1}\n{'='*60}\n{analysis}")
 
227
 
228
  except Exception as e:
229
+ error_msg = f"❌ Erro na página {page_num + 1}: {str(e)}"
230
+ page_analyses.append(f"\n{'='*60}\n📄 PÁGINA {page_num + 1} - ERRO\n{'='*60}\n{error_msg}")
231
 
232
+ # Fechar documento
233
  doc.close()
234
 
235
+ # Compilar resultado final
236
+ status_updates.append("📋 Compilando resultado...")
237
  yield "\n".join(status_updates), None, None
238
 
239
  final_output = f"""{'='*80}
 
241
  {'='*80}
242
 
243
  📄 ARQUIVO: {os.path.basename(file_path)}
244
+ 🤖 MODELO: {model_name}
245
+ 📊 PÁGINAS: {total_pages}
246
+ ⏰ PROCESSADO: {time.strftime('%Y-%m-%d %H:%M:%S')}
247
 
248
  {'='*80}
249
  📝 TEXTO EXTRAÍDO DIRETAMENTE
 
251
  {extracted_text}
252
 
253
  {'='*80}
254
+ 🔍 ANÁLISE VISUAL DETALHADA
255
  {'='*80}
256
  {chr(10).join(page_analyses)}
257
 
 
260
  {'='*80}
261
  """
262
 
263
+ # Salvar resultado
264
  output_filename = f"transcricao_pdf_{int(time.time())}.txt"
265
  output_path = os.path.join(tempfile.gettempdir(), output_filename)
266
 
267
  with open(output_path, 'w', encoding='utf-8') as f:
268
  f.write(final_output)
269
 
270
+ status_updates.append("✅ Processamento concluído!")
271
+ status_updates.append(f"💾 Arquivo: {output_filename}")
272
 
 
273
  yield "\n".join(status_updates), final_output, output_path
274
 
275
  except Exception as e:
276
  error_msg = f"❌ ERRO: {str(e)}"
277
  status_updates.append(error_msg)
 
278
  yield "\n".join(status_updates), None, None
279
 
280
  finally:
281
+ # Limpar arquivos temporários
282
  self.cleanup_temp_files()
283
 
284
  def create_interface(self):
285
+ """Cria interface Gradio"""
286
  with gr.Blocks(
287
  title="Transcritor de PDF com Gemini AI",
288
+ theme=gr.themes.Soft()
 
 
 
 
 
 
289
  ) as interface:
290
 
291
  gr.Markdown("""
292
  # 🔍 Transcritor de PDF com Gemini AI
293
 
294
+ **Ferramenta para transcrição e análise de documentos PDF**
295
 
296
  ✨ **Recursos:**
297
+ - 📝 Extração de texto nativo
298
+ - 🖼️ Análise visual com IA
299
+ - 📊 Descrição de elementos visuais
300
+ - 💾 Download do resultado
 
 
 
 
301
  """)
302
 
303
  with gr.Row():
 
307
  api_key = gr.Textbox(
308
  label="🔑 API Key Gemini",
309
  type="password",
310
+ placeholder="Insira sua API key do Google Gemini"
 
311
  )
312
 
313
+ # CORREÇÃO PRINCIPAL: Remover validação restritiva
314
  pdf_file = gr.File(
315
+ label="📄 Arquivo PDF",
316
+ file_types=None, # Aceita qualquer tipo
317
+ file_count="single"
318
  )
319
 
320
  model_choice = gr.Dropdown(
321
+ label="🤖 Modelo",
322
  choices=[
323
  "gemini-2.0-flash-exp - Última geração (Experimental)",
324
  "gemini-1.5-flash - Rápido e versátil",
325
  "gemini-1.5-flash-8b - Alto volume",
326
  "gemini-1.5-pro - Raciocínio complexo"
327
  ],
328
+ value="gemini-1.5-pro - Raciocínio complexo"
 
329
  )
330
 
 
 
331
  with gr.Row():
332
  temperature = gr.Slider(
333
  label="🌡️ Temperatura",
334
  minimum=0.0,
335
  maximum=1.0,
336
  step=0.1,
337
+ value=0.1
 
338
  )
339
 
340
  with gr.Row():
 
343
  minimum=0.0,
344
  maximum=1.0,
345
  step=0.1,
346
+ value=0.9
 
347
  )
348
 
349
  top_k = gr.Slider(
 
351
  minimum=1,
352
  maximum=40,
353
  step=1,
354
+ value=40
 
355
  )
356
 
357
  process_button = gr.Button(
358
+ "🚀 Transcrever PDF",
359
+ variant="primary"
 
360
  )
361
 
362
  with gr.Column(scale=2):
363
  gr.Markdown("### 📊 Resultados")
364
 
365
  status_output = gr.Textbox(
366
+ label="📋 Status",
367
  lines=8,
368
+ interactive=False
 
369
  )
370
 
371
  result_output = gr.Textbox(
372
+ label="📄 Transcrição",
373
  lines=20,
374
+ show_copy_button=True
 
 
375
  )
376
 
377
  file_output = gr.File(
378
+ label="💾 Download"
 
379
  )
380
 
381
+ # Conectar função
382
  process_button.click(
383
  fn=self.process_pdf,
384
  inputs=[pdf_file, api_key, model_choice, temperature, top_p, top_k],
 
387
  )
388
 
389
  gr.Markdown("""
390
+ ### 💡 Dicas
391
+ - Obtenha API Key gratuita em: https://ai.google.dev/
392
+ - Use temperatura baixa (0.1-0.3) para maior precisão
393
+ - Gemini 1.5 Pro é melhor para análise complexa
 
 
 
 
 
394
  """)
395
 
396
  return interface
 
401
  interface.launch(
402
  server_name="0.0.0.0",
403
  server_port=7860,
404
+ share=False
 
405
  )
406
 
 
407
  if __name__ == "__main__":
408
  app = PDFTranscriberGradio()
409
  app.run()