Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,5 @@ config.yaml
*.keyring
*.cfg
*.db
/keyring/*
/keyring/*
dictationRunner.bat
1 change: 1 addition & 0 deletions src/config_schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ model_options:
- whisper-large-v3-turbo
- distil-whisper-large-v3-en
- whisper-large-v3
- gpt-4o-transcribe
openai_transcription_api_key:
value: null
type: str
Expand Down
70 changes: 46 additions & 24 deletions src/input_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,20 @@ def typewrite(self, text):
self._typewrite_ydotool(text, interval)
elif self.input_method == 'dotool':
self._typewrite_dotool(text, interval)

@staticmethod
def safe_open_clipboard(max_retries=5, delay=0.1):
"""
Try to open the clipboard repeatedly.
Returns True if successful, else False.
"""
for _ in range(max_retries):
try:
win32clipboard.OpenClipboard()
return True
except Exception:
time.sleep(delay)
return False

def _paste_with_clipboard_preservation(self, text):
"""
Expand All @@ -87,7 +101,9 @@ def _paste_with_clipboard_preservation(self, text):
"""
# Store all clipboard formats
saved_formats = {}
win32clipboard.OpenClipboard()
if not InputSimulator.safe_open_clipboard():
print("Unable to open clipboard for preserving original content.")
return

try:
# Get the list of available formats
Expand All @@ -99,38 +115,44 @@ def _paste_with_clipboard_preservation(self, text):
except:
pass # Skip formats we can't handle
format_id = win32clipboard.EnumClipboardFormats(format_id)

# Clear clipboard and set our text
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(text)
win32clipboard.CloseClipboard()

# Simulate Ctrl+V
if self.input_method == 'pynput':
with self.keyboard.pressed(Key.ctrl):
self.keyboard.press('v')
self.keyboard.release('v')
elif self.input_method == 'ydotool':
run_command_or_exit_on_failure([
"ydotool", "key", "ctrl+v"
])
elif self.input_method == 'dotool':
assert self.dotool_process and self.dotool_process.stdin
self.dotool_process.stdin.write("key ctrl+v\n")
self.dotool_process.stdin.flush()

# Wait for paste to complete
time.sleep(0.1)
win32clipboard.SetClipboardText(text, win32con.CF_UNICODETEXT)
finally:
try:
win32clipboard.CloseClipboard()
except:
pass # Ensure clipboard is closed even if an error occurred

# Simulate Ctrl+V
if self.input_method == 'pynput':
with self.keyboard.pressed(Key.ctrl):
self.keyboard.press('v')
self.keyboard.release('v')
elif self.input_method == 'ydotool':
run_command_or_exit_on_failure([
"ydotool", "key", "ctrl+v"
])
elif self.input_method == 'dotool':
assert self.dotool_process and self.dotool_process.stdin
self.dotool_process.stdin.write("key ctrl+v\n")
self.dotool_process.stdin.flush()

# Restore all original clipboard formats
win32clipboard.OpenClipboard()
# Wait for paste to complete
time.sleep(0.1)

# Restore all original clipboard formats
if not InputSimulator.safe_open_clipboard():
print("Unable to reopen clipboard for restoring original content.")
return
try:
win32clipboard.EmptyClipboard()
for format_id, data in saved_formats.items():
try:
win32clipboard.SetClipboardData(format_id, data)
except:
pass # Skip if we can't restore a particular format

finally:
try:
win32clipboard.CloseClipboard()
Expand Down
4 changes: 2 additions & 2 deletions src/llm_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ def __init__(self, api_type=None):
self.api_key = KeyringManager.get_api_key("gemini")
ConfigManager.console_print("Using Gemini API")
elif self.api_type == 'ollama':
self.api_key = 'null'
ConfigManager.console_print("Using local Ollama installation")
elif self.api_type == 'groq':
self.api_key = KeyringManager.get_api_key("groq")
ConfigManager.console_print("Using Groq API")

if not self.api_key and self.api_type != 'ollama':
ConfigManager.console_print(f"Warning: No API key found for {self.api_type}")

Expand Down Expand Up @@ -473,4 +473,4 @@ def get_available_models(self, api_type):
except Exception as e:
ConfigManager.console_print(f"Error fetching {api_type} models: {str(e)}")

return []
return []
6 changes: 4 additions & 2 deletions src/transcription.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,14 @@ def transcribe_with_openai(audio_data, api_options):
sf.write(byte_io, audio_data, 16000, format='wav')
byte_io.seek(0)

model = api_options['model']

files = {
'file': ('audio.wav', byte_io, 'audio/wav'),
'model': (None, 'whisper-1'),
'model': (None, model),
}

ConfigManager.console_print("Sending request to OpenAI API...")
ConfigManager.console_print(f"Sending request to OpenAI API using {model}...")
response = requests.post(
f"{base_url}/audio/transcriptions",
headers=headers,
Expand Down
5 changes: 1 addition & 4 deletions src/ui/settings_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,10 +771,7 @@ def browse_system_message_file(self, file_edit, text_edit):
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
current_text = text_edit.toPlainText()
if current_text:
text_edit.setText(f"{current_text}\n\n{content}")
else:
text_edit.setText(content)
text_edit.setText(current_text)
except Exception as e:
QMessageBox.warning(self, "Error", f"Failed to read file: {str(e)}")

Expand Down