""" ffmpeg / ffprobe discovery and probing helpers. Shared by both concat and singing detection features. """ import json import os import shutil import subprocess import sys from pathlib import Path # ── Suppress console windows on Windows ────────────────────────────────────── _NO_WINDOW: dict = {} if sys.platform == "win32": _NO_WINDOW["creationflags"] = subprocess.CREATE_NO_WINDOW def app_dir() -> str: if getattr(sys, "frozen", False): return os.path.dirname(sys.executable) return os.path.dirname(os.path.abspath(__file__)) def find_tool(name: str) -> str | None: """Locate ffmpeg / ffprobe next to the exe / script, then on PATH.""" base = app_dir() for sub in ["_internal", "..", ""]: for variant in [name + ".exe", name]: p = os.path.join(base, sub, variant) if os.path.exists(p): return os.path.abspath(p) return shutil.which(name + ".exe") or shutil.which(name) def probe_duration(path: str, ffprobe_bin: str | None = None) -> float: """Get video/audio duration in seconds via ffprobe.""" ffprobe = ffprobe_bin or find_tool("ffprobe") if not ffprobe: return 0.0 try: r = subprocess.run( [ffprobe, "-v", "error", "-show_entries", "format=duration", "-of", "json", path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=20, **_NO_WINDOW, ) if r.returncode != 0: return 0.0 data = json.loads(r.stdout or "{}") return max(0.0, float(data.get("format", {}).get("duration", 0) or 0)) except Exception: return 0.0 def extract_audio(video_path: str, output_wav: str, ffmpeg_bin: str | None = None, progress_cb=None) -> str: """ Extract audio from video file as 16kHz mono WAV. Required format for inaSpeechSegmenter. progress_cb: optional callable(message: str) for status updates. """ ffmpeg = ffmpeg_bin or find_tool("ffmpeg") if not ffmpeg: raise FileNotFoundError("ffmpeg not found. Install ffmpeg and add to PATH.") if progress_cb: progress_cb(f"Extracting audio from: {Path(video_path).name}") cmd = [ ffmpeg, "-i", video_path, "-vn", "-acodec", "pcm_s16le", "-ar", "16000", "-ac", "1", "-y", output_wav, ] result = subprocess.run( cmd, capture_output=True, text=True, timeout=1800, **_NO_WINDOW, ) if result.returncode != 0: raise RuntimeError(f"ffmpeg failed:\n{result.stderr[-500:]}") if progress_cb: size_mb = os.path.getsize(output_wav) / (1024 * 1024) progress_cb(f"Audio extracted: {size_mb:.1f} MB") return output_wav