eraydikyologlu commited on
Commit
11255ff
·
1 Parent(s): 39c78ba

Video işleme süreci güncellendi; artık faster-whisper modeli kullanılıyor ve işlem sırası tek tek gerçekleştiriliyor. Dockerfile'a libsndfile1 bağımlılığı eklendi. Kodda gereksiz kütüphaneler kaldırıldı ve hata yönetimi ile logging iyileştirildi.

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -1
  2. main-videopluskazanim.py +166 -96
  3. requirements.txt +1 -1
Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
  FROM python:3.10
2
- RUN apt-get update && apt-get install -y ffmpeg
3
  WORKDIR /app
4
  COPY requirements.txt .
5
  RUN pip install -r requirements.txt
 
1
  FROM python:3.10
2
+ RUN apt-get update && apt-get install -y ffmpeg libsndfile1
3
  WORKDIR /app
4
  COPY requirements.txt .
5
  RUN pip install -r requirements.txt
main-videopluskazanim.py CHANGED
@@ -35,12 +35,10 @@ from typing import List, Dict, Optional
35
  from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline
36
  import torch
37
  import functools
38
- import asyncio
39
- import concurrent.futures
40
- import threading
41
  import kazanim_id_konu_isim_dict_list as kazanimlar
42
  import logging
43
- import whisper
44
  import tempfile
45
  import os
46
  import logging
@@ -123,77 +121,87 @@ def load_pipeline(model_name: str):
123
  raise HTTPException(status_code=500, detail=f"Model yükleme hatası: {str(e)}")
124
 
125
  @functools.lru_cache(maxsize=4)
126
- def load_whisper_model(model_name: str):
127
- """Whisper model yükleme"""
128
  try:
129
- print(f"Whisper modeli yükleniyor: {model_name}")
130
- model = whisper.load_model(model_name)
131
- print(f"Whisper modeli başarıyla yüklendi: {model_name}")
 
 
 
 
 
 
 
 
132
  return model
133
 
134
  except Exception as e:
135
- print(f"Whisper model yükleme hatası ({model_name}): {e}")
136
- raise HTTPException(status_code=500, detail=f"Whisper model yükleme hatası: {str(e)}")
137
 
138
  import time, logging, sys
139
  logging.basicConfig(stream=sys.stdout,
140
  level=logging.INFO,
141
  format="%(asctime)s %(levelname)s %(message)s")
142
 
143
- # Thread pool for parallel processing - HF Space friendly
144
- # HF Space'lerde çok aggressive olmamak için worker sayısını azalttık
145
- executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
146
 
147
- # Thread-safe model kullanımı için lock
148
- whisper_lock = threading.Lock()
149
-
150
- async def process_single_video(file: UploadFile, model, language: str) -> VideoResult:
151
- """Tek bir video dosyasını işle - THREAD SAFE paralel kullanım için"""
152
  if not file.filename.lower().endswith(('.mp4', '.wav', '.mp3', '.m4a', '.flac')):
153
- return VideoResult(id=file.filename, text="")
154
 
155
  # Geçici dosya oluştur
156
- with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as temp_file:
 
157
  content = await file.read()
158
  temp_file.write(content)
159
  temp_file_path = temp_file.name
160
 
161
- def transcribe_sync():
162
- """Senkron transcription - thread pool'da çalışacak - THREAD SAFE"""
163
- try:
164
- # 🔒 THREAD SAFETY: Model kullanımını serialize et
165
- with whisper_lock:
166
- print(f"🔄 {file.filename}: Model kullanımı başlıyor...")
167
- result = model.transcribe(
168
- temp_file_path,
169
- language=language.lower(),
170
- verbose=False
171
- )
172
- text = result['text'].strip()
173
-
174
- # Model çıktısının bir kısmını logla (debug için)
175
- preview = text[:150] + "..." if len(text) > 150 else text
176
- print(f"📝 {file.filename}: {preview}")
177
-
178
- return text
179
-
180
- except Exception as e:
181
- print(f"❌ Video işleme hatası ({file.filename}): {e}")
182
- return ""
183
-
184
- finally:
185
- # Geçici dosyayı temizle
186
- if os.path.exists(temp_file_path):
187
- try:
188
- os.unlink(temp_file_path)
189
- except:
190
- pass # HF Space'te silme hatası olabilir
191
 
