python-voiceio 0.9.0__py3-none-any.whl → 0.9.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_voiceio-0.9.0.dist-info → python_voiceio-0.9.2.dist-info}/METADATA +1 -1
- {python_voiceio-0.9.0.dist-info → python_voiceio-0.9.2.dist-info}/RECORD +10 -10
- voiceio/__init__.py +1 -1
- voiceio/ibus/__init__.py +31 -0
- voiceio/ibus/engine.py +20 -8
- voiceio/service.py +8 -1
- {python_voiceio-0.9.0.dist-info → python_voiceio-0.9.2.dist-info}/WHEEL +0 -0
- {python_voiceio-0.9.0.dist-info → python_voiceio-0.9.2.dist-info}/entry_points.txt +0 -0
- {python_voiceio-0.9.0.dist-info → python_voiceio-0.9.2.dist-info}/licenses/LICENSE +0 -0
- {python_voiceio-0.9.0.dist-info → python_voiceio-0.9.2.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
python_voiceio-0.9.
|
|
2
|
-
voiceio/__init__.py,sha256=
|
|
1
|
+
python_voiceio-0.9.2.dist-info/licenses/LICENSE,sha256=Gz61o8jFTAvZUZyB3nWDB3DQQVuipjfPkVu9W8hBHM0,1072
|
|
2
|
+
voiceio/__init__.py,sha256=gqT-BGoeEItda9fICQDvLbxEjWRIBhFJxPxxKvmHLUo,22
|
|
3
3
|
voiceio/__main__.py,sha256=xT5QCGGreYMisHO7Lh_Y-xAQ2TOzG2D7npKknGNcWSY,53
|
|
4
4
|
voiceio/app.py,sha256=ScrUVDQQ_YbAFKKSMOhaRPX_WTcPuenhnRdSzWMxThE,73785
|
|
5
5
|
voiceio/audit.py,sha256=p4F2BBX4wEC9eS8MSplzfXL52XC2dyIKbgrRvR-WbTU,15802
|
|
@@ -28,7 +28,7 @@ voiceio/postprocess.py,sha256=9grkq3I6XgiQV6ZQbIw2_fOUe57tzC7GBPvLXqhhdTs,2979
|
|
|
28
28
|
voiceio/prompt.py,sha256=9iza8KoQxvO_U9NPMJ8Ep7j-zkIs9nW8N5sacCRpsEk,2904
|
|
29
29
|
voiceio/recorder.py,sha256=p1r0mH_plBD3BHqBbIGV_m2N4Mj5RAF7LfMu8u6mk5I,12813
|
|
30
30
|
voiceio/retention.py,sha256=i_VWPCyykT4kQ1uKilTplpiIrACTYljn09BU1vL71A0,6019
|
|
31
|
-
voiceio/service.py,sha256=
|
|
31
|
+
voiceio/service.py,sha256=1YhnDPHnu0Aw66uwsp05TWfs18bf3io_h6r8eNla7nA,12525
|
|
32
32
|
voiceio/snapshots.py,sha256=bm-lzIeYucDUDlPBZiFiWd-jyB2I6Ft1jRANZCO6go4,2351
|
|
33
33
|
voiceio/streaming.py,sha256=mXkQEEMGMbABSRNG2H7MYObKUdCwfqMvNIZISGZ5uic,25360
|
|
34
34
|
voiceio/tokens.py,sha256=BbOXnsDIfWYMVbSp25PYG1UtK7-7OtZ7q7l4kOIm9OY,5292
|
|
@@ -45,8 +45,8 @@ voiceio/hotkeys/chain.py,sha256=8pvPhipmxWwmGG9DRXy8dlEKTPFILxNzYO-adcvw5Ps,2800
|
|
|
45
45
|
voiceio/hotkeys/evdev.py,sha256=DHmd2IASBkYnyY5JEGFIoYfZtw3T9Ij6wKclkx-6m94,5109
|
|
46
46
|
voiceio/hotkeys/pynput_backend.py,sha256=_rLb8p0JakR9T32LJclHVTch49M_mnB3UeUsI_IkfLk,3576
|
|
47
47
|
voiceio/hotkeys/socket_backend.py,sha256=xLNFa8FvJbL5VLKqMRhKPeaKdhztYwxyVV_VOlG1PkM,3373
|
|
48
|
-
voiceio/ibus/__init__.py,sha256=
|
|
49
|
-
voiceio/ibus/engine.py,sha256=
|
|
48
|
+
voiceio/ibus/__init__.py,sha256=z_f9-svxKgMpNpnATGl7lkMskAMhW2WzJ1MretmUwgs,2534
|
|
49
|
+
voiceio/ibus/engine.py,sha256=HF2rz3Jp51ljPqR3XP2iB2qpwfSKRV7Rbyj11zOiDrs,10015
|
|
50
50
|
voiceio/ibus/pending.py,sha256=T6Y_8oimhabahunp9RogtO-OTA5aw9d9G4T5lH1WAL4,1464
|
|
51
51
|
voiceio/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
52
|
voiceio/models/silero_vad.onnx,sha256=GhU6IvRQnikqlOZ9b5uF6N6yW0mIaCt-F0xlJ52HiOM,2327524
|
|
@@ -74,8 +74,8 @@ voiceio/typers/pynput_type.py,sha256=DTtkT59M-EKOAlTNkvcZrRvf_Fq52OpI8S5t_8S-QAY
|
|
|
74
74
|
voiceio/typers/wtype.py,sha256=d1wG-HZdYDQxXIysV_Xvk9HixDpaJgl3VEoKbP5vhIs,1786
|
|
75
75
|
voiceio/typers/xdotool.py,sha256=dh1zhPqUT8ihJahJ7ZKm6PtfYY087UAzADx664DvQOM,1356
|
|
76
76
|
voiceio/typers/ydotool.py,sha256=dt0W9ot__W8LQG2so8vZLGIw9leK2Q6g3i-Voo0LXE4,5327
|
|
77
|
-
python_voiceio-0.9.
|
|
78
|
-
python_voiceio-0.9.
|
|
79
|
-
python_voiceio-0.9.
|
|
80
|
-
python_voiceio-0.9.
|
|
81
|
-
python_voiceio-0.9.
|
|
77
|
+
python_voiceio-0.9.2.dist-info/METADATA,sha256=RZm3kWz94AUBzUicA4CWYbm_oLyaFrNVfvU0acqEHEY,15877
|
|
78
|
+
python_voiceio-0.9.2.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
79
|
+
python_voiceio-0.9.2.dist-info/entry_points.txt,sha256=U64fA65zxzyLoC8bgbn2ztQVWHLsc0o0H0qCk1J9DMc,218
|
|
80
|
+
python_voiceio-0.9.2.dist-info/top_level.txt,sha256=piwtn309lD6uexQyXdZ-efAVBJF9y6Wfr48Z-8zkNhg,8
|
|
81
|
+
python_voiceio-0.9.2.dist-info/RECORD,,
|
voiceio/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.9.
|
|
1
|
+
__version__ = "0.9.2"
|
voiceio/ibus/__init__.py
CHANGED
|
@@ -6,6 +6,37 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
SOCKET_PATH = Path(os.environ.get("XDG_RUNTIME_DIR", "/tmp")) / "voiceio-ibus.sock"
|
|
8
8
|
READY_PATH = Path(os.environ.get("XDG_RUNTIME_DIR", "/tmp")) / "voiceio-ibus.ready"
|
|
9
|
+
# Held for the engine's lifetime so exactly one process ever owns the command
|
|
10
|
+
# socket — see acquire_singleton_lock().
|
|
11
|
+
LOCK_PATH = Path(os.environ.get("XDG_RUNTIME_DIR", "/tmp")) / "voiceio-ibus.lock"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def acquire_singleton_lock() -> int | None:
|
|
15
|
+
"""Take an exclusive, lifetime-held lock on LOCK_PATH.
|
|
16
|
+
|
|
17
|
+
Returns the open fd on success (the caller must keep it open — the lock is
|
|
18
|
+
released when the fd closes, i.e. when the process dies), or None if another
|
|
19
|
+
engine already holds it.
|
|
20
|
+
|
|
21
|
+
This is the race-free gate against duplicate engines. ibus-daemon
|
|
22
|
+
exec-spawns its own copy of the engine when something activates the voiceio
|
|
23
|
+
source (GNOME's per-window source memory / Super+Space — voiceio sits first
|
|
24
|
+
in mru-sources). A ping-then-bind check is check-then-act: two engines
|
|
25
|
+
starting close together both see "no healthy peer", then both unlink+rebind
|
|
26
|
+
the DGRAM socket in _socket_listener. The loser is left bound to an orphaned
|
|
27
|
+
inode — alive but never receiving pings ("zombie") — which the daemon's
|
|
28
|
+
health watchdog then kills and respawns, churning ~15x/day. flock is atomic:
|
|
29
|
+
the loser never touches the socket, it just exits.
|
|
30
|
+
"""
|
|
31
|
+
import fcntl
|
|
32
|
+
|
|
33
|
+
fd = os.open(str(LOCK_PATH), os.O_CREAT | os.O_RDWR, 0o600)
|
|
34
|
+
try:
|
|
35
|
+
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
36
|
+
except OSError:
|
|
37
|
+
os.close(fd)
|
|
38
|
+
return None
|
|
39
|
+
return fd
|
|
9
40
|
|
|
10
41
|
|
|
11
42
|
def ping_engine(timeout: float = 2.0) -> bool:
|
voiceio/ibus/engine.py
CHANGED
|
@@ -24,10 +24,18 @@ import gi
|
|
|
24
24
|
gi.require_version("IBus", "1.0")
|
|
25
25
|
from gi.repository import GLib, GObject, IBus
|
|
26
26
|
|
|
27
|
-
from voiceio.ibus import
|
|
27
|
+
from voiceio.ibus import (
|
|
28
|
+
READY_PATH,
|
|
29
|
+
SOCKET_PATH,
|
|
30
|
+
acquire_singleton_lock,
|
|
31
|
+
)
|
|
28
32
|
from voiceio.ibus.pending import PendingBuffer
|
|
29
33
|
|
|
30
34
|
log = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
# Singleton lock fd, held for the process lifetime (see main()). Module-level so
|
|
37
|
+
# it is never garbage-collected — closing the fd would release the lock.
|
|
38
|
+
_lock_fd: int | None = None
|
|
31
39
|
ENGINE_NAME = "voiceio"
|
|
32
40
|
COMPONENT_NAME = "org.voiceio.ibus"
|
|
33
41
|
|
|
@@ -230,13 +238,17 @@ def main() -> None:
|
|
|
230
238
|
],
|
|
231
239
|
)
|
|
232
240
|
|
|
233
|
-
# Refuse to double-run. ibus-daemon
|
|
234
|
-
# engine (component XML) when something activates the voiceio source
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
|
|
239
|
-
|
|
241
|
+
# Refuse to double-run, race-free. ibus-daemon exec-spawns its own copy of
|
|
242
|
+
# this engine (component XML) when something activates the voiceio source
|
|
243
|
+
# (GNOME per-window source memory / Super+Space — voiceio is first in
|
|
244
|
+
# mru-sources). Without an atomic gate, two engines both pass a ping check
|
|
245
|
+
# then both unlink+rebind SOCKET_PATH in _socket_listener; the loser is
|
|
246
|
+
# left on an orphaned inode — a "zombie" the watchdog kills and respawns.
|
|
247
|
+
# The lock fd is held for our whole lifetime (released on process death).
|
|
248
|
+
global _lock_fd
|
|
249
|
+
_lock_fd = acquire_singleton_lock()
|
|
250
|
+
if _lock_fd is None:
|
|
251
|
+
log.info("Another VoiceIO engine holds the lock — exiting duplicate")
|
|
240
252
|
return
|
|
241
253
|
|
|
242
254
|
IBus.init()
|
voiceio/service.py
CHANGED
|
@@ -68,7 +68,7 @@ Environment=PYTHONUNBUFFERED=1
|
|
|
68
68
|
PassEnvironment=DISPLAY WAYLAND_DISPLAY XDG_SESSION_TYPE XDG_RUNTIME_DIR
|
|
69
69
|
|
|
70
70
|
[Install]
|
|
71
|
-
WantedBy=
|
|
71
|
+
WantedBy=graphical-session.target
|
|
72
72
|
"""
|
|
73
73
|
|
|
74
74
|
|
|
@@ -190,6 +190,13 @@ def install_service() -> bool:
|
|
|
190
190
|
["systemctl", "--user", "daemon-reload"],
|
|
191
191
|
capture_output=True, timeout=5,
|
|
192
192
|
)
|
|
193
|
+
# disable before enable so a prior [Install] target (older versions
|
|
194
|
+
# wired WantedBy=default.target, which never restarts the service on a
|
|
195
|
+
# GNOME logout/login) is unlinked instead of left as a stale want.
|
|
196
|
+
subprocess.run(
|
|
197
|
+
["systemctl", "--user", "disable", SERVICE_NAME],
|
|
198
|
+
capture_output=True, timeout=5,
|
|
199
|
+
)
|
|
193
200
|
subprocess.run(
|
|
194
201
|
["systemctl", "--user", "enable", SERVICE_NAME],
|
|
195
202
|
capture_output=True, timeout=5,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|