您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
调用第三方站点api将选中的文本或输入的文本转换为语音并下载
当前为
// ==UserScript== // @name OpenAI TTS API Converter // @namespace http://tampermonkey.net/ // @version 2.1 // @description 调用第三方站点api将选中的文本或输入的文本转换为语音并下载 // @author finch // @match *://*/* // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @license MIT // ==/UserScript== (function() { 'use strict'; // 加载 Font Awesome CSS const fontAwesomeLink = document.createElement('link'); fontAwesomeLink.rel = 'stylesheet'; fontAwesomeLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css'; document.head.appendChild(fontAwesomeLink); // 获取用户配置 function getUserConfig() { return { bearerToken: localStorage.getItem('tts_bearerToken') || '', postUrl: localStorage.getItem('tts_postUrl') || '', model: localStorage.getItem('tts_model') || '', voice: localStorage.getItem('tts_voice') || '' }; } // 设置用户配置 function setUserConfig(config) { localStorage.setItem('tts_bearerToken', config.bearerToken); localStorage.setItem('tts_postUrl', config.postUrl); localStorage.setItem('tts_model', config.model); localStorage.setItem('tts_voice', config.voice); } // 创建设置对话框 function createSettingsDialog() { const dialog = document.createElement('div'); dialog.style.position = 'fixed'; dialog.style.top = '50%'; dialog.style.left = '50%'; dialog.style.transform = 'translate(-50%, -50%)'; dialog.style.backgroundColor = 'white'; dialog.style.padding = '20px'; dialog.style.boxShadow = '0 4px 15px rgba(0, 0, 0, 0.3)'; dialog.style.zIndex = 10000; dialog.style.display = 'none'; dialog.style.borderRadius = '8px'; dialog.style.maxWidth = '400px'; dialog.style.width = '100%'; const config = getUserConfig(); const title = document.createElement('h2'); title.textContent = '设置 TTS API 配置'; title.style.marginTop = '0'; title.style.marginBottom = '20px'; title.style.textAlign = 'center'; dialog.appendChild(title); const form = document.createElement('form'); form.style.display = 'flex'; form.style.flexDirection = 'column'; form.style.gap = '10px'; const createInputField = (labelText, inputValue) => { const label = document.createElement('label'); label.textContent = labelText; label.style.fontWeight = 'bold'; const input = document.createElement('input'); input.type = 'text'; input.value = inputValue; input.style.width = '100%'; input.style.padding = '8px'; input.style.border = '1px solid #ccc'; input.style.borderRadius = '4px'; label.appendChild(input); return { label, input }; }; const bearerTokenField = createInputField('Bearer 令牌:', config.bearerToken); const postUrlField = createInputField('POST URL:', config.postUrl); const modelField = createInputField('模型名称:', config.model); const voiceField = createInputField('声音名称:', config.voice); form.appendChild(bearerTokenField.label); form.appendChild(postUrlField.label); form.appendChild(modelField.label); form.appendChild(voiceField.label); const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.justifyContent = 'space-between'; buttonContainer.style.marginTop = '20px'; const saveButton = document.createElement('button'); saveButton.textContent = '保存'; saveButton.style.backgroundColor = '#4CAF50'; saveButton.style.color = 'white'; saveButton.style.border = 'none'; saveButton.style.padding = '10px 20px'; saveButton.style.borderRadius = '4px'; saveButton.style.cursor = 'pointer'; saveButton.addEventListener('click', (e) => { e.preventDefault(); setUserConfig({ bearerToken: bearerTokenField.input.value, postUrl: postUrlField.input.value, model: modelField.input.value, voice: voiceField.input.value }); dialog.style.display = 'none'; }); const cancelButton = document.createElement('button'); cancelButton.textContent = '取消'; cancelButton.style.backgroundColor = '#f44336'; cancelButton.style.color = 'white'; cancelButton.style.border = 'none'; cancelButton.style.padding = '10px 20px'; cancelButton.style.borderRadius = '4px'; cancelButton.style.cursor = 'pointer'; cancelButton.addEventListener('click', (e) => { e.preventDefault(); dialog.style.display = 'none'; }); buttonContainer.appendChild(saveButton); buttonContainer.appendChild(cancelButton); form.appendChild(buttonContainer); // 添加测试按钮 const testButton = document.createElement('button'); testButton.textContent = '点此测试服务'; testButton.style.marginTop = '10px'; testButton.style.backgroundColor = '#2196F3'; testButton.style.color = 'white'; testButton.style.border = 'none'; testButton.style.padding = '10px 20px'; testButton.style.borderRadius = '4px'; testButton.style.cursor = 'pointer'; testButton.addEventListener('click', (e) => { e.preventDefault(); testTTSAPI(); }); form.appendChild(testButton); dialog.appendChild(form); document.body.appendChild(dialog); return dialog; } const settingsDialog = createSettingsDialog(); // 配置菜单 GM_registerMenuCommand('设置 TTS API 配置', () => { settingsDialog.style.display = 'block'; }); // 测试 TTS API function testTTSAPI() { const config = getUserConfig(); const data = { model: config.model, input: 'Hello, world', voice: config.voice }; GM_xmlhttpRequest({ method: 'POST', url: config.postUrl, headers: { 'Authorization': `Bearer ${config.bearerToken}`, 'Content-Type': 'application/json' }, data: JSON.stringify(data), responseType: 'blob', onload: function(response) { if (response.status === 200) { alert('TTS API 连通性测试成功!'); } else { alert('TTS API 连通性测试失败,请检查配置。'); } } }); } // 将文本转换为语音并下载 function convertTextToSpeech(text, download = false) { const config = getUserConfig(); const data = { model: config.model, input: text, voice: config.voice }; GM_xmlhttpRequest({ method: 'POST', url: config.postUrl, headers: { 'Authorization': `Bearer ${config.bearerToken}`, 'Content-Type': 'application/json' }, data: JSON.stringify(data), responseType: 'blob', onload: function(response) { if (response.status === 200) { const url = URL.createObjectURL(response.response); if (download) { const downloadLink = document.createElement('a'); downloadLink.href = url; downloadLink.download = 'tts_audio.mp3'; // 默认文件名 downloadLink.click(); } else { const audio = new Audio(url); audio.play(); // 显示下载按钮 downloadButton.style.display = 'block'; downloadButton.onclick = () => { const downloadLink = document.createElement('a'); downloadLink.href = url; downloadLink.download = 'tts_audio.mp3'; downloadLink.click(); }; } } else { console.error(`请求失败,状态码:${response.status}`); } } }); } // 创建按钮容器 const buttonContainer = document.createElement('div'); buttonContainer.style.position = 'absolute'; buttonContainer.style.zIndex = 1000; buttonContainer.style.display = 'flex'; buttonContainer.style.gap = '8px'; // 按钮间距 buttonContainer.style.display = 'none'; // 初始隐藏 document.body.appendChild(buttonContainer); // 按钮样式 const buttonStyle = { width: '40px', height: '40px', borderRadius: '50%', border: 'none', cursor: 'pointer', fontSize: '1.5em', color: 'white' }; // 创建转换语音按钮 const playButton = document.createElement('button'); playButton.innerHTML = '<i class="fas fa-play"></i>'; // 使用 Font Awesome 播放图标 Object.assign(playButton.style, buttonStyle, { backgroundColor: '#4CAF50' }); playButton.addEventListener('click', () => { const selection = window.getSelection(); const text = selection.toString(); if (text) { convertTextToSpeech(text); } else { alert('没有选中文本'); } }); buttonContainer.appendChild(playButton); // 创建下载按钮 const downloadButton = document.createElement('button'); downloadButton.innerHTML = '<i class="fas fa-download"></i>'; // 使用 Font Awesome 下载图标 Object.assign(downloadButton.style, buttonStyle, { backgroundColor: '#2196F3' }); downloadButton.style.display = 'none'; // 初始隐藏 buttonContainer.appendChild(downloadButton); // 创建输入文本按钮 const inputButton = document.createElement('button'); inputButton.innerHTML = '<i class="fas fa-file-alt"></i>'; // 使用 Font Awesome 纸张图标 Object.assign(inputButton.style, buttonStyle, { backgroundColor: '#FF9800' }); inputButton.addEventListener('click', () => { inputDialog.style.display = 'block'; }); buttonContainer.appendChild(inputButton); // 创建文本输入框页面 const inputDialog = document.createElement('div'); inputDialog.style.position = 'fixed'; inputDialog.style.top = '50%'; inputDialog.style.left = '50%'; inputDialog.style.transform = 'translate(-50%, -50%)'; inputDialog.style.backgroundColor = 'white'; inputDialog.style.padding = '20px'; inputDialog.style.boxShadow = '0 4px 15px rgba(0, 0, 0, 0.3)'; inputDialog.style.zIndex = 10000; inputDialog.style.display = 'none'; inputDialog.style.borderRadius = '8px'; inputDialog.style.maxWidth = '400px'; inputDialog.style.width = '100%'; const inputTitle = document.createElement('h2'); inputTitle.textContent = '输入文本进行转换'; inputTitle.style.marginTop = '0'; inputTitle.style.marginBottom = '20px'; inputTitle.style.textAlign = 'center'; inputDialog.appendChild(inputTitle); const inputText = document.createElement('input'); inputText.type = 'text'; inputText.placeholder = '输入文本'; inputText.style.padding = '10px'; inputText.style.border = '1px solid #ccc'; inputText.style.borderRadius = '4px'; inputText.style.width = '100%'; inputText.style.marginBottom = '20px'; inputDialog.appendChild(inputText); const inputButtonContainer = document.createElement('div'); inputButtonContainer.style.display = 'flex'; inputButtonContainer.style.justifyContent = 'space-between'; const playInputButton = document.createElement('button'); playInputButton.textContent = '播放'; playInputButton.style.backgroundColor = '#4CAF50'; playInputButton.style.color = 'white'; playInputButton.style.border = 'none'; playInputButton.style.padding = '10px 20px'; playInputButton.style.borderRadius = '4px'; playInputButton.style.cursor = 'pointer'; playInputButton.addEventListener('click', () => { const text = inputText.value.trim(); if (text) { convertTextToSpeech(text); } else { alert('请输入文本'); } }); const inputDownloadButton = document.createElement('button'); inputDownloadButton.textContent = '下载'; inputDownloadButton.style.backgroundColor = '#4CAF50'; inputDownloadButton.style.color = 'white'; inputDownloadButton.style.border = 'none'; inputDownloadButton.style.padding = '10px 20px'; inputDownloadButton.style.borderRadius = '4px'; inputDownloadButton.style.cursor = 'pointer'; inputDownloadButton.addEventListener('click', () => { const text = inputText.value.trim(); if (text) { convertTextToSpeech(text, true); } else { alert('请输入文本'); } }); const cancelInputButton = document.createElement('button'); cancelInputButton.textContent = '取消'; cancelInputButton.style.backgroundColor = '#f44336'; cancelInputButton.style.color = 'white'; cancelInputButton.style.border = 'none'; cancelInputButton.style.padding = '10px 20px'; cancelInputButton.style.borderRadius = '4px'; cancelInputButton.style.cursor = 'pointer'; cancelInputButton.addEventListener('click', () => { inputDialog.style.display = 'none'; }); inputButtonContainer.appendChild(playInputButton); inputButtonContainer.appendChild(inputDownloadButton); inputButtonContainer.appendChild(cancelInputButton); inputDialog.appendChild(inputButtonContainer); document.body.appendChild(inputDialog); // 监听文本选择事件 document.addEventListener('selectionchange', () => { const selection = window.getSelection(); if (selection.rangeCount > 0) { const range = selection.getRangeAt(0); const rect = range.getBoundingClientRect(); if (rect.width > 0 && rect.height > 0) { buttonContainer.style.top = `${rect.bottom + window.scrollY}px`; buttonContainer.style.left = `${rect.right + window.scrollX}px`; buttonContainer.style.display = 'flex'; } else { buttonContainer.style.display = 'none'; downloadButton.style.display = 'none'; // 隐藏下载按钮 } } else { buttonContainer.style.display = 'none'; downloadButton.style.display = 'none'; // 隐藏下载按钮 } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址