hatimqman commited on
Commit ·
c8d246c
1
Parent(s): 5c6eaf8
طوابع زمنيّة لكلّ كلمة (word_timestamps) لضبط حدود الآيات صوتيًّا
Browse files
app.py
CHANGED
|
@@ -87,15 +87,26 @@ async def transcribe(request: Request):
|
|
| 87 |
return {"text": (out.get("text") or "").strip(), "model": "quran"}
|
| 88 |
# VAD + عتبات مضادّة للهلوسة: large-v3 يهلوس على الصمت/المقاطع القصيرة (نصوص يوتيوب دخيلة)،
|
| 89 |
# فيُنتج نصًّا لا يطابق فيتجمّد الوسم الحيّ. VAD يحذف السكتات قبل التفريغ، والعتبات تُسقط الهلوسة.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
segments, _ = _general().transcribe(
|
| 91 |
audio, language="ar", beam_size=1, temperature=0.0,
|
| 92 |
-
without_timestamps=True, condition_on_previous_text=False,
|
| 93 |
vad_filter=True, vad_parameters=dict(min_silence_duration_ms=300, speech_pad_ms=120),
|
| 94 |
no_speech_threshold=0.6, compression_ratio_threshold=2.4, log_prob_threshold=-1.0,
|
| 95 |
)
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
except Exception as e:
|
| 100 |
print(f"[DIAG] EXC {type(e).__name__}: {e}", flush=True)
|
| 101 |
return JSONResponse({"text": "", "error": str(e), "model": which}, status_code=200)
|
|
|
|
| 87 |
return {"text": (out.get("text") or "").strip(), "model": "quran"}
|
| 88 |
# VAD + عتبات مضادّة للهلوسة: large-v3 يهلوس على الصمت/المقاطع القصيرة (نصوص يوتيوب دخيلة)،
|
| 89 |
# فيُنتج نصًّا لا يطابق فيتجمّد الوسم الحيّ. VAD يحذف السكتات قبل التفريغ، والعتبات تُسقط الهلوسة.
|
| 90 |
+
# ★ 2026-08-13: word_timestamps=True — طوابع زمنيّة لكلّ كلمة (محاذاة DTW على أوزان الانتباه
|
| 91 |
+
# داخل faster-whisper نفسه، بلا نموذجٍ إضافيّ ولا تحميلٍ جديد ولا إقلاعٍ باردٍ أطول).
|
| 92 |
+
# السبب: العميل كان **يستنتج** حدود الآيات من التتبّع النصّيّ مع تعويض زمن الاستدلال (تقدير)،
|
| 93 |
+
# فينزاح مقطع الآية المُرسَل لمحرّك QPS فينهار تطابقه رغم صحّة التلاوة (رُصد ١٢٪ و٢٪ حيًّا على
|
| 94 |
+
# سورة الزلزلة). بهذه الطوابع تصير حدود الآيات **صوتيّةً مضبوطة** لا مُقدَّرة.
|
| 95 |
+
# حقل `words` إضافيٌّ فقط — `text` يبقى كما هو تمامًا، فالعملاء القدامى لا يتأثّرون.
|
| 96 |
segments, _ = _general().transcribe(
|
| 97 |
audio, language="ar", beam_size=1, temperature=0.0,
|
| 98 |
+
without_timestamps=False, word_timestamps=True, condition_on_previous_text=False,
|
| 99 |
vad_filter=True, vad_parameters=dict(min_silence_duration_ms=300, speech_pad_ms=120),
|
| 100 |
no_speech_threshold=0.6, compression_ratio_threshold=2.4, log_prob_threshold=-1.0,
|
| 101 |
)
|
| 102 |
+
segs = list(segments)
|
| 103 |
+
txt = "".join(s.text for s in segs).strip()
|
| 104 |
+
words = []
|
| 105 |
+
for s in segs:
|
| 106 |
+
for w in (getattr(s, "words", None) or []):
|
| 107 |
+
words.append({"w": w.word.strip(), "s": round(float(w.start), 3), "e": round(float(w.end), 3)})
|
| 108 |
+
print(f"[DIAG] general → {txt!r} ({len(words)} words)", flush=True)
|
| 109 |
+
return {"text": txt, "model": "general", "words": words}
|
| 110 |
except Exception as e:
|
| 111 |
print(f"[DIAG] EXC {type(e).__name__}: {e}", flush=True)
|
| 112 |
return JSONResponse({"text": "", "error": str(e), "model": which}, status_code=200)
|