diff --git a/utils/__pycache__/text_to_mic.cpython-312.pyc b/utils/__pycache__/text_to_mic.cpython-312.pyc index ba0a47f..1a6b8ef 100644 Binary files a/utils/__pycache__/text_to_mic.cpython-312.pyc and b/utils/__pycache__/text_to_mic.cpython-312.pyc differ diff --git a/utils/ai_editor_manager.py b/utils/ai_editor_manager.py index 2189481..926b1ee 100644 --- a/utils/ai_editor_manager.py +++ b/utils/ai_editor_manager.py @@ -1,5 +1,6 @@ import tkinter as tk from tkinter import ttk, messagebox +from utils.settings_manager import SettingsManager class AIEditorManager: """ @@ -90,9 +91,9 @@ class AIEditorManager: def save_settings(self, settings): """Save AI copy editing settings and update the UI""" - self.app.save_settings_to_JSON(settings) + SettingsManager.update_settings(settings) + messagebox.showinfo("Settings Updated", "Your settings have been saved successfully.") - self.app.load_settings() # Refresh settings if needed elsewhere # Update the status indicator on the main screen with more specific information self.update_status_display() diff --git a/utils/settings_manager.py b/utils/settings_manager.py new file mode 100644 index 0000000..4aaca94 --- /dev/null +++ b/utils/settings_manager.py @@ -0,0 +1,116 @@ +import json +import platform +from pathlib import Path + +class SettingsManager: + """ + Centralizes access to application settings to prevent conflicts between components. + """ + + @staticmethod + def get_settings_file_path(filename="settings.json"): + """Get the platform-specific path for the settings file.""" + if platform.system() == 'Darwin': # macOS + from utils.api_key_manager import APIKeyManager + mac_path = APIKeyManager.get_app_support_path_mac() + return f"{mac_path}/{filename}" + else: + return filename # Default to current directory for non-macOS systems + + @classmethod + def get_default_settings(cls): + """Return the default settings structure.""" + return { + "chat_gpt_completion": False, + "model": "gpt-4o-mini", + "prompt": "", + "auto_apply_ai_to_recording": False, + "current_tone": "None", + "hide_banner": False, + "input_device": "Default", + "primary_device": "Select Device", + "secondary_device": "None", + "hotkeys": { + "record_start_stop": ["ctrl", "shift", "0"], + "stop_recording": ["ctrl", "shift", "9"], + "play_last_audio": ["ctrl", "shift", "8"], + "cancel_operation": ["ctrl", "shift", "1"] + }, + "max_tokens": 750 + } + + @classmethod + def load_settings(cls): + """Load settings from file, with defaults for missing values.""" + settings_file = cls.get_settings_file_path() + default_settings = cls.get_default_settings() + + try: + # Try to load existing settings + with open(settings_file, "r") as f: + settings = json.load(f) + + # Check if settings need to be updated with new defaults + settings_updated = False + + # Recursively update nested dictionaries with missing keys + def update_missing_settings(existing, defaults): + nonlocal settings_updated + for key, value in defaults.items(): + if key not in existing: + existing[key] = value + settings_updated = True + elif isinstance(value, dict) and isinstance(existing[key], dict): + # Recursively update nested dictionaries + update_missing_settings(existing[key], value) + return existing + + # Update settings with any missing values + settings = update_missing_settings(settings, default_settings) + + # Save if any settings were updated + if settings_updated: + cls.save_settings(settings) + print("Settings file updated with new default values") + + except FileNotFoundError: + # Create new settings file with defaults if it doesn't exist + settings = default_settings + cls.save_settings(settings) + + return settings + + @classmethod + def save_settings(cls, settings): + """Save complete settings to file.""" + settings_file = cls.get_settings_file_path() + + with open(settings_file, "w") as f: + json.dump(settings, f) + + @classmethod + def update_settings(cls, partial_settings): + """ + Update only specific settings without touching others. + + Args: + partial_settings: Dictionary containing only the settings to update + """ + # First load existing settings + current_settings = cls.load_settings() + + # Update settings (recursively for nested dictionaries) + def recursive_update(target, source): + for key, value in source.items(): + if isinstance(value, dict) and key in target and isinstance(target[key], dict): + # If both are dictionaries, update recursively + recursive_update(target[key], value) + else: + # Otherwise just update the value + target[key] = value + + recursive_update(current_settings, partial_settings) + + # Save the updated settings + cls.save_settings(current_settings) + return current_settings \ No newline at end of file diff --git a/utils/text_to_mic.py b/utils/text_to_mic.py index 6bf0bc7..78bb6bc 100644 --- a/utils/text_to_mic.py +++ b/utils/text_to_mic.py @@ -25,6 +25,7 @@ from utils.resource_utils import ResourceUtils from utils.tone_presets_manager import TonePresetsManager from utils.presets_manager import PresetsManager from utils.ai_editor_manager import AIEditorManager +from utils.settings_manager import SettingsManager # Modify the load environment variables to load from config/.env def load_env_file(): @@ -1168,75 +1169,20 @@ Please also make sure you read the Terms of use and licence statement before usi print(f"Transcription error: An error occurred during transcription: {str(e)}") def load_settings(self): - settings_file = self.get_settings_file_path("settings.json") - - # Define default settings structure - default_settings = { - "chat_gpt_completion": False, - "model": self.default_model, - "prompt": "", - "auto_apply_ai_to_recording": False, - "current_tone": "None", - "hide_banner": False, - "input_device": "Default", # Add default for input device - "primary_device": "Select Device", # Also add for primary device - "secondary_device": "None", # Also add for secondary device - "hotkeys": { - "record_start_stop": ["ctrl", "shift", "0"], - "stop_recording": ["ctrl", "shift", "9"], - "play_last_audio": ["ctrl", "shift", "8"], - "cancel_operation": ["ctrl", "shift", "1"] - }, - "max_tokens": 750 - } - - try: - # Try to load existing settings - with open(settings_file, "r") as f: - settings = json.load(f) - - # Check if settings need to be updated with new defaults - settings_updated = False - - # Recursively update nested dictionaries with missing keys - def update_missing_settings(existing, defaults): - nonlocal settings_updated - for key, value in defaults.items(): - if key not in existing: - existing[key] = value - settings_updated = True - elif isinstance(value, dict) and isinstance(existing[key], dict): - # Recursively update nested dictionaries - update_missing_settings(existing[key], value) - return existing - - # Update settings with any missing values - settings = update_missing_settings(settings, default_settings) - - # Save if any settings were updated - if settings_updated: - self.save_settings_to_JSON(settings) - print("Settings file updated with new default values") - - except FileNotFoundError: - # Create new settings file with defaults if it doesn't exist - settings = default_settings - self.save_settings_to_JSON(settings) - - return settings + """Load settings using the SettingsManager.""" + return SettingsManager.load_settings() def save_settings_to_JSON(self, settings): - settings_file = self.get_settings_file_path("settings.json") - - with open(settings_file, "w") as f: - json.dump(settings, f) + """Save complete settings using the SettingsManager.""" + SettingsManager.save_settings(settings) + + def update_settings(self, partial_settings): + """Update specific settings without overwriting others.""" + return SettingsManager.update_settings(partial_settings) def get_settings_file_path(self, filename): - if platform.system() == 'Darwin': # Check if the OS is macOS - mac_path = APIKeyManager.get_app_support_path_mac() - return f"{mac_path}/{filename}" - else: - return filename # Default to current directory for non-macOS systems + """Get the settings file path using SettingsManager.""" + return SettingsManager.get_settings_file_path(filename) # Methods for tone preset management def show_tone_presets_manager(self):