Update clipboard-to-tts.py
This commit is contained in:
1 parent
c31e1f68b5
commit
b03e08405e
1 file changed
+156
-27
@@ -1,12 +1,28 @@
|
||||
import sys
|
||||
import time
|
||||
import signal
|
||||
import json
|
||||
import warnings
|
||||
import pyperclip
|
||||
import requests
|
||||
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton
|
||||
from PyQt5.QtWidgets import (QApplication, QSystemTrayIcon, QMenu, QWidget, QVBoxLayout, QLabel,
|
||||
QLineEdit, QPushButton, QMessageBox, QComboBox, QSlider)
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
|
||||
from PyQt5.QtGui import QIcon
|
||||
|
||||
# Suppress the specific warning
|
||||
warnings.filterwarnings("ignore", category=UserWarning, module="charset_normalizer")
|
||||
|
||||
SETTINGS_FILE = "tts_settings.json"
|
||||
TTS_SERVER_URL = "http://127.0.0.1:7851"
|
||||
|
||||
LANGUAGES = {
|
||||
"ar": "Arabic", "zh-cn": "Chinese (Simplified)", "cs": "Czech", "nl": "Dutch",
|
||||
"en": "English", "fr": "French", "de": "German", "hi": "Hindi (limited support)",
|
||||
"hu": "Hungarian", "it": "Italian", "ja": "Japanese", "ko": "Korean",
|
||||
"pl": "Polish", "pt": "Portuguese", "ru": "Russian", "es": "Spanish", "tr": "Turkish"
|
||||
}
|
||||
|
||||
class ClipboardMonitor(QThread):
|
||||
text_changed = pyqtSignal(str)
|
||||
|
||||
@@ -27,6 +43,8 @@ class ClipboardMonitor(QThread):
|
||||
self.running = False
|
||||
|
||||
class TTSWorker(QThread):
|
||||
error_occurred = pyqtSignal(str)
|
||||
|
||||
def __init__(self, api_url, params):
|
||||
super().__init__()
|
||||
self.api_url = api_url
|
||||
@@ -47,62 +65,143 @@ class TTSWorker(QThread):
|
||||
def send_to_tts(self, text):
|
||||
params = self.params.copy()
|
||||
params["text_input"] = text
|
||||
params["language"] = params["language"][:2] # Ensure we only send the 2-letter code
|
||||
try:
|
||||
response = requests.post(self.api_url, data=params)
|
||||
response = requests.post(f"{self.api_url}/api/tts-generate", data=params)
|
||||
if response.status_code != 200:
|
||||
print(f"TTS generation failed with status code: {response.status_code}")
|
||||
error_message = f"TTS generation failed with status code: {response.status_code}. Response: {response.text}"
|
||||
print(error_message)
|
||||
self.error_occurred.emit(error_message)
|
||||
except requests.RequestException as e:
|
||||
print(f"Error sending request to TTS API: {e}")
|
||||
error_message = f"Error sending request to TTS API: {e}"
|
||||
print(error_message)
|
||||
self.error_occurred.emit(error_message)
|
||||
|
||||
class SettingsWindow(QWidget):
|
||||
settings_updated = pyqtSignal(dict)
|
||||
|
||||
def __init__(self, tts_worker):
|
||||
super().__init__()
|
||||
self.tts_worker = tts_worker
|
||||
self.voices = []
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
layout.addWidget(QLabel("TTS API URL:"))
|
||||
layout.addWidget(QLabel("AllTalk API URL:"))
|
||||
self.api_url_input = QLineEdit(self.tts_worker.api_url)
|
||||
layout.addWidget(self.api_url_input)
|
||||
|
||||
layout.addWidget(QLabel("Voice:"))
|
||||
self.voice_input = QLineEdit(self.tts_worker.params["character_voice_gen"])
|
||||
layout.addWidget(self.voice_input)
|
||||
self.voice_combo = QComboBox()
|
||||
layout.addWidget(self.voice_combo)
|
||||
|
||||
layout.addWidget(QLabel("Language:"))
|
||||
self.language_combo = QComboBox()
|
||||
for code, name in LANGUAGES.items():
|
||||
self.language_combo.addItem(f"{name} ({code})", code)
|
||||
current_lang = self.tts_worker.params.get("language", "en")
|
||||
index = self.language_combo.findData(current_lang)
|
||||
if index >= 0:
|
||||
self.language_combo.setCurrentIndex(index)
|
||||
layout.addWidget(self.language_combo)
|
||||
|
||||
layout.addWidget(QLabel("Text Filtering:"))
|
||||
self.text_filtering_input = QLineEdit(self.tts_worker.params["text_filtering"])
|
||||
layout.addWidget(self.text_filtering_input)
|
||||
|
||||
layout.addWidget(QLabel("Playback Volume:"))
|
||||
self.volume_slider = QSlider(Qt.Horizontal)
|
||||
self.volume_slider.setRange(1, 10)
|
||||
self.volume_slider.setValue(int(float(self.tts_worker.params["autoplay_volume"]) * 10))
|
||||
layout.addWidget(self.volume_slider)
|
||||
|
||||
save_button = QPushButton("Save")
|
||||
save_button.clicked.connect(self.save_settings)
|
||||
layout.addWidget(save_button)
|
||||
|
||||
self.setWindowTitle("TTS Settings")
|
||||
self.setGeometry(300, 300, 300, 150)
|
||||
refresh_button = QPushButton("Refresh Voices")
|
||||
refresh_button.clicked.connect(self.refresh_voices)
|
||||
layout.addWidget(refresh_button)
|
||||
|
||||
self.setWindowTitle("AllTalk Clipboard TTS")
|
||||
self.setGeometry(300, 300, 300, 400)
|
||||
|
||||
self.refresh_voices()
|
||||
|
||||
def refresh_voices(self):
|
||||
try:
|
||||
response = requests.get(f"{self.tts_worker.api_url}/api/voices")
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
if data["status"] == "success" and "voices" in data:
|
||||
self.voices = data["voices"]
|
||||
self.voice_combo.clear()
|
||||
for voice in self.voices:
|
||||
self.voice_combo.addItem(voice)
|
||||
if self.voices:
|
||||
current_voice = self.tts_worker.params["character_voice_gen"]
|
||||
if current_voice in self.voices:
|
||||
self.voice_combo.setCurrentText(current_voice)
|
||||
else:
|
||||
self.voice_combo.setCurrentIndex(0)
|
||||
else:
|
||||
QMessageBox.warning(self, "Error", "Failed to fetch voices: Unexpected response format")
|
||||
else:
|
||||
QMessageBox.warning(self, "Error", f"Failed to fetch voices. Status code: {response.status_code}")
|
||||
except requests.RequestException as e:
|
||||
QMessageBox.warning(self, "Error", f"Failed to fetch voices: {str(e)}")
|
||||
|
||||
def save_settings(self):
|
||||
self.tts_worker.api_url = self.api_url_input.text()
|
||||
self.tts_worker.params["character_voice_gen"] = self.voice_input.text()
|
||||
self.close()
|
||||
new_settings = {
|
||||
"api_url": self.api_url_input.text(),
|
||||
"params": {
|
||||
"character_voice_gen": self.voice_combo.currentText(),
|
||||
"language": self.language_combo.currentData(),
|
||||
"text_filtering": self.text_filtering_input.text(),
|
||||
"output_file_name": "clipboard_tts",
|
||||
"output_file_timestamp": "true",
|
||||
"autoplay": "true",
|
||||
"autoplay_volume": str(self.volume_slider.value() / 10.0)
|
||||
}
|
||||
}
|
||||
self.settings_updated.emit(new_settings)
|
||||
self.hide()
|
||||
|
||||
class TTSApp(QSystemTrayIcon):
|
||||
def __init__(self, app):
|
||||
super().__init__(parent=app)
|
||||
self.app = app
|
||||
self.load_settings()
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
self.tts_worker = TTSWorker(
|
||||
api_url="http://127.0.0.1:7851/api/tts-generate",
|
||||
params={
|
||||
"text_filtering": "standard",
|
||||
"character_voice_gen": "female_01.wav",
|
||||
"language": "en",
|
||||
"output_file_name": "clipboard_tts",
|
||||
"output_file_timestamp": "true",
|
||||
"autoplay": "true",
|
||||
"autoplay_volume": "0.8"
|
||||
def load_settings(self):
|
||||
try:
|
||||
with open(SETTINGS_FILE, 'r') as f:
|
||||
self.settings = json.load(f)
|
||||
except FileNotFoundError:
|
||||
self.settings = {
|
||||
"api_url": TTS_SERVER_URL,
|
||||
"params": {
|
||||
"text_filtering": "standard",
|
||||
"character_voice_gen": "",
|
||||
"language": "en",
|
||||
"output_file_name": "clipboard_tts",
|
||||
"output_file_timestamp": "true",
|
||||
"autoplay": "true",
|
||||
"autoplay_volume": "0.8"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def save_settings(self):
|
||||
with open(SETTINGS_FILE, 'w') as f:
|
||||
json.dump(self.settings, f)
|
||||
|
||||
def init_ui(self):
|
||||
self.tts_worker = TTSWorker(self.settings["api_url"], self.settings["params"])
|
||||
self.tts_worker.error_occurred.connect(self.show_error_message)
|
||||
self.tts_worker.start()
|
||||
|
||||
self.clipboard_monitor = ClipboardMonitor()
|
||||
@@ -123,6 +222,7 @@ class TTSApp(QSystemTrayIcon):
|
||||
self.setContextMenu(menu)
|
||||
|
||||
self.settings_window = SettingsWindow(self.tts_worker)
|
||||
self.settings_window.settings_updated.connect(self.update_settings)
|
||||
|
||||
def on_text_changed(self, text):
|
||||
self.tts_worker.add_text(text)
|
||||
@@ -138,23 +238,50 @@ class TTSApp(QSystemTrayIcon):
|
||||
def show_settings(self):
|
||||
self.settings_window.show()
|
||||
|
||||
def update_settings(self, new_settings):
|
||||
self.settings = new_settings
|
||||
self.tts_worker.api_url = new_settings["api_url"]
|
||||
self.tts_worker.params = new_settings["params"]
|
||||
self.save_settings()
|
||||
self.showMessage("TTS App", "Settings updated successfully")
|
||||
|
||||
def show_error_message(self, message):
|
||||
QMessageBox.critical(None, "Error", message)
|
||||
|
||||
def exit_app(self):
|
||||
self.clipboard_monitor.stop()
|
||||
self.tts_worker.quit()
|
||||
QApplication.instance().quit()
|
||||
|
||||
def check_server_ready(url, timeout=10):
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
try:
|
||||
response = requests.get(f"{url}/api/ready")
|
||||
if response.status_code == 200 and response.text == "Ready":
|
||||
return True
|
||||
except requests.RequestException:
|
||||
pass
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
def signal_handler(signal, frame):
|
||||
print("[Clipboard to TTS] Exiting...")
|
||||
QApplication.instance().quit()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("[AllTalk TTS] AllTalk Clipboard to TTS is starting...")
|
||||
print("[AllTalk TTS] Press Ctrl+C to exit")
|
||||
print("[Clipboard to TTS] AllTalk Clipboard to TTS is starting...")
|
||||
|
||||
if not check_server_ready(TTS_SERVER_URL):
|
||||
print("[Clipboard to TTS] Error: AllTalk TTS server is not ready. Please start the AllTalk server and try again.")
|
||||
sys.exit(1)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
if not QSystemTrayIcon.isSystemTrayAvailable():
|
||||
print("System tray is not available on this system.")
|
||||
print("[Clipboard to TTS] System tray is not available on this system.")
|
||||
sys.exit(1)
|
||||
|
||||
tts_app = TTSApp(app)
|
||||
@@ -164,4 +291,6 @@ if __name__ == "__main__":
|
||||
timer.start(500)
|
||||
timer.timeout.connect(lambda: None)
|
||||
|
||||
print("[Clipboard to TTS] AllTalk Clipboard to TTS is running... Check your system tray for the icon.")
|
||||
print("[Clipboard to TTS] Press Ctrl+C to exit")
|
||||
sys.exit(app.exec_())
|
||||
Reference in new issue
Block a user