Remove context capture
This commit is contained in:
parent
f826617ed4
commit
3b7fa236b4
5 changed files with 1 additions and 99 deletions
|
|
@ -9,7 +9,7 @@ Python X11 transcription daemon that records audio, runs Whisper, logs the trans
|
||||||
- `soundfile` (libsndfile)
|
- `soundfile` (libsndfile)
|
||||||
- `faster-whisper`
|
- `faster-whisper`
|
||||||
- Tray icon deps: `gtk3`
|
- Tray icon deps: `gtk3`
|
||||||
- Python deps: `pillow`, `python-xlib`, `faster-whisper`, `PyGObject`, `i3ipc`, `sounddevice`, `soundfile`
|
- Python deps: `pillow`, `python-xlib`, `faster-whisper`, `PyGObject`, `sounddevice`, `soundfile`
|
||||||
|
|
||||||
System packages (example names): `portaudio`/`libportaudio2` and `libsndfile`.
|
System packages (example names): `portaudio`/`libportaudio2` and `libsndfile`.
|
||||||
|
|
||||||
|
|
@ -84,11 +84,6 @@ AI provider:
|
||||||
|
|
||||||
- Generic OpenAI-compatible chat API at `ai_base_url` (base URL only; the app uses `/v1/chat/completions`)
|
- Generic OpenAI-compatible chat API at `ai_base_url` (base URL only; the app uses `/v1/chat/completions`)
|
||||||
|
|
||||||
Context capture (i3 only):
|
|
||||||
|
|
||||||
- The focused window at hotkey time is stored via i3 IPC.
|
|
||||||
- If focus changes before injection, the workflow aborts (interpreted as a cancel).
|
|
||||||
|
|
||||||
Control:
|
Control:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ dependencies = [
|
||||||
"pillow",
|
"pillow",
|
||||||
"python-xlib",
|
"python-xlib",
|
||||||
"PyGObject",
|
"PyGObject",
|
||||||
"i3ipc",
|
|
||||||
"sounddevice",
|
"sounddevice",
|
||||||
"soundfile",
|
"soundfile",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
import i3ipc
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Context:
|
|
||||||
window_id: int
|
|
||||||
app_id: str
|
|
||||||
klass: str
|
|
||||||
instance: str
|
|
||||||
title: str
|
|
||||||
|
|
||||||
|
|
||||||
class I3Provider:
|
|
||||||
def __init__(self):
|
|
||||||
self._conn = i3ipc.Connection()
|
|
||||||
|
|
||||||
def _focused(self):
|
|
||||||
return self._conn.get_tree().find_focused()
|
|
||||||
|
|
||||||
def capture(self) -> Context:
|
|
||||||
node = self._focused()
|
|
||||||
return Context(
|
|
||||||
window_id=node.id,
|
|
||||||
app_id=node.app_id or "",
|
|
||||||
klass=node.window_class or "",
|
|
||||||
instance=node.window_instance or "",
|
|
||||||
title=node.name or "",
|
|
||||||
)
|
|
||||||
|
|
||||||
def is_same_focus(self, ctx: Context) -> bool:
|
|
||||||
node = self._focused()
|
|
||||||
return bool(node and node.id == ctx.window_id)
|
|
||||||
|
|
||||||
def focus_window(self, window_id: int) -> bool:
|
|
||||||
try:
|
|
||||||
self._conn.command(f"[con_id={window_id}] focus")
|
|
||||||
return True
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
35
src/leld.py
35
src/leld.py
|
|
@ -13,7 +13,6 @@ from config import Config, load, redacted_dict
|
||||||
from recorder import start_recording, stop_recording
|
from recorder import start_recording, stop_recording
|
||||||
from stt import FasterWhisperSTT, STTConfig
|
from stt import FasterWhisperSTT, STTConfig
|
||||||
from aiprocess import AIConfig, build_processor
|
from aiprocess import AIConfig, build_processor
|
||||||
from context import I3Provider
|
|
||||||
from inject import inject
|
from inject import inject
|
||||||
from x11_hotkey import listen
|
from x11_hotkey import listen
|
||||||
from tray import run_tray
|
from tray import run_tray
|
||||||
|
|
@ -35,8 +34,6 @@ class Daemon:
|
||||||
self.proc = None
|
self.proc = None
|
||||||
self.record = None
|
self.record = None
|
||||||
self.timer = None
|
self.timer = None
|
||||||
self.context = None
|
|
||||||
self.context_provider = I3Provider()
|
|
||||||
self.stt = FasterWhisperSTT(
|
self.stt = FasterWhisperSTT(
|
||||||
STTConfig(
|
STTConfig(
|
||||||
model=cfg.transcribing.get("model", "base"),
|
model=cfg.transcribing.get("model", "base"),
|
||||||
|
|
@ -75,23 +72,6 @@ class Daemon:
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.error("record start failed: %s", exc)
|
logging.error("record start failed: %s", exc)
|
||||||
return
|
return
|
||||||
try:
|
|
||||||
if self.context_provider:
|
|
||||||
self.context = self.context_provider.capture()
|
|
||||||
except Exception as exc:
|
|
||||||
logging.error("context capture failed: %s", exc)
|
|
||||||
self.context = None
|
|
||||||
if self.context:
|
|
||||||
logging.info(
|
|
||||||
"context: id=%s app_id=%s class=%s instance=%s title=%s",
|
|
||||||
self.context.window_id,
|
|
||||||
self.context.app_id,
|
|
||||||
self.context.klass,
|
|
||||||
self.context.instance,
|
|
||||||
self.context.title,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logging.info("context: none")
|
|
||||||
self.proc = proc
|
self.proc = proc
|
||||||
self.record = record
|
self.record = record
|
||||||
self.state = State.RECORDING
|
self.state = State.RECORDING
|
||||||
|
|
@ -179,11 +159,6 @@ class Daemon:
|
||||||
try:
|
try:
|
||||||
self.set_state(State.OUTPUTTING)
|
self.set_state(State.OUTPUTTING)
|
||||||
logging.info("outputting started")
|
logging.info("outputting started")
|
||||||
if self.context_provider and self.context:
|
|
||||||
if not self.context_provider.is_same_focus(self.context):
|
|
||||||
logging.info("focus changed, aborting injection")
|
|
||||||
self.set_state(State.IDLE)
|
|
||||||
return
|
|
||||||
backend = self.cfg.injection.get("backend", "clipboard")
|
backend = self.cfg.injection.get("backend", "clipboard")
|
||||||
inject(text, backend)
|
inject(text, backend)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|
@ -191,16 +166,6 @@ class Daemon:
|
||||||
finally:
|
finally:
|
||||||
self.set_state(State.IDLE)
|
self.set_state(State.IDLE)
|
||||||
|
|
||||||
def _context_json(self, ctx):
|
|
||||||
if not ctx:
|
|
||||||
return None
|
|
||||||
return {
|
|
||||||
"window_id": ctx.window_id,
|
|
||||||
"app_id": ctx.app_id,
|
|
||||||
"class": ctx.klass,
|
|
||||||
"instance": ctx.instance,
|
|
||||||
"title": ctx.title,
|
|
||||||
}
|
|
||||||
|
|
||||||
def stop_recording(self):
|
def stop_recording(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
|
|
||||||
14
uv.lock
generated
14
uv.lock
generated
|
|
@ -373,18 +373,6 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/d5/ae/2f6d96b4e6c5478d87d606a1934b5d436c4a2bce6bb7c6fdece891c128e3/huggingface_hub-1.4.1-py3-none-any.whl", hash = "sha256:9931d075fb7a79af5abc487106414ec5fba2c0ae86104c0c62fd6cae38873d18", size = 553326, upload-time = "2026-02-06T09:20:00.728Z" },
|
{ url = "https://files.pythonhosted.org/packages/d5/ae/2f6d96b4e6c5478d87d606a1934b5d436c4a2bce6bb7c6fdece891c128e3/huggingface_hub-1.4.1-py3-none-any.whl", hash = "sha256:9931d075fb7a79af5abc487106414ec5fba2c0ae86104c0c62fd6cae38873d18", size = 553326, upload-time = "2026-02-06T09:20:00.728Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "i3ipc"
|
|
||||||
version = "2.2.1"
|
|
||||||
source = { registry = "https://pypi.org/simple" }
|
|
||||||
dependencies = [
|
|
||||||
{ name = "python-xlib" },
|
|
||||||
]
|
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/61/f3/dfab70c888d85d3e933ff4d6b351aaed0ae137a29c896e03e364de3bec94/i3ipc-2.2.1.tar.gz", hash = "sha256:e880d7d7147959ead5cb34764f08b97b41385b36eb8256e8af1ce163dbcccce8", size = 47760, upload-time = "2020-04-05T17:25:08.666Z" }
|
|
||||||
wheels = [
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/de/30/294b07ddeccb58855c890c3ef3a951c3b0c1e2d089666d548b6a9edc39fb/i3ipc-2.2.1-py3-none-any.whl", hash = "sha256:c0b898223d50d42c90c818deb5033d1304c582755547dee7d15df3e3781bc690", size = 26591, upload-time = "2020-04-05T17:25:07.338Z" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.11"
|
version = "3.11"
|
||||||
|
|
@ -400,7 +388,6 @@ version = "0.0.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "faster-whisper" },
|
{ name = "faster-whisper" },
|
||||||
{ name = "i3ipc" },
|
|
||||||
{ name = "pillow" },
|
{ name = "pillow" },
|
||||||
{ name = "pygobject" },
|
{ name = "pygobject" },
|
||||||
{ name = "python-xlib" },
|
{ name = "python-xlib" },
|
||||||
|
|
@ -411,7 +398,6 @@ dependencies = [
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "faster-whisper" },
|
{ name = "faster-whisper" },
|
||||||
{ name = "i3ipc" },
|
|
||||||
{ name = "pillow" },
|
{ name = "pillow" },
|
||||||
{ name = "pygobject" },
|
{ name = "pygobject" },
|
||||||
{ name = "python-xlib" },
|
{ name = "python-xlib" },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue