Raí Santos commited on
Commit
2e52adb
·
1 Parent(s): 3af6bcf

feat: Complete optimization with 3 bugs fixed + backend-only

Browse files
Dockerfile CHANGED
@@ -25,12 +25,12 @@ RUN mkdir -p /app/uploads /app/models /app/frontend/dist
25
  RUN chmod -R 777 /app
26
 
27
  # Upgrade pip and install build tools
28
- RUN pip install --no-cache-dir --upgrade pip setuptools wheel
29
 
30
- # Install Torch CPU specifically (Prevents Erro 500 and OOM)
31
  RUN pip install --no-cache-dir torch==2.5.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cpu
32
 
33
- # Install backend dependencies
34
  COPY backend/requirements.txt .
35
  RUN pip install --no-cache-dir -r requirements.txt
36
 
 
25
  RUN chmod -R 777 /app
26
 
27
  # Upgrade pip and install build tools
28
+ RUN pip install --no-cache-dir --upgrade pip setuptools wheel Cython
29
 
30
+ # Install Torch CPU specifically (Essential for HF Spaces CPU tier)
31
  RUN pip install --no-cache-dir torch==2.5.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cpu
32
 
33
+ # Install remaining dependencies (WhisperX will pull its required pyannote version here)
34
  COPY backend/requirements.txt .
35
  RUN pip install --no-cache-dir -r requirements.txt
36
 
backend/main.py CHANGED
@@ -40,7 +40,11 @@ from fastapi.staticfiles import StaticFiles
40
  from fastapi.responses import JSONResponse, FileResponse
41
  from processor import TranscriptionProcessor
42
 
 
 
43
  app = FastAPI()
 
 
44
 
45
  # Configuração de CORS Universal
