|
@@ -1,4 +1,6 @@
|
|
|
import tempfile
|
|
import tempfile
|
|
|
|
|
+import re
|
|
|
|
|
+import unicodedata
|
|
|
import os
|
|
import os
|
|
|
import numpy as np
|
|
import numpy as np
|
|
|
import time
|
|
import time
|
|
@@ -19,6 +21,7 @@ VAD_ENABLED = env_bool("WHISPER_VAD", True)
|
|
|
MODEL_SIZE = str(os.getenv("MODEL_SIZE", "small").lower())
|
|
MODEL_SIZE = str(os.getenv("MODEL_SIZE", "small").lower())
|
|
|
COMPUTE_TYPE = os.getenv("WHISPER_COMPUTE_TYPE", "float32").strip().lower()
|
|
COMPUTE_TYPE = os.getenv("WHISPER_COMPUTE_TYPE", "float32").strip().lower()
|
|
|
BEAM_SIZE = int(os.getenv("WHISPER_BEAM_SIZE", "1"))
|
|
BEAM_SIZE = int(os.getenv("WHISPER_BEAM_SIZE", "1"))
|
|
|
|
|
+HOTWORDS = os.getenv("WHISPER_HOTWORDS", "Satoko").strip()
|
|
|
if BEAM_SIZE < 1:
|
|
if BEAM_SIZE < 1:
|
|
|
raise ValueError("WHISPER_BEAM_SIZE doit être supérieur ou égal à 1")
|
|
raise ValueError("WHISPER_BEAM_SIZE doit être supérieur ou égal à 1")
|
|
|
MIN_AUDIO_SEC = float(os.getenv("MIN_AUDIO_SEC", "2.0"))
|
|
MIN_AUDIO_SEC = float(os.getenv("MIN_AUDIO_SEC", "2.0"))
|
|
@@ -49,14 +52,31 @@ model = WhisperModel(MODEL_SIZE, device="cuda", compute_type=COMPUTE_TYPE)
|
|
|
list(model.transcribe(np.zeros(16000, dtype=np.float32), language="fr", beam_size=BEAM_SIZE)[0])
|
|
list(model.transcribe(np.zeros(16000, dtype=np.float32), language="fr", beam_size=BEAM_SIZE)[0])
|
|
|
model_lock = Lock()
|
|
model_lock = Lock()
|
|
|
|
|
|
|
|
|
|
+def is_subtitle_artifact(text):
|
|
|
|
|
+ normalized = unicodedata.normalize("NFKD", text.lower())
|
|
|
|
|
+ normalized = "".join(c for c in normalized if not unicodedata.combining(c))
|
|
|
|
|
+ normalized = re.sub(r"[^a-z0-9]+", " ", normalized).strip()
|
|
|
|
|
+ return bool(re.fullmatch(
|
|
|
|
|
+ r"(?:sous titrage|sous titres?) (?:st ?501|mfp|www amara org|amara org)"
|
|
|
|
|
+ r"|sous titres? (?:realises? par la communaute )?d? ?amara org",
|
|
|
|
|
+ normalized,
|
|
|
|
|
+ ))
|
|
|
|
|
+
|
|
|
def transcribe_audio(audio):
|
|
def transcribe_audio(audio):
|
|
|
# Consommer le générateur sous le verrou, hors de la boucle asyncio.
|
|
# Consommer le générateur sous le verrou, hors de la boucle asyncio.
|
|
|
with model_lock:
|
|
with model_lock:
|
|
|
segments, info = model.transcribe(
|
|
segments, info = model.transcribe(
|
|
|
audio, language="fr", vad_filter=VAD_ENABLED,
|
|
audio, language="fr", vad_filter=VAD_ENABLED,
|
|
|
beam_size=BEAM_SIZE, condition_on_previous_text=False,
|
|
beam_size=BEAM_SIZE, condition_on_previous_text=False,
|
|
|
|
|
+ hotwords=HOTWORDS or None,
|
|
|
)
|
|
)
|
|
|
- return " ".join(seg.text for seg in segments).strip(), info.language
|
|
|
|
|
|
|
+ texts = []
|
|
|
|
|
+ for segment in segments:
|
|
|
|
|
+ if is_subtitle_artifact(segment.text):
|
|
|
|
|
+ logger.info("Segment parasite de sous-titrage ignoré")
|
|
|
|
|
+ else:
|
|
|
|
|
+ texts.append(segment.text)
|
|
|
|
|
+ return " ".join(texts).strip(), info.language
|
|
|
|
|
|
|
|
@app.get("/healthz")
|
|
@app.get("/healthz")
|
|
|
def healthz():
|
|
def healthz():
|