192
- # Thread pool'da transcription çalıştır
193
- loop = asyncio.get_event_loop()
194
- text = await loop.run_in_executor(executor, transcribe_sync)
195
-
196
- return VideoResult(id=file.filename, text=text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
  @app.post("/predict", response_model=PredictResponse)
199
  async def predict(req: PredictRequest):
@@ -231,57 +239,118 @@ async def predict(req: PredictRequest):
231
  traceback.print_exc()
232
  raise HTTPException(status_code=500, detail=f"Hata: {str(e)}")
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  @app.post("/whisper", response_model=WhisperResponse)
235
  async def transcribe_videos(files: List[UploadFile] = File(...),
236
  model_name: str = "small",
237
- language: str = "Turkish"):
238
- """Video dosyalarını metne çevir - PARALEL İŞLEME"""
 
 
 
239
  t0 = time.time()
240
- print(f"🚀 new whisper request /model = {model_name} / n = {len(files)} - PARALEL İŞLEME BAŞLIYOR")
 
241
 
242
  try:
243
  if not files:
244
  raise HTTPException(status_code=400, detail="Video dosyaları boş olamaz")
245
 
246
- # Whisper modelini yükle
247
- model = load_whisper_model(model_name)
248
 
249
- # HF SPACE İÇİN CHUNK'LI PARALEL İŞLEME
250
- chunk_size = 16 # HF Space için güvenli chunk boyutu
251
  final_results = []
252
 
253
- print(f"📡 {len(files)} dosya {chunk_size}'lı chunk'larda paralel işlenecek...")
254
 
255
- # Dosyaları chunk'lara böl ve her chunk'ı paralel işle
256
- for i in range(0, len(files), chunk_size):
257
- chunk = files[i:i + chunk_size]
258
- print(f"🔄 Chunk {i//chunk_size + 1}: {len(chunk)} dosya işleniyor...")
259
 
260
- # Bu chunk'ı paralel işle
261
- tasks = [process_single_video(file, model, language) for file in chunk]
262
- chunk_results = await asyncio.gather(*tasks, return_exceptions=True)
263
-
264
- # Exception'ları handle et + SUCCESS/ERROR COUNT
265
- success_count = 0
266
- error_count = 0
267
- for j, result in enumerate(chunk_results):
268
- if isinstance(result, Exception):
269
- print(f"❌ Dosya {chunk[j].filename} işlenirken hata: {result}")
270
- final_results.append(VideoResult(id=chunk[j].filename, text=""))
271
- error_count += 1
 
 
 
272
  else:
273
- final_results.append(result)
274
- # Başarılı sayısı = boş olmayan text'ler
275
- if result.text.strip():
276
- success_count += 1
277
- else:
278
- error_count += 1
279
-
280
- print(f"✅ Chunk {i//chunk_size + 1} tamamlandı! ✅{success_count} ❌{error_count}")
281
 
282
- # Memory'yi rahatlatmak için küçük bir bekleme (HF Space için)
283
- if i + chunk_size < len(files):
284
- await asyncio.sleep(0.1)
 
285
 
286
  dt = time.time() - t0
287
 
@@ -290,9 +359,9 @@ async def transcribe_videos(files: List[UploadFile] = File(...),
290
  total_failed = len(final_results) - total_success
291
  success_rate = (total_success / len(final_results) * 100) if final_results else 0
292
 
293
- print(f"✅ Whisper PARALEL done | took {dt:.2f}s")
294
- print(f"🎯 SONUÇ: {len(final_results)} video | ✅{total_success} başarılı | ❌{total_failed} hatalı | 📊{success_rate:.1f}% başarı oranı")
295
- print(f"⚡ Hız: {len(final_results)/dt:.1f} video/saniye (chunk_size={chunk_size})")
296
 
297
  return WhisperResponse(model=model_name, results=final_results)
298
 
@@ -318,14 +387,15 @@ def health_check():
318
  device_info = f"GPU: {gpu_name}"
319
 
320
  bert_models = load_pipeline.cache_info().currsize if hasattr(load_pipeline, 'cache_info') else 0
321
- whisper_models = load_whisper_model.cache_info().currsize if hasattr(load_whisper_model, 'cache_info') else 0
322
 
323
  return {
324
  "status": "healthy",
325
  "device": device_info,
326
  "bert_models_loaded": bert_models,
327
  "whisper_models_loaded": whisper_models,
328
- "endpoints": ["/predict", "/whisper", "/health"]
 
329
  }
330
  except Exception as e:
331
  return {"status": "error", "message": f"Sağlık kontrolü hatası: {str(e)}"}
 
35
  from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline
36
  import torch
37
  import functools
38
+ # asyncio, concurrent.futures, threading kaldırıldı - direkt sequential processing
 
 
39
  import kazanim_id_konu_isim_dict_list as kazanimlar
40
  import logging
41
+ from faster_whisper import WhisperModel
42
  import tempfile
43
  import os
44
  import logging
 
121
  raise HTTPException(status_code=500, detail=f"Model yükleme hatası: {str(e)}")
122
 
123
  @functools.lru_cache(maxsize=4)
124
+ def load_faster_whisper_model(model_name: str):
125
+ """faster-whisper model yükleme"""
126
  try:
127
+ print(f"faster-whisper modeli yükleniyor: {model_name}")
128
+
129
+ # CPU/GPU device seçimi
130
+ device = "cuda" if torch.cuda.is_available() else "cpu"
131
+ compute_type = "float16" if device == "cuda" else "int8"
132
+
133
+ print(f"Device: {device}, Compute type: {compute_type}")
134
+
135
+ # faster-whisper modeli yükle
136
+ model = WhisperModel(model_name, device=device, compute_type=compute_type)
137
+ print(f"✅ faster-whisper modeli başarıyla yüklendi: {model_name}")
138
  return model
139
 
140
  except Exception as e:
141
+ print(f"faster-whisper model yükleme hatası ({model_name}): {e}")
142
+ raise HTTPException(status_code=500, detail=f"faster-whisper model yükleme hatası: {str(e)}")
143
 
144
  import time, logging, sys
145
  logging.basicConfig(stream=sys.stdout,
146
  level=logging.INFO,
147
  format="%(asctime)s %(levelname)s %(message)s")
148
 
149
+ # WORKER YOK - Direkt sequential processing
150
+ # faster-whisper kullanıyoruz artık
 
151
 
152
+ async def prepare_video_file(file: UploadFile) -> tuple[str, str]:
153
+ """Video dosyasını geçici dizine kaydet - Validation ile"""
 
 
 
154
  if not file.filename.lower().endswith(('.mp4', '.wav', '.mp3', '.m4a', '.flac')):
155
+ return file.filename, ""
156
 
157
  # Geçici dosya oluştur
158
+ original_ext = os.path.splitext(file.filename)[1]
159
+ with tempfile.NamedTemporaryFile(delete=False, suffix=original_ext) as temp_file:
160
  content = await file.read()
161
  temp_file.write(content)
162
  temp_file_path = temp_file.name
163
 
164
+ # FILE VALIDATION: Dosya boyutu kontrolü
165
+ file_size = len(content)
166
+ if file_size < 1000: # 1KB'den küçükse corrupt
167
+ print(f"❌ {file.filename}: Dosya çok küçük ({file_size} bytes)")
168
+ os.unlink(temp_file_path)
169
+ return file.filename, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
+ print(f"✅ {file.filename}: Dosya geçerli ({file_size} bytes)")
172
+ return file.filename, temp_file_path
173
+
174
+ def process_single_video_sync(file_path: str, filename: str, model, language: str) -> VideoResult:
175
+ """Tek bir video dosyasını işle - DIREKT SEQUENTIAL (worker yok)"""
176
+ try:
177
+ print(f"🔄 {filename}: faster-whisper ile işleniyor...")
178
+
179
+ # faster-whisper ile transcription
180
+ segments, info = model.transcribe(file_path, language=language.lower())
181
+
182
+ # Segmentleri birleştir
183
+ text = ""
184
+ for segment in segments:
185
+ text += segment.text
186
+ text = text.strip()
187
+
188
+ # Model çıktısının bir kısmını logla (debug için)
189
+ preview = text[:150] + "..." if len(text) > 150 else text
190
+ print(f"📝 {filename}: {preview}")
191
+
192
+ return VideoResult(id=filename, text=text)
193
+
194
+ except Exception as e:
195
+ print(f"❌ Video işleme hatası ({filename}): {e}")
196
+ return VideoResult(id=filename, text="")
197
+
198
+ finally:
199
+ # Geçici dosyayı temizle
200
+ if os.path.exists(file_path):
201
+ try:
202
+ os.unlink(file_path)
203
+ except:
204
+ pass
205
 
206
  @app.post("/predict", response_model=PredictResponse)
207
  async def predict(req: PredictRequest):
 
239
  traceback.print_exc()
240
  raise HTTPException(status_code=500, detail=f"Hata: {str(e)}")
241
 
242
+ @app.post("/whisper-single", response_model=WhisperResponse)
243
+ async def transcribe_single_video(file: UploadFile = File(...),
244
+ model_name: str = "small",
245
+ language: str = "Turkish",
246
+ ustkurumid: Optional[str] = None,
247
+ testid: Optional[str] = None,
248
+ soruno: Optional[str] = None):
249
+ """TEK VİDEO işleme - Bug'sız tek tek processing"""
250
+ t0 = time.time()
251
+ print(f"🎯 TEK VIDEO işleme başlıyor: {file.filename}")
252
+ print(f"📊 Request info: ustkurumid={ustkurumid}, testid={testid}, soruno={soruno}")
253
+
254
+ try:
255
+ if not file.filename.lower().endswith(('.mp4', '.wav', '.mp3', '.m4a', '.flac')):
256
+ raise HTTPException(status_code=400, detail="Desteklenmeyen dosya formatı")
257
+
258
+ # faster-whisper Model yükle
259
+ model = load_faster_whisper_model(model_name)
260
+
261
+ # Dosyayı hazırla
262
+ print(f"📁 Dosya hazırlanıyor: {file.filename}")
263
+ file_name, file_path = await prepare_video_file(file)
264
+
265
+ if not file_path:
266
+ return WhisperResponse(model=model_name, results=[VideoResult(id=file_name, text="")])
267
+
268
+ print(f"🚀 Transcription başlıyor: {file_name}")
269
+
270
+ # faster-whisper ile TEK VİDEO işleme - Bug yok!
271
+ segments, info = model.transcribe(file_path, language=language.lower())
272
+
273
+ # Segmentleri birleştir
274
+ text = ""
275
+ for segment in segments:
276
+ text += segment.text
277
+ text = text.strip()
278
+
279
+ # Model çıktısının bir kısmını logla
280
+ preview = text[:150] + "..." if len(text) > 150 else text
281
+ print(f"📝 {file_name}: {preview}")
282
+
283
+ # Geçici dosyayı temizle
284
+ try:
285
+ os.unlink(file_path)
286
+ except:
287
+ pass
288
+
289
+ dt = time.time() - t0
290
+ print(f"✅ TEK VIDEO tamamlandı | took {dt:.2f}s")
291
+
292
+ return WhisperResponse(model=model_name, results=[VideoResult(id=file_name, text=text)])
293
+
294
+ except Exception as e:
295
+ print(f"❌ Tek video hatası: {e}")
296
+ import traceback
297
+ traceback.print_exc()
298
+ raise HTTPException(status_code=500, detail=f"Tek video hatası: {str(e)}")
299
+
300
  @app.post("/whisper", response_model=WhisperResponse)
301
  async def transcribe_videos(files: List[UploadFile] = File(...),
302
  model_name: str = "small",
303
+ language: str = "Turkish",
304
+ ustkurumid: Optional[str] = None,
305
+ testid: Optional[str] = None,
306
+ soruno: Optional[str] = None):
307
+ """Video dosyalarını metne çevir - TEK TEK İŞLEME (Bug'sız)"""
308
  t0 = time.time()
309
+ print(f"🎯 TEK TEK whisper request /model = {model_name} / n = {len(files)} - SEQUENTIAL İŞLEME")
310
+ print(f"📊 Batch Request info: ustkurumid={ustkurumid}, testid={testid}, soruno={soruno}")
311
 
312
  try:
313
  if not files:
314
  raise HTTPException(status_code=400, detail="Video dosyaları boş olamaz")
315
 
316
+ # faster-whisper modelini yükle
317
+ model = load_faster_whisper_model(model_name)
318
 
319
+ # 🎯 TEK TEK SEQUENTIAL İŞLEME - Bug'sız!
 
320
  final_results = []
321
 
322
+ print(f"📝 {len(files)} dosya tek tek sırayla işlenecek (bug'sız)...")
323
 
324
+ # Her dosyayı sırasıyla tek tek işle
325
+ for i, file in enumerate(files):
326
+ print(f"🔄 Video {i+1}/{len(files)}: {file.filename} işleniyor...")
 
327
 
328
+ try:
329
+ # Dosyayı hazırla
330
+ file_name, file_path = await prepare_video_file(file)
331
+
332
+ if not file_path:
333
+ final_results.append(VideoResult(id=file_name, text=""))
334
+ continue
335
+
336
+ # TEK TEK işleme - DIREKT SEQUENTIAL
337
+ result = process_single_video_sync(file_path, file_name, model, language)
338
+ final_results.append(result)
339
+
340
+ # Başarı durumunu logla
341
+ if result.text.strip():
342
+ print(f"✅ {file.filename}: Başarılı!")
343
  else:
344
+ print(f"❌ {file.filename}: Boş sonuç!")
345
+
346
+ except Exception as e:
347
+ print(f"❌ {file.filename}: İşleme hatası: {e}")
348
+ final_results.append(VideoResult(id=file.filename, text=""))
 
 
 
349
 
350
+ # Her dosya arasında kısa bekleme (stability için)
351
+ if i < len(files) - 1:
352
+ import asyncio
353
+ await asyncio.sleep(0.5)
354
 
355
  dt = time.time() - t0
356
 
 
359
  total_failed = len(final_results) - total_success
360
  success_rate = (total_success / len(final_results) * 100) if final_results else 0
361
 
362
+ print(f"✅ Whisper SEQUENTIAL done | took {dt:.2f}s")
363
+ print(f"🎯 TEK TEK SONUÇ: {len(final_results)} video | ✅{total_success} başarılı | ❌{total_failed} hatalı | 📊{success_rate:.1f}% başarı oranı")
364
+ print(f"⚡ SEQUENTIAL HIZI: {len(final_results)/dt:.1f} video/saniye | Bug'sız stabil!")
365
 
366
  return WhisperResponse(model=model_name, results=final_results)
367
 
 
387
  device_info = f"GPU: {gpu_name}"
388
 
389
  bert_models = load_pipeline.cache_info().currsize if hasattr(load_pipeline, 'cache_info') else 0
390
+ whisper_models = load_faster_whisper_model.cache_info().currsize if hasattr(load_faster_whisper_model, 'cache_info') else 0
391
 
392
  return {
393
  "status": "healthy",
394
  "device": device_info,
395
  "bert_models_loaded": bert_models,
396
  "whisper_models_loaded": whisper_models,
397
+ "endpoints": ["/predict", "/whisper", "/whisper-single", "/health"],
398
+ "processing_mode": "SEQUENTIAL - Tek tek bug'sız işleme"
399
  }
400
  except Exception as e:
401
  return {"status": "error", "message": f"Sağlık kontrolü hatası: {str(e)}"}
requirements.txt CHANGED
@@ -8,6 +8,6 @@ uvicorn
8
  python-multipart
9
  requests
10
  pandas
11
- openai-whisper==20250625
12
  typing-extensions
13
  ffmpeg-python
 
8
  python-multipart
9
  requests
10
  pandas
11
+ faster-whisper
12
  typing-extensions
13
  ffmpeg-python