EXPERIMENT: Added more specific path locations for mac to see if this stops it erroring
This commit is contained in:
108
text-to-mic.py
108
text-to-mic.py
@@ -1,4 +1,5 @@
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
import platform
|
||||||
from tkinter import ttk, messagebox, simpledialog, Menu
|
from tkinter import ttk, messagebox, simpledialog, Menu
|
||||||
import os
|
import os
|
||||||
import pyaudio
|
import pyaudio
|
||||||
@@ -6,6 +7,8 @@ import wave
|
|||||||
import webbrowser
|
import webbrowser
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Load environment variables
|
# Load environment variables
|
||||||
@@ -209,23 +212,63 @@ https://www.scorchsoft.com/blog/text-to-mic-for-meetings/
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_app_support_path_mac():
|
||||||
|
home = Path.home()
|
||||||
|
app_support_path = home / 'Library' / 'Application Support' / 'scorchsoft-text-to-mic'
|
||||||
|
app_support_path.mkdir(parents=True, exist_ok=True) # Ensure directory exists
|
||||||
|
return app_support_path
|
||||||
|
|
||||||
|
def save_api_key_mac(api_key):
|
||||||
|
env_path = get_app_support_path() / '.env'
|
||||||
|
with open(env_path, 'w') as f:
|
||||||
|
f.write(f"OPENAI_API_KEY={api_key}\n")
|
||||||
|
# Consider manually loading this .env file into your environment as needed
|
||||||
|
|
||||||
|
def load_api_key_mac():
|
||||||
|
env_path = get_app_support_path() / '.env'
|
||||||
|
if env_path.exists():
|
||||||
|
with open(env_path, 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith('OPENAI_API_KEY'):
|
||||||
|
return line.strip().split('=')[1]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_api_key(self):
|
def get_api_key(self):
|
||||||
|
# First, try to load the API key from environment variables or local file
|
||||||
api_key = os.getenv("OPENAI_API_KEY")
|
api_key = os.getenv("OPENAI_API_KEY")
|
||||||
if not api_key: # No API key found in the environment
|
if not api_key: # Check for macOS and use the macOS-specific method
|
||||||
self.show_instructions() # Show the "How to Use" modal after setting the key
|
if platform.system() == 'Darwin': # Darwin is the system name for macOS
|
||||||
api_key = simpledialog.askstring("API Key", "Enter your OpenAI API Key:", parent=self)
|
api_key = self.load_api_key_mac()
|
||||||
if api_key:
|
|
||||||
self.save_api_key(api_key)
|
# If no API key is found, prompt the user
|
||||||
messagebox.showinfo("API Key Set", "The OpenAI API Key has been updated successfully.")
|
if not api_key:
|
||||||
|
self.show_instructions() # Show the "How to Use" modal after setting the key
|
||||||
|
api_key = simpledialog.askstring("API Key", "Enter your OpenAI API Key:", parent=self)
|
||||||
|
if api_key:
|
||||||
|
try:
|
||||||
|
if platform.system() == 'Darwin':
|
||||||
|
self.save_api_key_mac(api_key)
|
||||||
|
else:
|
||||||
|
self.save_api_key(api_key)
|
||||||
|
messagebox.showinfo("API Key Set", "The OpenAI API Key has been updated successfully.")
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Error", f"Failed to save API key: {str(e)}")
|
||||||
|
|
||||||
return api_key
|
return api_key
|
||||||
|
|
||||||
|
|
||||||
def save_api_key(self, api_key):
|
def save_api_key(self, api_key):
|
||||||
with open('.env', 'w') as f:
|
try:
|
||||||
f.write(f"OPENAI_API_KEY={api_key}\n")
|
if platform.system() == 'Darwin':
|
||||||
load_dotenv()
|
self.save_api_key_mac(api_key)
|
||||||
|
else:
|
||||||
|
with open('.env', 'w') as f:
|
||||||
|
f.write(f"OPENAI_API_KEY={api_key}\n")
|
||||||
|
load_dotenv() # Reload environment to include the new API key
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Error", f"Failed to save API key: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
def get_audio_devices(self):
|
def get_audio_devices(self):
|
||||||
p = pyaudio.PyAudio()
|
p = pyaudio.PyAudio()
|
||||||
@@ -236,6 +279,13 @@ https://www.scorchsoft.com/blog/text-to-mic-for-meetings/
|
|||||||
devices[info['name']] = i
|
devices[info['name']] = i
|
||||||
p.terminate()
|
p.terminate()
|
||||||
return devices
|
return devices
|
||||||
|
|
||||||
|
def get_audio_file_path(self, filename):
|
||||||
|
if platform.system() == 'Darwin': # Check if the OS is macOS
|
||||||
|
return self.get_app_support_path_mac() / filename
|
||||||
|
else:
|
||||||
|
return Path(filename) # Default to current directory for non-macOS systems
|
||||||
|
|
||||||
|
|
||||||
def submit_text(self):
|
def submit_text(self):
|
||||||
|
|
||||||
@@ -264,8 +314,9 @@ https://www.scorchsoft.com/blog/text-to-mic-for-meetings/
|
|||||||
input=text,
|
input=text,
|
||||||
response_format='wav'
|
response_format='wav'
|
||||||
)
|
)
|
||||||
self.last_audio_file = "last_output.wav"
|
|
||||||
response.stream_to_file(self.last_audio_file)
|
self.last_audio_file = self.get_audio_file_path("last_output.wav")
|
||||||
|
response.stream_to_file(str(self.last_audio_file))
|
||||||
|
|
||||||
#Play to either two or a single stream
|
#Play to either two or a single stream
|
||||||
if primary_index and secondary_index != "None" and secondary_index is not None:
|
if primary_index and secondary_index != "None" and secondary_index is not None:
|
||||||
@@ -287,12 +338,30 @@ https://www.scorchsoft.com/blog/text-to-mic-for-meetings/
|
|||||||
try:
|
try:
|
||||||
# Open all files and start all streams
|
# Open all files and start all streams
|
||||||
for file_path, device_index in zip(file_paths, device_indices):
|
for file_path, device_index in zip(file_paths, device_indices):
|
||||||
wf = wave.open(file_path, 'rb')
|
|
||||||
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
|
|
||||||
channels=wf.getnchannels(),
|
|
||||||
rate=wf.getframerate(),
|
try:
|
||||||
output=True,
|
# Ensure the file_path is a string when opening the file
|
||||||
output_device_index=int(device_index))
|
wf = wave.open(str(file_path), 'rb')
|
||||||
|
except FileNotFoundError:
|
||||||
|
messagebox.showerror("File Not Found", f"Could not find audio file: {file_path}")
|
||||||
|
continue # Skip this iteration and proceed with other files if any
|
||||||
|
except wave.Error as e:
|
||||||
|
messagebox.showerror("Wave Error", f"Error reading audio file: {file_path}. Error: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
|
||||||
|
channels=wf.getnchannels(),
|
||||||
|
rate=wf.getframerate(),
|
||||||
|
output=True,
|
||||||
|
output_device_index=int(device_index))
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Stream Creation Error", f"Failed to create audio stream for device index {device_index}: {str(e)}")
|
||||||
|
wf.close()
|
||||||
|
continue
|
||||||
|
|
||||||
streams.append((stream, wf))
|
streams.append((stream, wf))
|
||||||
|
|
||||||
# Play interleaved
|
# Play interleaved
|
||||||
@@ -308,6 +377,7 @@ https://www.scorchsoft.com/blog/text-to-mic-for-meetings/
|
|||||||
wf.close()
|
wf.close()
|
||||||
streams.remove((stream, wf))
|
streams.remove((stream, wf))
|
||||||
active_streams -= 1
|
active_streams -= 1
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messagebox.showerror("Playback Error", f"Error during multiplexed playback: {e}")
|
messagebox.showerror("Playback Error", f"Error during multiplexed playback: {e}")
|
||||||
finally:
|
finally:
|
||||||
@@ -316,8 +386,6 @@ https://www.scorchsoft.com/blog/text-to-mic-for-meetings/
|
|||||||
|
|
||||||
def play_last_audio(self):
|
def play_last_audio(self):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if hasattr(self, 'last_audio_file'):
|
if hasattr(self, 'last_audio_file'):
|
||||||
primary_index = self.available_devices.get(self.device_index.get(), None)
|
primary_index = self.available_devices.get(self.device_index.get(), None)
|
||||||
secondary_index = self.available_devices.get(self.device_index_2.get(), None) if self.device_index_2.get() != "None" else None
|
secondary_index = self.available_devices.get(self.device_index_2.get(), None) if self.device_index_2.get() != "None" else None
|
||||||
|
|||||||
Reference in New Issue
Block a user