Spaces:
Running
Running
S185: stop masking killed/bad-score jobs, force Itiqan aggressive everywhere, harden deep-filter
Browse files- Dockerfile +4 -0
- app.py +15 -6
- engine_safaa_v4.py +6 -2
Dockerfile
CHANGED
|
@@ -5,5 +5,9 @@ COPY requirements.txt .
|
|
| 5 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 6 |
COPY . .
|
| 7 |
RUN chmod +x /app/deep-filter || true
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
EXPOSE 7860
|
| 9 |
CMD ["python3", "app.py"]
|
|
|
|
| 5 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 6 |
COPY . .
|
| 7 |
RUN chmod +x /app/deep-filter || true
|
| 8 |
+
# S184/S185: pre-warm DF3 model into image so runtime never downloads
|
| 9 |
+
RUN ffmpeg -f lavfi -i "anullsrc=r=48000:cl=mono" -t 2 /tmp/df_warm.wav -y 2>/dev/null && \
|
| 10 |
+
/app/deep-filter -o /tmp /tmp/df_warm.wav 2>&1 || true && \
|
| 11 |
+
rm -f /tmp/df_warm.wav /tmp/in.wav
|
| 12 |
EXPOSE 7860
|
| 13 |
CMD ["python3", "app.py"]
|
app.py
CHANGED
|
@@ -49,7 +49,7 @@ DEFAULT_ENGINE = "v11.0"
|
|
| 49 |
REF_DIR = BASE / "reference_audio"
|
| 50 |
CHUNK_SIZE = 4 * 1024 * 1024
|
| 51 |
MAX_DOWNLOAD_CHUNK = 32 * 1024 * 1024
|
| 52 |
-
ENGINE_TIMEOUT =
|
| 53 |
|
| 54 |
_REF_CACHE = None
|
| 55 |
_REF_CACHE_TS = 0.0
|
|
@@ -342,19 +342,24 @@ def _run_engine(job_id):
|
|
| 342 |
"-i", job["in_path"], "-o", _o_path,
|
| 343 |
"--iterations", "3"]
|
| 344 |
for rf in ref_files[:3]: cmd += ["--ref", str(rf)]
|
|
|
|
|
|
|
| 345 |
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
| 346 |
stderr=subprocess.STDOUT, text=True)
|
| 347 |
engine_log = []
|
| 348 |
# S176: communicate() drains stdout AND enforces timeout atomically.
|
| 349 |
# The old `for line in proc.stdout:` blocked until process exit,
|
| 350 |
# making proc.wait(timeout=…) unreachable — silent hangs ate semaphores.
|
|
|
|
| 351 |
try:
|
| 352 |
_out, _ = proc.communicate(timeout=ENGINE_TIMEOUT)
|
| 353 |
except subprocess.TimeoutExpired:
|
| 354 |
proc.kill(); proc.wait()
|
|
|
|
| 355 |
job["engine_error"] = (
|
| 356 |
f"engine killed after {ENGINE_TIMEOUT}s — possible hang")
|
| 357 |
_out = ""
|
|
|
|
| 358 |
for line in _out.splitlines():
|
| 359 |
line = line.strip()
|
| 360 |
engine_log.append(line)
|
|
@@ -380,7 +385,7 @@ def _run_engine(job_id):
|
|
| 380 |
if m:
|
| 381 |
try:
|
| 382 |
s = float(m.group(1))
|
| 383 |
-
if
|
| 384 |
job["score"] = s; job["progress"] = 95
|
| 385 |
job["label"] = "حساب النتيجة..."
|
| 386 |
except Exception: pass
|
|
@@ -388,7 +393,8 @@ def _run_engine(job_id):
|
|
| 388 |
job["engine_log"] = engine_log
|
| 389 |
# S173 Bug2: engine wrote to _o_path (may be .wav for safaa)
|
| 390 |
_out = Path(_o_path)
|
| 391 |
-
|
|
|
|
| 392 |
if _is_safaa and _o_path != job["out_path"]:
|
| 393 |
subprocess.run(
|
| 394 |
["ffmpeg", "-y", "-i", str(_out),
|
|
@@ -397,8 +403,10 @@ def _run_engine(job_id):
|
|
| 397 |
try: _out.unlink()
|
| 398 |
except Exception: pass
|
| 399 |
success = True
|
| 400 |
-
|
| 401 |
-
|
|
|
|
|
|
|
| 402 |
except Exception as exc:
|
| 403 |
job["engine_error"] = str(exc)
|
| 404 |
if not success:
|
|
@@ -418,7 +426,8 @@ def _run_engine(job_id):
|
|
| 418 |
return
|
| 419 |
with JOBS_LOCK:
|
| 420 |
job["status"] = "done"; job["progress"] = 100; job["label"] = "اكتملت ✓"
|
| 421 |
-
if
|
|
|
|
| 422 |
_add_history(job)
|
| 423 |
try: Path(job["in_path"]).unlink(missing_ok=True)
|
| 424 |
except Exception: pass
|
|
|
|
| 49 |
REF_DIR = BASE / "reference_audio"
|
| 50 |
CHUNK_SIZE = 4 * 1024 * 1024
|
| 51 |
MAX_DOWNLOAD_CHUNK = 32 * 1024 * 1024
|
| 52 |
+
ENGINE_TIMEOUT = 600 # S185: 300s was killing ~7min recordings mid-pass
|
| 53 |
|
| 54 |
_REF_CACHE = None
|
| 55 |
_REF_CACHE_TS = 0.0
|
|
|
|
| 342 |
"-i", job["in_path"], "-o", _o_path,
|
| 343 |
"--iterations", "3"]
|
| 344 |
for rf in ref_files[:3]: cmd += ["--ref", str(rf)]
|
| 345 |
+
if "itiqan" in str(script).lower():
|
| 346 |
+
cmd.append("--aggressive") # S185: always aggressive, all spaces
|
| 347 |
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
| 348 |
stderr=subprocess.STDOUT, text=True)
|
| 349 |
engine_log = []
|
| 350 |
# S176: communicate() drains stdout AND enforces timeout atomically.
|
| 351 |
# The old `for line in proc.stdout:` blocked until process exit,
|
| 352 |
# making proc.wait(timeout=…) unreachable — silent hangs ate semaphores.
|
| 353 |
+
_timed_out = False
|
| 354 |
try:
|
| 355 |
_out, _ = proc.communicate(timeout=ENGINE_TIMEOUT)
|
| 356 |
except subprocess.TimeoutExpired:
|
| 357 |
proc.kill(); proc.wait()
|
| 358 |
+
_timed_out = True
|
| 359 |
job["engine_error"] = (
|
| 360 |
f"engine killed after {ENGINE_TIMEOUT}s — possible hang")
|
| 361 |
_out = ""
|
| 362 |
+
job["engine_rc"] = proc.returncode # S185: always recorded
|
| 363 |
for line in _out.splitlines():
|
| 364 |
line = line.strip()
|
| 365 |
engine_log.append(line)
|
|
|
|
| 385 |
if m:
|
| 386 |
try:
|
| 387 |
s = float(m.group(1))
|
| 388 |
+
if 0.0 <= s <= 100.0: # S185: was 50-100, hid real bad scores
|
| 389 |
job["score"] = s; job["progress"] = 95
|
| 390 |
job["label"] = "حساب النتيجة..."
|
| 391 |
except Exception: pass
|
|
|
|
| 393 |
job["engine_log"] = engine_log
|
| 394 |
# S173 Bug2: engine wrote to _o_path (may be .wav for safaa)
|
| 395 |
_out = Path(_o_path)
|
| 396 |
+
_clean = (not _timed_out) and proc.returncode == 0 # S185
|
| 397 |
+
if _clean and _out.exists() and _out.stat().st_size > 0:
|
| 398 |
if _is_safaa and _o_path != job["out_path"]:
|
| 399 |
subprocess.run(
|
| 400 |
["ffmpeg", "-y", "-i", str(_out),
|
|
|
|
| 403 |
try: _out.unlink()
|
| 404 |
except Exception: pass
|
| 405 |
success = True
|
| 406 |
+
elif not _clean:
|
| 407 |
+
# S185: killed or non-zero rc — don't trust a partial file,
|
| 408 |
+
# force the real ffmpeg fallback instead of faking success
|
| 409 |
+
job["engine_error"] = job.get("engine_error") or f"engine exited rc={proc.returncode}"
|
| 410 |
except Exception as exc:
|
| 411 |
job["engine_error"] = str(exc)
|
| 412 |
if not success:
|
|
|
|
| 426 |
return
|
| 427 |
with JOBS_LOCK:
|
| 428 |
job["status"] = "done"; job["progress"] = 100; job["label"] = "اكتملت ✓"
|
| 429 |
+
if job.get("score") is None and not job.get("used_fallback"):
|
| 430 |
+
job["score"] = 90 # S185: only on a clean, non-fallback run
|
| 431 |
_add_history(job)
|
| 432 |
try: Path(job["in_path"]).unlink(missing_ok=True)
|
| 433 |
except Exception: pass
|
engine_safaa_v4.py
CHANGED
|
@@ -573,8 +573,12 @@ def _df3(wav, samples, rt60, st):
|
|
| 573 |
r2 = subprocess.run(cmd, capture_output=True, timeout=600)
|
| 574 |
wp = os.path.join(od, 'in.wav')
|
| 575 |
if r2.returncode or not os.path.exists(wp):
|
| 576 |
-
|
| 577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 578 |
arr = _df3_pipe_decode(wp, SR)
|
| 579 |
if arr is None:
|
| 580 |
_L(st, f' [S4-ADF] {cls} decode failed — using source')
|
|
|
|
| 573 |
r2 = subprocess.run(cmd, capture_output=True, timeout=600)
|
| 574 |
wp = os.path.join(od, 'in.wav')
|
| 575 |
if r2.returncode or not os.path.exists(wp):
|
| 576 |
+
_stderr = r2.stderr.decode('utf-8', errors='replace')[:400] if r2.stderr else ''
|
| 577 |
+
_stdout = r2.stdout.decode('utf-8', errors='replace')[:200] if r2.stdout else ''
|
| 578 |
+
_L(st, f' [S4-ADF] {cls} pass failed rc={r2.returncode} cmd={cmd}')
|
| 579 |
+
_L(st, f' [S4-ADF] stderr: {_stderr!r}')
|
| 580 |
+
if _stdout: _L(st, f' [S4-ADF] stdout: {_stdout!r}')
|
| 581 |
+
return cls, mono.copy() # S183/S185: was silent
|
| 582 |
arr = _df3_pipe_decode(wp, SR)
|
| 583 |
if arr is None:
|
| 584 |
_L(st, f' [S4-ADF] {cls} decode failed — using source')
|