-
Notifications
You must be signed in to change notification settings - Fork 0
/
WhisperController.py
216 lines (183 loc) · 9.1 KB
/
WhisperController.py
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
from pynput import mouse, keyboard
import threading
import pyperclip
import logging
from WhisperRecorder import WhisperRecorder
from WhisperUI import WhisperUI
import time
import sys
import json
class WhisperController:
def __init__(self, whisper_ui):
# Initialize UI with a reference to this controller and a callback for closing
self.whisper_ui = whisper_ui
self.transcribed_text = "..."
self.transcription_lock = threading.Lock()
self.keyboard_controller = keyboard.Controller()
# Initialize Recorder/Transcription
self.recorder = WhisperRecorder(self.whisper_ui, self.keyboard_controller)
# Start the mouse and keyboard listeners
self.mouse_listener = mouse.Listener(on_click=self.on_click)
self.mouse_listener.start()
# Keyboard listeners for closing
self.keyboard_listener = keyboard.Listener(on_press=self.on_press)
self.keyboard_listener.start()
# Config key detection
self.shortcut_detection_mode = None
self.is_listening_for_shortcut = False
# Initialize default values for shortcuts
self.record_shortcut = {"type": "mouse", "key": "Button.x2"}
self.paste_shortcut = {"type": "keyboard", "key": "v"}
# Load the configuration
self.load_configuration()
def start_recording(self):
if not self.recorder.is_recording:
# Only start recording if it's not already happening
with self.transcription_lock:
try:
# Clear the previous transcribed text
self.transcribed_text = ""
# Start recording
self.transcribed_text = self.recorder.toggle_recording()
print("Recording started...")
except Exception as e:
logging.error("Error starting recording: ", exc_info=e)
def stop_recording_and_transcribe(self):
if self.recorder.is_recording:
# Only stop recording if it's currently active
with self.transcription_lock:
try:
# This will now also fetch the transcription
self.transcribed_text = self.recorder.toggle_recording()
logging.info("Transcribed text: %s", self.transcribed_text)
print("Recording stopped.")
except Exception as e:
logging.error("Error stopping recording: ", exc_info=e)
def perform_macro(self):
# Retrieve text from the transcription box
text = self.whisper_ui.transcription_box.get("1.0", "end-1c").strip()
if self.transcription_lock.acquire(blocking=False):
try:
if not self.whisper_ui.chat_mode.get():
logging.info("Chat Mode paste")
# Tap the 't' key first
self.keyboard_controller.tap(keyboard.Key.esc)
self.keyboard_controller.tap(keyboard.KeyCode.from_char('t'))
self.keyboard_controller.press(keyboard.Key.backspace)
self.keyboard_controller.press(keyboard.Key.backspace)
self.keyboard_controller.press(keyboard.Key.backspace)
time.sleep(0.1) # Tiny pause
pyperclip.copy(text)
with self.keyboard_controller.pressed(keyboard.Key.ctrl):
self.keyboard_controller.press('v')
self.keyboard_controller.release('v')
else:
# Non-chat mode
# Copy text to clipboard
pyperclip.copy(text)
# Paste using Ctrl+V
with self.keyboard_controller.pressed(keyboard.Key.ctrl):
self.keyboard_controller.press('v')
self.keyboard_controller.release('v')
time.sleep(0.1) # Tiny pause
# Press Enter
self.keyboard_controller.tap(keyboard.Key.enter)
finally:
self.transcription_lock.release()
else:
logging.info("Transcription still in progress...")
def on_click(self, x, y, button, pressed):
if self.is_listening_for_shortcut and self.shortcut_detection_mode:
if pressed:
self.set_shortcut(button, self.shortcut_detection_mode)
return # Skip the normal logic when in shortcut detection mode
if button == mouse.Button.x2:
if pressed:
threading.Thread(target=self.start_recording).start()
else:
threading.Thread(target=self.stop_recording_and_transcribe).start()
elif button == mouse.Button.x1 and not pressed and self.transcribed_text:
logging.info("Running Pasting macro text: %s", self.transcribed_text)
self.perform_macro()
def on_press(self, key):
if key == keyboard.Key.f3:
print("Exiting...")
# Initiate the UI close
self.whisper_ui.root.destroy()
return False # This will stop the listener
if self.is_listening_for_shortcut and self.shortcut_detection_mode:
self.set_shortcut(key, self.shortcut_detection_mode)
self.stop_listening_for_shortcut() # Stop listening after capturing the shortcut
return # Skip the normal logic when in shortcut detection mode
else:
self.trigger_recording_if_shortcut_matches(key)
def set_shortcut(self, input, mode):
# Determine the type of the input
if isinstance(input, mouse.Button):
input_type = "mouse"
shortcut_key = input.name # Get just the button name (e.g., 'x2', 'left', etc.)
elif isinstance(input, keyboard.Key) or isinstance(input, keyboard.KeyCode):
input_type = "keyboard"
shortcut_key = str(input) # Convert to string if necessary
else:
# Handle unexpected input type
input_type = "unknown"
shortcut_key = str(input)
# Update the UI and save only the key
shortcut_info = {"type": input_type, "key": shortcut_key}
if mode == 'record':
self.whisper_ui.set_record_shortcut(shortcut_info)
elif mode == 'paste':
self.whisper_ui.set_paste_shortcut(shortcut_info)
def close_application(self):
# Add cleanup code here
print("Application closing...")
self.recorder.terminate() # Assuming terminate is a method to clean up the recorder
self.mouse_listener.stop() # Assuming you have a mouse listener to stop
self.keyboard_listener.stop() # Assuming you have a keyboard listener to stop
self.whisper_ui.root.destroy() # This will destroy the UI
sys.exit()
def run(self):
try:
self.whisper_ui.start()
logging.info("Setup Complete.")
finally:
self.recorder.terminate()
self.mouse_listener.stop()
self.keyboard_listener.stop()
def load_configuration(self):
try:
with open('config.json', 'r') as config_file:
config = json.load(config_file)
self.record_shortcut = config.get('record_shortcut', {"type": "mouse", "key": "Button.x2"})
self.paste_shortcut = config.get('paste_shortcut', {"type": "keyboard", "key": "v"})
except (FileNotFoundError, json.JSONDecodeError) as e:
logging.error(f"Error loading configuration: {e}")
# Set default values if configuration loading fails
self.record_shortcut = {"type": "mouse", "key": "Button.x2"}
self.paste_shortcut = {"type": "keyboard", "key": "v"}
def start_listening_for_shortcut(self):
self.is_listening_for_shortcut = True
def stop_listening_for_shortcut(self):
self.is_listening_for_shortcut = False
def trigger_recording_if_shortcut_matches(self, key):
# Load or keep track of the configured record shortcut
record_shortcut = self.record_shortcut
if record_shortcut['type'] == 'keyboard' and record_shortcut['key'] == str(key):
if self.recorder.is_recording:
# Stop recording if it's currently active
self.stop_recording_and_transcribe()
else:
# Start recording if it's not currently active
self.start_recording()
def update_shortcuts_from_config(self):
try:
with open('config.json', 'r') as config_file:
config = json.load(config_file)
self.record_shortcut = config.get('record_shortcut', {"type": "mouse", "key": "Button.x2"})
self.paste_shortcut = config.get('paste_shortcut', {"type": "keyboard", "key": "v"})
except (FileNotFoundError, json.JSONDecodeError) as e:
logging.error(f"Error updating configuration: {e}")
def update_model_in_recorder(self):
selected_model = self.whisper_ui.model_combobox.get()
self.recorder.update_model(selected_model)