Repository navigation
Expand file tree
/
Copy pathstt.py
More file actions
126 lines (106 loc) · 4.45 KB
/
Copy pathstt.py
File metadata and controls
126 lines (106 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import argparse
import json
import os
import re
import sys
from pathlib import Path
from smart import restore
RUNTIME = Path(os.environ.get("STT_RUNTIME_DIR") or
((Path(os.environ.get("LOCALAPPDATA", Path.home())) / "STTCore" / "runtime")
if getattr(sys, "frozen", False) else Path(__file__).resolve().parent / "runtime"))
TERMS_FILE = RUNTIME.parent / "personal_terms.json"
SHORTCUTS_FILE = RUNTIME.parent / "shortcuts.json"
HISTORY_DIR = RUNTIME.parent / "history"
SHORTCUT_START = re.compile(r"\binsert\s+my\s+", re.IGNORECASE)
def replace_shortcuts(text, protect=None, on_error=None):
if not SHORTCUT_START.search(text):
return text
try:
terms = json.loads(TERMS_FILE.read_text(encoding="utf-8"))
except FileNotFoundError:
terms = {}
except (OSError, UnicodeError, json.JSONDecodeError) as exc:
raise ValueError(f"Cannot read {TERMS_FILE.name}: {exc}") from exc
if not isinstance(terms, dict):
raise ValueError(f"{TERMS_FILE.name} must contain a JSON object.")
shortcuts = {}
for key, value in terms.items():
normalized = " ".join(key.lower().split())
if not normalized or normalized in shortcuts:
raise ValueError(f"{TERMS_FILE.name} contains a blank or duplicate key: {key!r}")
shortcuts[normalized] = value
if not shortcuts:
return text
names = sorted(shortcuts, key=len, reverse=True)
choices = "|".join(re.escape(name).replace(r"\ ", r"\s+") for name in names)
# shortcut: A known key may match the start of an unknown longer phrase; require a spoken delimiter if that becomes a problem.
shortcut = re.compile(r"\binsert\s+my\s+(" + choices + r")(?!\w)", re.IGNORECASE)
missing = set()
def replace(match):
key = " ".join(match.group(1).lower().split())
value = shortcuts[key]
if not isinstance(value, str) or not value.strip():
missing.add(key)
return protect(match.group()) if protect else match.group()
return protect(value) if protect else value
result = shortcut.sub(replace, text)
for key in sorted(missing):
message = f"No saved value for {key} in {TERMS_FILE.name}."
if on_error:
on_error(message)
else:
print(message, file=sys.stderr)
return result
def format_transcript(text, cleaner=None, on_error=None):
if cleaner is None or not text.strip():
return replace_shortcuts(text, on_error=on_error)
values = {}
def protect(value):
index = len(values)
marker = f"ZXQ{index}QXZ"
while marker in text or marker in values:
index += 1
marker = f"ZXQ{index}QXZ"
values[marker] = value
return marker
protected = replace_shortcuts(text, protect, on_error)
try:
return cleaner.clean(protected, values)
except (OSError, ValueError, KeyError, IndexError, TypeError, RecursionError):
if on_error:
on_error("Smart cleanup failed; using original transcript.")
else:
print("Smart cleanup failed; using original transcript.", file=sys.stderr)
return restore(protected, values)
def main(argv=None):
parser = argparse.ArgumentParser()
parser.add_argument("--show-raw", action="store_true", help="Show the raw transcript before cleanup; do not save it.")
args = parser.parse_args(argv)
enabled = input("Smart cleanup? [y/N]: ").strip().lower() == "y"
from client import EngineClient
client = EngineClient(start=True)
try:
try:
client.command("settings", mode="hold", smart=enabled)
except RuntimeError as exc:
if not enabled:
raise
print(f"Smart cleanup unavailable: {exc}", file=sys.stderr)
client.command("settings", mode="hold", smart=False)
client.command("subscribe")
input("Press Enter to record...")
client.command("start")
input("Recording. Press Enter to stop...")
client.command("release")
for event in client.events():
if event["event"] == "result":
if args.show_raw:
print(f"Raw transcript: {event['raw']}", file=sys.stderr, flush=True)
print(event["text"], flush=True)
break
if event["event"] == "error":
print(event["message"], file=sys.stderr)
finally:
client.close()
if __name__ == "__main__":
main()