Simplify daemon tray

This commit is contained in:
Thales Maciel 2026-02-24 11:31:26 -03:00
parent 4e8edc3e40
commit 3f15a0e686
No known key found for this signature in database
GPG key ID: 33112E6833C34679

View file

@ -31,6 +31,12 @@ class State:
OUTPUTTING = "outputting" OUTPUTTING = "outputting"
ASSETS_DIR = Path(__file__).parent / "assets"
RECORD_TIMEOUT_SEC = 300
STT_LANGUAGE = "en"
TRAY_UPDATE_MS = 250
def _compute_type(device: str) -> str: def _compute_type(device: str) -> str:
dev = (device or "cpu").lower() dev = (device or "cpu").lower()
if dev == "cuda": if dev == "cuda":
@ -51,7 +57,14 @@ class Daemon:
device=cfg.stt.get("device", "cpu"), device=cfg.stt.get("device", "cpu"),
compute_type=_compute_type(cfg.stt.get("device", "cpu")), compute_type=_compute_type(cfg.stt.get("device", "cpu")),
) )
self.tray = _Tray(self.get_state, self._quit) self.icon = Gtk.StatusIcon()
self.icon.set_visible(True)
self.icon.connect("popup-menu", self._on_tray_menu)
self.menu = Gtk.Menu()
quit_item = Gtk.MenuItem(label="Quit")
quit_item.connect("activate", lambda *_: self._quit())
self.menu.append(quit_item)
self.menu.show_all()
def set_state(self, state: str): def set_state(self, state: str):
with self.lock: with self.lock:
@ -67,6 +80,9 @@ class Daemon:
def _quit(self): def _quit(self):
os._exit(0) os._exit(0)
def _on_tray_menu(self, _icon, _button, _time):
self.menu.popup(None, None, None, None, 0, _time)
def toggle(self): def toggle(self):
with self.lock: with self.lock:
if self.state == State.IDLE: if self.state == State.IDLE:
@ -90,7 +106,7 @@ class Daemon:
logging.info("recording started (%s)", record.wav_path) logging.info("recording started (%s)", record.wav_path)
if self.timer: if self.timer:
self.timer.cancel() self.timer.cancel()
self.timer = threading.Timer(300, self._timeout_stop) self.timer = threading.Timer(RECORD_TIMEOUT_SEC, self._timeout_stop)
self.timer.daemon = True self.timer.daemon = True
self.timer.start() self.timer.start()
@ -184,11 +200,7 @@ class Daemon:
threading.Thread(target=self._stop_and_process, daemon=True).start() threading.Thread(target=self._stop_and_process, daemon=True).start()
def _transcribe(self, wav_path: str) -> str: def _transcribe(self, wav_path: str) -> str:
segments, _info = self.model.transcribe( segments, _info = self.model.transcribe(wav_path, language=STT_LANGUAGE, vad_filter=True)
wav_path,
language=None,
vad_filter=True,
)
parts = [] parts = []
for seg in segments: for seg in segments:
text = (seg.text or "").strip() text = (seg.text or "").strip()
@ -196,35 +208,14 @@ class Daemon:
parts.append(text) parts.append(text)
return " ".join(parts).strip() return " ".join(parts).strip()
def run_tray(self):
self.tray.run()
class _Tray:
def __init__(self, state_getter, on_quit):
self.state_getter = state_getter
self.on_quit = on_quit
self.base = Path(__file__).parent / "assets"
self.icon = Gtk.StatusIcon()
self.icon.set_visible(True)
self.icon.connect("popup-menu", self._on_menu)
self.menu = Gtk.Menu()
quit_item = Gtk.MenuItem(label="Quit")
quit_item.connect("activate", lambda *_: self.on_quit())
self.menu.append(quit_item)
self.menu.show_all()
def _on_menu(self, _icon, _button, _time):
self.menu.popup(None, None, None, None, 0, _time)
def _icon_path(self, state: str) -> str: def _icon_path(self, state: str) -> str:
if state == State.RECORDING: if state == State.RECORDING:
return str(self.base / "recording.png") return str(ASSETS_DIR / "recording.png")
if state == State.STT: if state == State.STT:
return str(self.base / "transcribing.png") return str(ASSETS_DIR / "transcribing.png")
if state == State.PROCESSING: if state == State.PROCESSING:
return str(self.base / "processing.png") return str(ASSETS_DIR / "processing.png")
return str(self.base / "idle.png") return str(ASSETS_DIR / "idle.png")
def _title(self, state: str) -> str: def _title(self, state: str) -> str:
if state == State.RECORDING: if state == State.RECORDING:
@ -235,15 +226,15 @@ class _Tray:
return "AI Processing" return "AI Processing"
return "Idle" return "Idle"
def update(self): def _update_tray(self):
state = self.state_getter() state = self.get_state()
self.icon.set_from_file(self._icon_path(state)) self.icon.set_from_file(self._icon_path(state))
self.icon.set_tooltip_text(self._title(state)) self.icon.set_tooltip_text(self._title(state))
return True return True
def run(self): def run_tray(self):
self.update() self._update_tray()
GLib.timeout_add(250, self.update) GLib.timeout_add(TRAY_UPDATE_MS, self._update_tray)
Gtk.main() Gtk.main()
@ -265,7 +256,6 @@ def _lock_single_instance():
def main(): def main():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--config", default="", help="path to config.json") parser.add_argument("--config", default="", help="path to config.json")
parser.add_argument("--no-tray", action="store_true", help="disable tray icon")
parser.add_argument("--dry-run", action="store_true", help="log hotkey only") parser.add_argument("--dry-run", action="store_true", help="log hotkey only")
args = parser.parse_args() args = parser.parse_args()
@ -289,13 +279,6 @@ def main():
signal.signal(signal.SIGINT, handle_signal) signal.signal(signal.SIGINT, handle_signal)
signal.signal(signal.SIGTERM, handle_signal) signal.signal(signal.SIGTERM, handle_signal)
if args.no_tray:
listen(
cfg.daemon.get("hotkey", ""),
lambda: logging.info("hotkey pressed (dry-run)") if args.dry_run else daemon.toggle(),
)
return
threading.Thread( threading.Thread(
target=lambda: listen( target=lambda: listen(
cfg.daemon.get("hotkey", ""), cfg.daemon.get("hotkey", ""),