diff --git a/.gitignore b/.gitignore index 9baee880..39252f08 100644 --- a/.gitignore +++ b/.gitignore @@ -139,4 +139,5 @@ config.yaml *.keyring *.cfg *.db -/keyring/* \ No newline at end of file +/keyring/* +dictationRunner.bat diff --git a/src/config_schema.yaml b/src/config_schema.yaml index ad7900c0..58decd3d 100644 --- a/src/config_schema.yaml +++ b/src/config_schema.yaml @@ -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 diff --git a/src/input_simulation.py b/src/input_simulation.py index cda59d2e..29e39633 100644 --- a/src/input_simulation.py +++ b/src/input_simulation.py @@ -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): """ @@ -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 @@ -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() diff --git a/src/llm_processor.py b/src/llm_processor.py index c898ab92..81e932c4 100644 --- a/src/llm_processor.py +++ b/src/llm_processor.py @@ -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}") @@ -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 [] \ No newline at end of file + return [] diff --git a/src/transcription.py b/src/transcription.py index 6df8b130..fb16d885 100644 --- a/src/transcription.py +++ b/src/transcription.py @@ -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, diff --git a/src/ui/settings_window.py b/src/ui/settings_window.py index 0bdd7c75..6890f882 100644 --- a/src/ui/settings_window.py +++ b/src/ui/settings_window.py @@ -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)}")