"""One place that knows how to run ffmpeg and how to report it failing. Both the /analyze endpoint and the Stage Read pipeline shell out to ffmpeg, and both used to lose the reason it failed: one discarded the pipes, the other caught everything and returned False. A process that dies before main() then reports only a numeric exit status - on Windows 3221225794 is 0xC0000142, STATUS_DLL_INIT_FAILED, which says nothing about audio and everything about the environment the service was started in. """ import os import shutil import subprocess class FFmpegError(RuntimeError): """ffmpeg exited non-zero. The message carries its stderr where there was any.""" def ffmpeg_exe() -> str: """Absolute path to ffmpeg. Resolving it here beats relying on the PATH the service happened to inherit — a long-lived service can outlive the shell whose PATH it was started with.""" exe = os.environ.get("STAGE_READ_FFMPEG") or shutil.which("ffmpeg") if not exe: raise FFmpegError("ffmpeg is not on PATH. Install it, or set STAGE_READ_FFMPEG to its full path.") return exe def run_ffmpeg(args, timeout=None): """Run ffmpeg with the given arguments and, if it fails, say why. Passes -nostdin and an explicit DEVNULL: a service launched in the background can be holding a stdin that is no longer connected to anything, and ffmpeg reads stdin unless told not to. """ exe = ffmpeg_exe() cmd = [exe, "-nostdin", "-y", "-v", "error", *args] r = subprocess.run(cmd, stdin=subprocess.DEVNULL, capture_output=True, timeout=timeout) if r.returncode == 0: return r err = (r.stderr or b"").decode("utf-8", "replace").strip() detail = err.splitlines()[-3:] if err else [] status = r.returncode & 0xFFFFFFFF msg = f"ffmpeg failed (exit {r.returncode}, 0x{status:08X}) [{exe}]" if detail: msg += ": " + " | ".join(detail) elif status == 0xC0000142: msg += (": the process could not start (Windows STATUS_DLL_INIT_FAILED) - it produced no " "output at all. Restart the service; if it persists, set STAGE_READ_FFMPEG to a " "known-good ffmpeg.exe.") else: msg += ": no error output." raise FFmpegError(msg)