diff --git a/system/cliptotts/clipboard-to-tts.py b/system/cliptotts/clipboard-to-tts.py new file mode 100644 index 0000000..4ea967b --- /dev/null +++ b/system/cliptotts/clipboard-to-tts.py @@ -0,0 +1,167 @@ +import sys +import time +import signal +import pyperclip +import requests +from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton +from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer +from PyQt5.QtGui import QIcon + +class ClipboardMonitor(QThread): + text_changed = pyqtSignal(str) + + def __init__(self): + super().__init__() + self.last_text = pyperclip.paste() + self.running = True + + def run(self): + while self.running: + current_text = pyperclip.paste() + if current_text != self.last_text: + self.text_changed.emit(current_text) + self.last_text = current_text + time.sleep(0.5) + + def stop(self): + self.running = False + +class TTSWorker(QThread): + def __init__(self, api_url, params): + super().__init__() + self.api_url = api_url + self.params = params + self.text_queue = [] + + def add_text(self, text): + self.text_queue.append(text) + + def run(self): + while True: + if self.text_queue: + text = self.text_queue.pop(0) + self.send_to_tts(text) + else: + time.sleep(0.1) + + def send_to_tts(self, text): + params = self.params.copy() + params["text_input"] = text + try: + response = requests.post(self.api_url, data=params) + if response.status_code != 200: + print(f"TTS generation failed with status code: {response.status_code}") + except requests.RequestException as e: + print(f"Error sending request to TTS API: {e}") + +class SettingsWindow(QWidget): + def __init__(self, tts_worker): + super().__init__() + self.tts_worker = tts_worker + self.init_ui() + + def init_ui(self): + layout = QVBoxLayout() + self.setLayout(layout) + + layout.addWidget(QLabel("TTS 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) + + 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) + + 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() + +class TTSApp(QSystemTrayIcon): + def __init__(self, app): + super().__init__(parent=app) + self.app = app + 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" + } + ) + self.tts_worker.start() + + self.clipboard_monitor = ClipboardMonitor() + self.clipboard_monitor.text_changed.connect(self.on_text_changed) + + self.setIcon(QIcon("icon.png")) + self.setVisible(True) + + menu = QMenu() + start_action = menu.addAction("Start Monitoring") + start_action.triggered.connect(self.start_monitoring) + stop_action = menu.addAction("Stop Monitoring") + stop_action.triggered.connect(self.stop_monitoring) + settings_action = menu.addAction("Settings") + settings_action.triggered.connect(self.show_settings) + exit_action = menu.addAction("Exit") + exit_action.triggered.connect(self.exit_app) + self.setContextMenu(menu) + + self.settings_window = SettingsWindow(self.tts_worker) + + def on_text_changed(self, text): + self.tts_worker.add_text(text) + + def start_monitoring(self): + self.clipboard_monitor.start() + self.showMessage("TTS App", "Clipboard monitoring started") + + def stop_monitoring(self): + self.clipboard_monitor.stop() + self.showMessage("TTS App", "Clipboard monitoring stopped") + + def show_settings(self): + self.settings_window.show() + + def exit_app(self): + self.clipboard_monitor.stop() + self.tts_worker.quit() + QApplication.instance().quit() + +def signal_handler(signal, frame): + QApplication.instance().quit() + +if __name__ == "__main__": + print("[AllTalk TTS] AllTalk Clipboard to TTS is starting...") + print("[AllTalk TTS] Press Ctrl+C to exit") + signal.signal(signal.SIGINT, signal_handler) + + app = QApplication(sys.argv) + + if not QSystemTrayIcon.isSystemTrayAvailable(): + print("System tray is not available on this system.") + sys.exit(1) + + tts_app = TTSApp(app) + + # This allows for Ctrl+C to work + timer = QTimer() + timer.start(500) + timer.timeout.connect(lambda: None) + + sys.exit(app.exec_()) diff --git a/system/cliptotts/icon.png b/system/cliptotts/icon.png new file mode 100644 index 0000000..adcc4c5 Binary files /dev/null and b/system/cliptotts/icon.png differ diff --git a/system/cliptotts/instructions.txt b/system/cliptotts/instructions.txt new file mode 100644 index 0000000..c89aaef --- /dev/null +++ b/system/cliptotts/instructions.txt @@ -0,0 +1,11 @@ +This is a Windows only clipboard to TTS extension. Its a very rough bit of code with no logic or error handling built in. + +When run it will create a system tray icon that you can Start/Stop clipboard monitoring for the text stored in the clipboard changing. When the text changes, it will be sent to AllTalk for TTS generation. You can specify the voice to use in the System Tray icon Settings area. + +In the main folder, run "start_environment.bat" + +Go to the "cliptotts" folder and "python clipboard-to-tts.py" which should start the application. + +Start monitoring of the clipboard on the icon in the system tray. + +When you Ctrl+C text to the clipboard, it will be sent over for TTS generation and it will play back at the AllTalk console. \ No newline at end of file