Spaces:
Sleeping
Sleeping
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- Dockerfile +1 -1
- main-videopluskazanim.py +166 -96
- 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 |
-
|
39 |
-
import concurrent.futures
|
40 |
-
import threading
|
41 |
import kazanim_id_konu_isim_dict_list as kazanimlar
|
42 |
import logging
|
43 |
-
import
|
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
|
127 |
-
"""
|
128 |
try:
|
129 |
-
print(f"
|
130 |
-
|
131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
return model
|
133 |
|
134 |
except Exception as e:
|
135 |
-
print(f"
|
136 |
-
raise HTTPException(status_code=500, detail=f"
|
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 |
-
#
|
144 |
-
#
|
145 |
-
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
|
146 |
|
147 |
-
|
148 |
-
|
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
|
154 |
|
155 |
# Geçici dosya oluştur
|
156 |
-
|
|
|
157 |
content = await file.read()
|
158 |
temp_file.write(content)
|
159 |
temp_file_path = temp_file.name
|
160 |
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
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 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
239 |
t0 = time.time()
|
240 |
-
print(f"
|
|
|
241 |
|
242 |
try:
|
243 |
if not files:
|
244 |
raise HTTPException(status_code=400, detail="Video dosyaları boş olamaz")
|
245 |
|
246 |
-
#
|
247 |
-
model =
|
248 |
|
249 |
-
#
|
250 |
-
chunk_size = 16 # HF Space için güvenli chunk boyutu
|
251 |
final_results = []
|
252 |
|
253 |
-
print(f"
|
254 |
|
255 |
-
#
|
256 |
-
for i in
|
257 |
-
|
258 |
-
print(f"🔄 Chunk {i//chunk_size + 1}: {len(chunk)} dosya işleniyor...")
|
259 |
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
|
|
|
|
|
|
272 |
else:
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
error_count += 1
|
279 |
-
|
280 |
-
print(f"✅ Chunk {i//chunk_size + 1} tamamlandı! ✅{success_count} ❌{error_count}")
|
281 |
|
282 |
-
#
|
283 |
-
if i
|
284 |
-
|
|
|
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
|
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"⚡
|
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 =
|
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 |
-
|
12 |
typing-extensions
|
13 |
ffmpeg-python
|
|
|
8 |
python-multipart
|
9 |
requests
|
10 |
pandas
|
11 |
+
faster-whisper
|
12 |
typing-extensions
|
13 |
ffmpeg-python
|