Migrate to Python daemon

This commit is contained in:
Thales Maciel 2026-02-07 15:12:17 -03:00
parent 49ef349d48
commit d81f3dbffe
42 changed files with 660 additions and 1816 deletions

70
src/recorder.py Normal file
View file

@ -0,0 +1,70 @@
import os
import signal
import subprocess
import tempfile
import time
from dataclasses import dataclass
from pathlib import Path
@dataclass
class RecordResult:
wav_path: str
temp_dir: str
def _resolve_ffmpeg_path(explicit: str) -> str:
if explicit:
return explicit
appdir = os.getenv("APPDIR")
if appdir:
candidate = Path(appdir) / "usr" / "bin" / "ffmpeg"
if candidate.exists():
return str(candidate)
return "ffmpeg"
def _ffmpeg_input_args(spec: str) -> list[str]:
if not spec:
spec = "pulse:default"
kind = spec
name = "default"
if ":" in spec:
kind, name = spec.split(":", 1)
return ["-f", kind, "-i", name]
def start_recording(ffmpeg_input: str, ffmpeg_path: str) -> tuple[subprocess.Popen, RecordResult]:
tmpdir = tempfile.mkdtemp(prefix="lel-")
wav = str(Path(tmpdir) / "mic.wav")
args = ["-hide_banner", "-loglevel", "error"]
args += _ffmpeg_input_args(ffmpeg_input)
args += ["-ac", "1", "-ar", "16000", "-c:a", "pcm_s16le", wav]
proc = subprocess.Popen(
[_resolve_ffmpeg_path(ffmpeg_path), *args],
preexec_fn=os.setsid,
)
return proc, RecordResult(wav_path=wav, temp_dir=tmpdir)
def stop_recording(proc: subprocess.Popen, timeout_sec: float = 5.0) -> None:
if proc.poll() is None:
try:
os.killpg(proc.pid, signal.SIGINT)
except ProcessLookupError:
return
start = time.time()
while proc.poll() is None:
if time.time() - start > timeout_sec:
try:
os.killpg(proc.pid, signal.SIGKILL)
except ProcessLookupError:
pass
break
time.sleep(0.05)
# ffmpeg returns 255 on SIGINT; treat as success
if proc.returncode not in (0, 255, None):
raise RuntimeError(f"ffmpeg exited with status {proc.returncode}")