record: fix wf-recorder audio flag and proc error handling (#48)

* fix(recording): Fix wf-recorder audio flag and improve process monitoring

- Fix incorrect audio flag format for wf-recorder(Invalid whitespace)
  Changed from `-a <device>` to `--audio=<device>` as per wf-recorder docs:
  "Specify device like this: -a<device> or --audio=<device>"

- Add fallback to IDLE audio sources
  Audio sources are typically in IDLE state when no media is playing.
  Now falls back to IDLE sources if no RUNNING sources are found,
  ensuring audio capture works when recording starts during silence
  but media plays later.

- Improve process startup monitoring
  The 0.1s sleep was insufficient for reliable process detection on NVIDIA systems.
  Process would start and immediately die ~90% of the time when triggered via keybinds.
  Now shows immediate UI feedback then monitors for 3 seconds to ensure
  stable process startup while maintaining responsive user experience.

* check returncode + timeout 3s -> 1s + format

---------

Co-authored-by: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>
This commit is contained in:
Matheus Oliveira
2025-08-30 14:59:26 +03:00
committed by GitHub
parent 46a9516f72
commit 35b10394b6
2 changed files with 44 additions and 26 deletions
+29 -26
View File
@@ -5,7 +5,7 @@ import time
from argparse import Namespace from argparse import Namespace
from datetime import datetime from datetime import datetime
from caelestia.utils.notify import notify from caelestia.utils.notify import close_notification, notify
from caelestia.utils.paths import recording_notif_path, recording_path, recordings_dir from caelestia.utils.paths import recording_notif_path, recording_path, recordings_dir
@@ -66,16 +66,28 @@ class Command:
if self.args.sound: if self.args.sound:
sources = subprocess.check_output(["pactl", "list", "short", "sources"], text=True).splitlines() sources = subprocess.check_output(["pactl", "list", "short", "sources"], text=True).splitlines()
audio_source = None
for source in sources: for source in sources:
if "RUNNING" in source: if "RUNNING" in source:
if self.recorder == "wf-recorder": audio_source = source.split()[1]
args += ["-a", source.split()[1]]
else:
args += ["--audio", "--audio-device", source.split()[1]]
break break
else:
# Fallback to IDLE source if no RUNNING source
if not audio_source:
for source in sources:
if "IDLE" in source:
audio_source = source.split()[1]
break
if not audio_source:
raise ValueError("No audio source found") raise ValueError("No audio source found")
if self.recorder == "wf-recorder":
args += [f"--audio={audio_source}"]
else:
args += ["--audio", "--audio-device", audio_source]
recording_path.parent.mkdir(parents=True, exist_ok=True) recording_path.parent.mkdir(parents=True, exist_ok=True)
proc = subprocess.Popen( proc = subprocess.Popen(
[self.recorder, *args, "-f", recording_path], [self.recorder, *args, "-f", recording_path],
@@ -84,13 +96,16 @@ class Command:
start_new_session=True, start_new_session=True,
) )
# Send notif if proc hasn't ended after a small delay notif = notify("-p", "Recording started", "Recording...")
time.sleep(0.1) recording_notif_path.write_text(notif)
if proc.poll() is None:
notif = notify("-p", "Recording started", "Recording...") for _ in range(5):
recording_notif_path.write_text(notif) if proc.poll() is not None:
else: if proc.returncode != 0:
notify("Recording failed", f"Recording failed to start: {proc.communicate()[1]}") close_notification(notif)
notify("Recording failed", f"Recording error: {proc.communicate()[1]}")
return
time.sleep(0.2)
def stop(self) -> None: def stop(self) -> None:
# Start killing recording process # Start killing recording process
@@ -107,19 +122,7 @@ class Command:
# Close start notification # Close start notification
try: try:
notif = recording_notif_path.read_text() close_notification(recording_notif_path.read_text())
subprocess.run(
[
"gdbus",
"call",
"--session",
"--dest=org.freedesktop.Notifications",
"--object-path=/org/freedesktop/Notifications",
"--method=org.freedesktop.Notifications.CloseNotification",
notif,
],
stdout=subprocess.DEVNULL,
)
except IOError: except IOError:
pass pass
+15
View File
@@ -3,3 +3,18 @@ import subprocess
def notify(*args: list[str]) -> str: def notify(*args: list[str]) -> str:
return subprocess.check_output(["notify-send", "-a", "caelestia-cli", *args], text=True).strip() return subprocess.check_output(["notify-send", "-a", "caelestia-cli", *args], text=True).strip()
def close_notification(id: str) -> None:
subprocess.run(
[
"gdbus",
"call",
"--session",
"--dest=org.freedesktop.Notifications",
"--object-path=/org/freedesktop/Notifications",
"--method=org.freedesktop.Notifications.CloseNotification",
id,
],
stdout=subprocess.DEVNULL,
)