46
  app.add_middleware(
@@ -61,69 +65,59 @@ async def process_media(
61
  script: UploadFile = File(None)
62
  ):
63
  session_id = uuid.uuid4().hex[:8]
64
- print(f"\n🚀 [SESSÃO {session_id}] - INICIANDO PROCESSO")
65
-
66
  audio_path = os.path.join(UPLOAD_DIR, f"{session_id}_{audio.filename}")
67
  script_path = None
68
 
 
69
  try:
70
- # Salvamento de Áudio Robusto
71
  with open(audio_path, "wb") as buffer:
72
  shutil.copyfileobj(audio.file, buffer)
73
 
74
- script_text = ""
75
- if script:
76
- script_path = os.path.join(UPLOAD_DIR, f"{session_id}_{script.filename}")
77
- with open(script_path, "wb") as buffer:
78
- shutil.copyfileobj(script.file, buffer)
79
- script_text = processor.process_docx(script_path)
80
-
81
- # TRANSCRICÃO COM SEGURANÇA TOTAL
82
- words = processor.transcribe(audio_path, language="pt")
83
-
84
- # CORREÇÃO INTELIGENTE (Script Based)
85
- if script_text:
86
- words = processor.align_with_script(words, script_text)
87
 
88
- words = processor.correct_orthography(words)
89
-
90
- # GERAÇÃO DO JSON
91
- result_json = processor.generate_json(words)
92
- json_filename = f"{session_id}_transcription.json"
93
- json_path = os.path.join(UPLOAD_DIR, json_filename)
94
-
95
- # Escrita com persistência
96
- with open(json_path, "w", encoding="utf-8") as f:
97
- json.dump(result_json, f, ensure_ascii=False, indent=2)
98
-
99
- # Cleanup de arquivos pesados (MANTÉM APENAS O JSON)
100
- background_tasks.add_task(os.remove, audio_path)
101
- if script_path and os.path.exists(script_path):
102
- background_tasks.add_task(os.remove, script_path)
103
-
104
- # Limpeza de Memória RAM agendada
105
- gc.collect()
 
 
 
 
106
 
107
- print(f"✅ [SESSÃO {session_id}] - FINALIZADA COM SUCESSO\n")
108
- return {
109
- "success": True,
110
- "session_id": session_id,
111
- "words": words,
112
- "script_text": script_text,
113
- "json_url": f"/api/download/{json_filename}"
114
- }
115
 
116
  except Exception as e:
117
- err_msg = traceback.format_exc()
118
- print(f" [ERRO NA SESSÃO {session_id}]:\n{err_msg}")
119
- return JSONResponse(
120
- status_code=500,
121
- content={
122
- "success": False,
123
- "error": str(e),
124
- "traceback": err_msg
125
- }
126
- )
127
 
128
  @app.get("/api/download/{filename}")
129
  async def download_json(filename: str):
 
40
  from fastapi.responses import JSONResponse, FileResponse
41
  from processor import TranscriptionProcessor
42
 
43
+ import asyncio
44
+
45
  app = FastAPI()
46
+ # Lock para evitar que múltiplas requisições fritem a CPU/RAM simultaneamente
47
+ process_lock = asyncio.Lock()
48
 
49
  # Configuração de CORS Universal
50
  app.add_middleware(
 
65
  script: UploadFile = File(None)
66
  ):
67
  session_id = uuid.uuid4().hex[:8]
 
 
68
  audio_path = os.path.join(UPLOAD_DIR, f"{session_id}_{audio.filename}")
69
  script_path = None
70
 
71
+ # Garantir limpeza mesmo em erro fatal
72
  try:
73
+ # Salvamento
74
  with open(audio_path, "wb") as buffer:
75
  shutil.copyfileobj(audio.file, buffer)
76
 
77
+ async with process_lock: # FILA DE ESPERA INTELIGENTE
78
+ print(f"🚀 [SESSÃO {session_id}] Iniciando processamento exclusivo...")
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ script_text = ""
81
+ if script:
82
+ script_path = os.path.join(UPLOAD_DIR, f"{session_id}_{script.filename}")
83
+ with open(script_path, "wb") as buffer:
84
+ shutil.copyfileobj(script.file, buffer)
85
+ script_text = processor.process_docx(script_path)
86
+
87
+ # 1. Transcrição
88
+ words = processor.transcribe(audio_path, language="pt")
89
+
90
+ # 2. Correção de Roteiro (Fuzzy)
91
+ if script_text:
92
+ words = processor.align_with_script(words, script_text)
93
+
94
+ # 3. Limpeza Final
95
+ words = processor.correct_orthography(words)
96
+ result_json = processor.generate_json(words)
97
+
98
+ json_filename = f"{session_id}_transcription.json"
99
+ json_path = os.path.join(UPLOAD_DIR, json_filename)
100
+ with open(json_path, "w", encoding="utf-8") as f:
101
+ json.dump(result_json, f, ensure_ascii=False, indent=2)
102
 
103
+ return {
104
+ "success": True,
105
+ "session_id": session_id,
106
+ "words": words,
107
+ "json_url": f"/api/download/{json_filename}"
108
+ }
 
 
109
 
110
  except Exception as e:
111
+ print(f"❌ [ERRO SESSÃO {session_id}]: {str(e)}")
112
+ return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
113
+
114
+ finally:
115
+ # LIMPEZA OBRIGATÓRIA DE ÁUDIO (Anti-Leak)
116
+ if os.path.exists(audio_path):
117
+ os.remove(audio_path)
118
+ if script_path and os.path.exists(script_path):
119
+ os.remove(script_path)
120
+ gc.collect() # Garante liberação de RAM
121
 
122
  @app.get("/api/download/{filename}")
123
  async def download_json(filename: str):
backend/requirements.txt CHANGED
@@ -7,5 +7,6 @@ pandas==2.2.2
7
  onnxruntime
8
  accelerate
9
  nest_asyncio
10
- pyannote.audio==3.3.1
11
- git+https://github.com/m-bain/whisperX.git@v3.1.1
 
 
7
  onnxruntime
8
  accelerate
9
  nest_asyncio
10
+ pydub
11
+ librosa
12
+ git+https://github.com/m-bain/whisperX.git
google_colab/colab_app.py CHANGED
@@ -5,35 +5,38 @@ import os
5
  import sys
6
  import subprocess
7
  import difflib
 
8
 
9
  def install_safe_stack():
10
  print("🛠️ LIMPANDO E CURANDO AMBIENTE (Aguarde 3 min)...")
11
-
12
  try:
13
- # 1. Limpeza Radical para evitar conflitos de versões "sequestradas"
14
  print("🧹 Removendo versões instáveis...")
15
- subprocess.check_call([sys.executable, "-m", "pip", "uninstall", "-y", "torch", "torchaudio", "torchvision", "whisperx", "pandas"])
16
 
17
- # 2. Instalação Sincronizada (A "Santíssima Trindade" estável para T4)
18
- print("📦 Instalando PyTorch Stack Estável (2.5.1)...")
19
  subprocess.check_call([
20
- sys.executable, "-m", "pip", "install",
21
  "torch==2.5.1+cu121", "torchvision==0.20.1+cu121", "torchaudio==2.5.1+cu121",
22
- "pandas==2.2.2", # Versão que o Colab exige
23
- "--index-url", "https://download.pytorch.org/whl/cu121"
24
  ])
25
 
26
- # 3. WhisperX v3.1.1 (A versão mais estável já feita)
27
- print("📦 Instalando WhisperX v3.1.1...")
28
- subprocess.check_call([sys.executable, "-m", "pip", "install", "git+https://github.com/m-bain/whisperX.git@v3.1.1"])
29
-
30
- # 4. Dependências cruciais
31
- print("📦 Finalizando componentes...")
32
- subprocess.check_call([sys.executable, "-m", "pip", "install", "pyannote.audio==3.3.1", "gradio", "python-docx", "transformers", "accelerate", "nest_asyncio"])
 
 
 
 
33
  subprocess.check_call(["apt-get", "install", "-y", "-qq", "ffmpeg", "libsndfile1"])
34
 
35
- print("\n✅ AMBIENTE CURADO COM SUCESSO!")
36
- print("⚠️ AÇÃO NECESSÁRIA: Vá em 'Ambiente de Execução' > 'Reiniciar sessão' e rode esta célula de novo.")
37
  os.kill(os.getpid(), 9)
38
  except Exception as e:
39
  print(f"❌ Erro na cura: {e}")
 
5
  import sys
6
  import subprocess
7
  import difflib
8
+ import time # Added for time.sleep
9
 
10
  def install_safe_stack():
11
  print("🛠️ LIMPANDO E CURANDO AMBIENTE (Aguarde 3 min)...")
 
12
  try:
13
+ # 1. Limpeza pesada
14
  print("🧹 Removendo versões instáveis...")
15
+ subprocess.check_call([sys.executable, "-m", "pip", "uninstall", "-y", "torch", "torchvision", "torchaudio", "whisperx", "pandas"])
16
 
17
+ # 2. Instalação do Stack de Áudio/Vídeo (Caminho oficial CUDA 12.1)
18
+ print("📦 Instalando Motores CUDA...")
19
  subprocess.check_call([
20
+ sys.executable, "-m", "pip", "install",
21
  "torch==2.5.1+cu121", "torchvision==0.20.1+cu121", "torchaudio==2.5.1+cu121",
22
+ "--extra-index-url", "https://download.pytorch.org/whl/cu121"
 
23
  ])
24
 
25
+ # 3. Instalação de dependências de sistema e Python
26
+ print("📦 Instalando WhisperX e Pandas...")
27
+ subprocess.check_call([
28
+ sys.executable, "-m", "pip", "install",
29
+ "pandas==2.2.2", "pyannote.audio==3.3.1",
30
+ "git+https://github.com/m-bain/whisperX.git@v3.1.1",
31
+ "gradio", "python-docx", "transformers", "accelerate", "nest_asyncio" # Kept from original
32
+ ])
33
+
34
+ # 4. Dependências de sistema
35
+ print("📦 Finalizando componentes de sistema...")
36
  subprocess.check_call(["apt-get", "install", "-y", "-qq", "ffmpeg", "libsndfile1"])
37
 
38
+ print("\n✅ AMBIENTE CURADO! REINICIANDO PARA APLICAR...")
39
+ time.sleep(2)
40
  os.kill(os.getpid(), 9)
41
  except Exception as e:
42
  print(f"❌ Erro na cura: {e}")