您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add a button to each chat message to play it using TTS API
// ==UserScript== // @name vits-simple-api for SillyTavern // @namespace https://github.com/yujianke100/vits-simple-api-for-SillyTavern // @version 1.0.3 // @license MIT // @description Add a button to each chat message to play it using TTS API // @author yujianke100 // @match http://*:8080* // @match http://localhost:8080/* // @match http://127.0.0.1:8080/* // @grant GM_xmlhttpRequest // ==/UserScript== (function() { 'use strict'; let currentAudio = null; let readQuotesOnly = false; let toggleSwitches = []; let apiDomain = localStorage.getItem('ttsApiDomain') || 'http://127.0.0.1:23456'; // Function to add TTS/Stop button and a Toggle button to each chat message function addTTSButton(message) { if (!message.querySelector('.mytts-button')) { const ttsButton = document.createElement('button'); ttsButton.innerText = 'TTS'; ttsButton.classList.add('mytts-button', 'action-button'); updateTTSButtonStyle(ttsButton, false); const toggleButton = document.createElement('button'); toggleButton.innerText = readQuotesOnly ? 'Quotes' : 'All'; toggleButton.classList.add('toggle-button', 'action-button'); updateToggleButtonStyle(toggleButton); const handleToggleChange = () => { readQuotesOnly = !readQuotesOnly; console.log('Read Quotes Only:', readQuotesOnly); toggleSwitches.forEach(btn => { btn.innerText = readQuotesOnly ? 'Quotes' : 'All'; updateToggleButtonStyle(btn); }); }; toggleButton.addEventListener('click', handleToggleChange); toggleSwitches.push(toggleButton); ttsButton.addEventListener('click', () => { if (currentAudio && !currentAudio.paused) { currentAudio.pause(); currentAudio.currentTime = 0; console.log('Audio stopped'); updateTTSButtonStyle(ttsButton, false); } else { console.log('TTS button clicked'); let messageText = message.querySelector('.mes_text').innerText; if (readQuotesOnly) { const quotes = messageText.match(/“([^”]*)”/g); messageText = quotes ? quotes.map(quote => quote.replace(/“|”/g, '')).join(' ') : ''; } console.log('TTS for:', messageText); playTTS(messageText, ttsButton); } }); const buttonsContainer = message.querySelector('.mes_buttons'); if (buttonsContainer) { buttonsContainer.appendChild(toggleButton); buttonsContainer.appendChild(ttsButton); console.log('TTS/Stop button and Toggle button added'); } } } // Function to update the toggle button style function updateToggleButtonStyle(button) { if (readQuotesOnly) { button.style.backgroundColor = 'green'; button.style.color = 'white'; } else { button.style.backgroundColor = 'white'; button.style.color = 'black'; } } // Function to update the TTS button style function updateTTSButtonStyle(button, isPlaying) { if (isPlaying) { button.innerText = 'Stop'; button.style.backgroundColor = 'green'; button.style.color = 'white'; } else { button.innerText = 'TTS'; button.style.backgroundColor = 'white'; button.style.color = 'black'; } } // Function to send text to TTS API and play response function playTTS(text, ttsButton) { console.log('Sending text to TTS API:', text); const apiUrl = `${apiDomain}/voice/bert-vits2?text=${encodeURIComponent(text)}&id=0&noise=0.5&noisew=0.5`; GM_xmlhttpRequest({ method: 'GET', url: apiUrl, responseType: 'blob', // Expecting a binary response onload: function(response) { // Convert the response Blob to a URL const audioUrl = URL.createObjectURL(response.response); if (currentAudio) { currentAudio.pause(); currentAudio.currentTime = 0; } currentAudio = new Audio(audioUrl); currentAudio.play().catch(e => console.error('Audio play error:', e)); currentAudio.onplay = () => updateTTSButtonStyle(ttsButton, true); currentAudio.onpause = () => updateTTSButtonStyle(ttsButton, false); currentAudio.onended = () => updateTTSButtonStyle(ttsButton, false); }, onerror: function(error) { console.error('TTS API Error:', error); } }); } // Function to add the settings button to the chat container function addSettingsButton() { const chatContainer = document.querySelector('#chat'); if (chatContainer && !chatContainer.querySelector('.settings-button')) { const settingsButton = document.createElement('button'); settingsButton.innerText = 'TTS API Setting'; settingsButton.classList.add('settings-button', 'action-button'); settingsButton.addEventListener('click', () => { const newDomain = prompt('Enter TTS API Domain:', apiDomain); if (newDomain) { apiDomain = newDomain; localStorage.setItem('ttsApiDomain', apiDomain); console.log('API Domain updated to:', apiDomain); } }); chatContainer.appendChild(settingsButton); } } // Adding CSS styles for the buttons const style = document.createElement('style'); style.innerHTML = ` .action-button { background-color: gray; color: white; border: none; cursor: pointer; } .action-button:hover { background-color: lightgray; } .mytts-button.playing { background-color: green; } .settings-button { margin-bottom: 10px; } `; document.head.appendChild(style); // Using MutationObserver to dynamically add buttons to new messages and the settings button const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.addedNodes) { mutation.addedNodes.forEach(node => { if (node.classList && node.classList.contains('mes')) { addTTSButton(node); } if (node.id === 'chat') { addSettingsButton(); } }); } }); }); // Start observing const config = { childList: true, subtree: true }; observer.observe(document.body, config); // Initial settings button addition addSettingsButton(); })();