Compare commits
2 Commits
6172cc0601
...
4a9a1ec799
| Author | SHA1 | Date |
|---|---|---|
|
|
4a9a1ec799 | |
|
|
79f5bfabe5 |
|
|
@ -71,7 +71,31 @@
|
|||
"Bash(.venv-linux/bin/pyinstaller whisper-dictation.spec --clean)",
|
||||
"Bash(.venv-linux/bin/pyinstaller whisper-dictation.spec --clean -y)",
|
||||
"Bash(pactl --version)",
|
||||
"Bash(pactl list:*)"
|
||||
"Bash(pactl list:*)",
|
||||
"Bash(grep -r \"input\\\\|permission\\\\|portal\\\\|access\" /mnt/ventoy/projects/chrka/whisper-dictation/whisper_app/*.py)",
|
||||
"Read(//home/chk/.config/**)",
|
||||
"Bash(ls ~/.config/xdg-desktop-portal*)",
|
||||
"Read(//home/chk/.local/share/flatpak/**)",
|
||||
"Read(//home/chk/.local/share/**)",
|
||||
"Bash(find ~/.config -name *permission* -o -name *remote* -o -name *fakeinput* -o -name *access*control*)",
|
||||
"Bash(qdbus org.freedesktop.impl.portal.desktop.kde /org/freedesktop/impl/portal/desktop/kde)",
|
||||
"Bash(find ~/.local/share -name *permission*)",
|
||||
"Bash(kwriteconfig6 --help)",
|
||||
"Bash(plasma-browser-integration-host --version)",
|
||||
"Read(//usr/share/xdg-desktop-portal/portals/**)",
|
||||
"Bash(grep -E \"\\(Makefile|setup|build|spec|\\\\.sh$|\\\\.py$\\)\")",
|
||||
"Bash(systemctl --user status ydotool)",
|
||||
"Bash(ydotool key:*)",
|
||||
"Bash(echo \"exit: $?\")",
|
||||
"Bash(wl-copy)",
|
||||
"Bash(wl-paste --no-newline)",
|
||||
"Bash(ydotool type:*)",
|
||||
"Bash(wl-copy -- \"XDOTOOL_TEST\")",
|
||||
"Bash(xdotool key:*)",
|
||||
"Bash(find /mnt/ventoy/projects/chrka/whisper-dictation/shared_data/ -name *.bin -o -name *.pt -o -name model*)",
|
||||
"Bash(python3 -c \":*)",
|
||||
"Read(//usr/bin/**)",
|
||||
"Bash(/run/media/chk/Ventoy/projects/chrka/whisper-dictation/.venv-linux/bin/python build.py)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@
|
|||
"hotkey": "ctrl+shift+space",
|
||||
"language": "de",
|
||||
"sample_rate": 16000,
|
||||
"vocab_path": "/run/media/chk/Ventoy/projects/chrka/whisper-dictation/shared_data/vocabulary.json",
|
||||
"model_dir": "/run/media/chk/Ventoy/projects/chrka/whisper-dictation/shared_data/"
|
||||
"vocab_path": "shared_data/vocabulary.json",
|
||||
"model_dir": "shared_data/"
|
||||
}
|
||||
|
|
@ -66,6 +66,10 @@
|
|||
{
|
||||
"from": "SHP",
|
||||
"to": "SAP"
|
||||
},
|
||||
{
|
||||
"from": "GRA App",
|
||||
"to": "KRAH-App"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -6,7 +6,12 @@ import sys
|
|||
def _app_dir() -> str:
|
||||
"""Root dir for config.json and vocabulary.json."""
|
||||
if getattr(sys, "frozen", False):
|
||||
return os.path.dirname(sys.executable)
|
||||
exe_dir = os.path.dirname(sys.executable)
|
||||
# Built exe lives at dist/<platform>/; project root is two levels up.
|
||||
project_root = os.path.normpath(os.path.join(exe_dir, "..", ".."))
|
||||
if os.path.isfile(os.path.join(project_root, "config.json")):
|
||||
return project_root
|
||||
return exe_dir
|
||||
# config.py lives at whisper_app/config.py → parent of parent = repo root
|
||||
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
|
@ -52,14 +57,19 @@ config: dict = {}
|
|||
vocab: dict = {"words": [], "replacements": []}
|
||||
|
||||
|
||||
def _resolve_path(value: str, fallback_name: str = "") -> str:
|
||||
"""Resolve a config path: absolute stays absolute, relative is joined with DATA_DIR."""
|
||||
if value:
|
||||
return value if os.path.isabs(value) else os.path.join(DATA_DIR, value)
|
||||
if fallback_name:
|
||||
return os.path.join(DATA_DIR, fallback_name)
|
||||
return ""
|
||||
|
||||
|
||||
def _resolve_vocab_file() -> None:
|
||||
"""Set VOCAB_FILE from config['vocab_path'], falling back to DATA_DIR."""
|
||||
global VOCAB_FILE
|
||||
vp = config.get("vocab_path", "")
|
||||
if vp:
|
||||
VOCAB_FILE = vp if os.path.isabs(vp) else os.path.join(DATA_DIR, vp)
|
||||
else:
|
||||
VOCAB_FILE = os.path.join(DATA_DIR, "vocabulary.json")
|
||||
VOCAB_FILE = _resolve_path(config.get("vocab_path", ""), "vocabulary.json")
|
||||
|
||||
|
||||
def load_config() -> None:
|
||||
|
|
|
|||
|
|
@ -7,17 +7,20 @@ from whisper_app import app, config, grammar, media_duck, typer
|
|||
|
||||
|
||||
def load_model() -> None:
|
||||
app.log(f"Loading {config.config['model']} on {config.config['device']}...")
|
||||
model_dir = config.config.get("model_dir") or None
|
||||
app.model = WhisperModel(
|
||||
config.config["model"],
|
||||
device=config.config["device"],
|
||||
compute_type=config.config["compute_type"],
|
||||
download_root=model_dir,
|
||||
)
|
||||
app.log("Model ready.")
|
||||
if config.config.get("grammar_check"):
|
||||
grammar.init(config.config.get("language") or "de", log=app.log)
|
||||
try:
|
||||
app.log(f"Loading {config.config['model']} on {config.config['device']}...")
|
||||
model_dir = config._resolve_path(config.config.get("model_dir", "")) or None
|
||||
app.model = WhisperModel(
|
||||
config.config["model"],
|
||||
device=config.config["device"],
|
||||
compute_type=config.config["compute_type"],
|
||||
download_root=model_dir,
|
||||
)
|
||||
app.log("Model ready.")
|
||||
if config.config.get("grammar_check"):
|
||||
grammar.init(config.config.get("language") or "de", log=app.log)
|
||||
except Exception as e:
|
||||
app.log(f"Model load FAILED: {e}")
|
||||
|
||||
|
||||
def stop_and_transcribe() -> None:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import shutil
|
|||
import subprocess
|
||||
import time
|
||||
|
||||
from whisper_app import config
|
||||
from whisper_app import app, config
|
||||
|
||||
|
||||
def _pynput_type(text):
|
||||
|
|
@ -36,6 +36,31 @@ def _wl_copy_bytes(data):
|
|||
pass
|
||||
|
||||
|
||||
def _paste_via_ydotool():
|
||||
"""Simulate Ctrl+V using ydotool (kernel-level, works on all compositors)."""
|
||||
subprocess.run(["ydotool", "key", "29:1", "47:1", "47:0", "29:0"], check=False)
|
||||
|
||||
|
||||
def _paste_via_wtype():
|
||||
"""Simulate Ctrl+V using wtype (native Wayland, wlroots only)."""
|
||||
subprocess.run(["wtype", "-M", "ctrl", "-P", "v", "-p", "v", "-m", "ctrl"],
|
||||
check=False)
|
||||
|
||||
|
||||
def _paste_via_xdotool():
|
||||
"""Simulate Ctrl+V using xdotool (XWayland fallback)."""
|
||||
subprocess.run(["xdotool", "key", "ctrl+v"], check=False)
|
||||
|
||||
|
||||
def _pick_paste_cmd():
|
||||
"""Select best available paste tool for the current Wayland session."""
|
||||
if shutil.which("ydotool"):
|
||||
return _paste_via_ydotool
|
||||
if shutil.which("wtype"):
|
||||
return _paste_via_wtype
|
||||
return _paste_via_xdotool
|
||||
|
||||
|
||||
def type_text(text):
|
||||
"""Type text into the active window, cross-platform."""
|
||||
if os.name == "nt":
|
||||
|
|
@ -47,7 +72,9 @@ def type_text(text):
|
|||
old_clipboard = _wl_paste()
|
||||
subprocess.run(["wl-copy", "--", text], check=False)
|
||||
time.sleep(0.05)
|
||||
subprocess.run(["xdotool", "key", "ctrl+v"], check=False)
|
||||
paste_fn = _pick_paste_cmd()
|
||||
app.log(f"typer: using {paste_fn.__name__}, session={session}")
|
||||
paste_fn()
|
||||
time.sleep(delay)
|
||||
if old_clipboard is not None:
|
||||
_wl_copy_bytes(old_clipboard)
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ def _open_main(root: tk.Tk) -> None:
|
|||
win.title("Vokabular")
|
||||
win.configure(bg=BG)
|
||||
win.attributes("-topmost", True)
|
||||
win.resizable(False, False)
|
||||
win.minsize(600, 0)
|
||||
win.resizable(True, True)
|
||||
win.minsize(600, 480)
|
||||
win.option_add("*Menu.background", BG3)
|
||||
win.option_add("*Menu.foreground", FG)
|
||||
win.option_add("*Menu.activeBackground", AMBER)
|
||||
|
|
@ -192,6 +192,6 @@ def _open_main(root: tk.Tk) -> None:
|
|||
win.update_idletasks()
|
||||
sw = win.winfo_screenwidth()
|
||||
sh = win.winfo_screenheight()
|
||||
w = win.winfo_reqwidth()
|
||||
h = win.winfo_reqheight()
|
||||
win.geometry(f"+{(sw-w)//2}+{(sh-h)//2}")
|
||||
w = max(win.winfo_reqwidth(), 600)
|
||||
h = min(max(win.winfo_reqheight(), 480), sh - 100)
|
||||
win.geometry(f"{w}x{h}+{(sw-w)//2}+{(sh-h)//2}")
|
||||
|
|
|
|||
Loading…
Reference in New Issue