From a17b6a8172dedd8a552a9cc53d0e0eb7e3103304 Mon Sep 17 00:00:00 2001 From: Palash Debnath <4178343+debpalash@users.noreply.github.com> Date: Thu, 17 Sep 2026 12:47:12 +0530 Subject: [PATCH] fix: close lifecycle and diagnostic review regressions --- backend/services/dub_pipeline.py | 14 ++++++++++---- tests/test_dub_extract_diagnostics.py | 7 +++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/backend/services/dub_pipeline.py b/backend/services/dub_pipeline.py index e2f41f4c..b00a4432 100644 --- a/backend/services/dub_pipeline.py +++ b/backend/services/dub_pipeline.py @@ -65,10 +65,16 @@ from core.logging_utils import log_safe logger = logging.getLogger("omnivoice.dub_pipeline") -def _media_process_error(tool: str, returncode: int, stderr: bytes) -> str: +def _media_process_error(tool: str, returncode: int, stderr: bytes, *, paths=()) -> str: """Keep the actionable end of native diagnostics without leaking paths.""" from core.scrub import scrub_text - detail = scrub_text(stderr.decode(errors="replace")).strip() + detail = stderr.decode(errors="replace") + # Input/output may live outside home directories (mounted media, Windows + # drive roots). Remove the exact command paths before generic scrubbing. + for path in sorted((str(p) for p in paths if p), key=len, reverse=True): + for variant in {path, path.replace("\\", "/"), path.replace("/", "\\")}: + detail = detail.replace(variant, "[redacted path]") + detail = scrub_text(detail).strip() tail = detail[-2000:] if len(detail) > 2000: tail = "…" + tail @@ -1337,7 +1343,7 @@ async def ingest_pipeline( "-ar", "16000", "-ac", "1", audio_path, "-y", ]) if p.returncode != 0: - msg = _media_process_error("FFmpeg", p.returncode, stderr) + msg = _media_process_error("FFmpeg", p.returncode, stderr, paths=(video_path, audio_path, job_dir)) raise Exception(msg) # Second, FULL-QUALITY extraction for source separation. audio.wav # is deliberately 16 kHz mono — that's what ASR wants — but Demucs @@ -1487,7 +1493,7 @@ async def ingest_pipeline( elif evt[0] == "done": rc, stderr_full = evt[1], evt[2] if rc != 0: - raise Exception(_media_process_error("Demucs", rc, stderr_full)) + raise Exception(_media_process_error("Demucs", rc, stderr_full, paths=(audio_hq_path, audio_path, job_dir))) # Stems land under the INPUT's basename ("audio_hq" when the # full-quality extraction succeeded, "audio" on its fallback). demucs_out = os.path.join( diff --git a/tests/test_dub_extract_diagnostics.py b/tests/test_dub_extract_diagnostics.py index 6baf1f83..f66853eb 100644 --- a/tests/test_dub_extract_diagnostics.py +++ b/tests/test_dub_extract_diagnostics.py @@ -23,3 +23,10 @@ async def test_extract_error_preserves_failure_after_long_banner(tmp_path, monke def test_empty_native_diagnostic_retains_exit_code(): assert "code 7" in dub_pipeline._media_process_error("FFmpeg", 7, b"") + + +@pytest.mark.parametrize("path", ["/mnt/media/private.mp4", "D:\\Media\\private.mp4", "/opt/data/private.mp4"]) +def test_native_error_redacts_non_home_command_paths(path): + error = dub_pipeline._media_process_error("FFmpeg", 1, (path + ": Permission denied").encode(), paths=(path,)) + assert path not in error + assert "Permission denied" in error