- capture/: frame sampling at configurable fps, ffmpeg audio extraction (WAV, whisper-ready) - vision/: changed-pixel motion scoring, stable/moving segmentation, contour + perspective document detection, Laplacian sharpness scoring, optional CLAHE enhancement - pipeline: two-pass detect (motion timeline, then best-frame crop per stable window) writing crops, debug frames, report.json, and motion_scores.csv - CLI: probe / detect / extract-audio subcommands - config.yaml with tunable thresholds; placeholder packages for events, transcribe, ocr, naming, pdf, review_cli - synthetic sample video generator + 16 unit tests
41 lines
1.2 KiB
Python
41 lines
1.2 KiB
Python
"""Audio track extraction (WAV, whisper-ready) via ffmpeg."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
|
|
def extract_audio(
|
|
video_path: str | Path,
|
|
wav_path: str | Path,
|
|
sample_rate: int = 16000,
|
|
) -> Path:
|
|
"""Extract the audio track as mono WAV at sample_rate. Requires ffmpeg."""
|
|
video_path = Path(video_path)
|
|
wav_path = Path(wav_path)
|
|
if not video_path.exists():
|
|
raise FileNotFoundError(f"Video not found: {video_path}")
|
|
if shutil.which("ffmpeg") is None:
|
|
raise RuntimeError(
|
|
"ffmpeg not found on PATH. Install it with: brew install ffmpeg"
|
|
)
|
|
wav_path.parent.mkdir(parents=True, exist_ok=True)
|
|
cmd = [
|
|
"ffmpeg",
|
|
"-y",
|
|
"-i", str(video_path),
|
|
"-vn",
|
|
"-ac", "1",
|
|
"-ar", str(sample_rate),
|
|
"-f", "wav",
|
|
str(wav_path),
|
|
]
|
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
if result.returncode != 0:
|
|
stderr = result.stderr.strip().splitlines()
|
|
tail = "\n".join(stderr[-5:])
|
|
raise RuntimeError(f"ffmpeg failed extracting audio:\n{tail}")
|
|
return wav_path
|