OpenAI TTS Text Reader with Floating Button

Read selected text with OpenAI's TTS API

目前為 2023-11-20 提交的版本,檢視 最新版本

// ==UserScript==
// @name         OpenAI TTS Text Reader with Floating Button
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Read selected text with OpenAI's TTS API
// @include      *
// @grant        GM_xmlhttpRequest
// @license MIT
// ==/UserScript==


(function() {
    'use strict';
    // 创建一个悬浮的朗读按钮并添加到页面上
    // 创建一个悬浮的朗读按钮并添加到页面上
    var readButton = document.createElement("button");
    var textSpan = document.createElement("span");
    textSpan.textContent = ">"; // 初始文本
    readButton.appendChild(textSpan); // 将文本span添加到按钮中

    // 设置按钮样式
    readButton.style.position = "fixed";
    readButton.style.bottom = "20px";
    readButton.style.right = "20px";
    readButton.style.zIndex = "1000";
    readButton.style.width = "40px";  // 设置按钮宽度为40像素
    readButton.style.height = "40px"; // 设置按钮高度为40像素
    readButton.style.borderRadius = "50%"; // 设置按钮为圆形
    readButton.style.backgroundColor = "#4CAF50";
    readButton.style.border = "none";
    readButton.style.cursor = "pointer";

    // 设置文本span的样式
    textSpan.style.color = "white"; // 设置文本颜色为白色
    textSpan.style.fontSize = "20px"; // 设置字体大小
    textSpan.style.textAlign = "center"; // 文本居中
    textSpan.style.lineHeight = "40px"; // 设置行高以垂直居中文本
    textSpan.style.transition = "opacity 0.4s ease"; // 设置文本的透明度过渡

    // 添加按下动画的样式
    readButton.style.transition = "transform 0.1s ease";
    readButton.addEventListener('mousedown', function() {
        this.style.transform = "scale(0.9)";
    });
    readButton.addEventListener('mouseup', function() {
        this.style.transform = "scale(1)";
    });
    readButton.addEventListener('mouseleave', function() {
        this.style.transform = "scale(1)";
    });

    // 将按钮添加到页面
    document.body.appendChild(readButton);
/*
    // 创建一个悬浮的朗读按钮并添加到页面上
    var readButton = document.createElement("button");
    readButton.textContent = "朗读选中文本";
    readButton.style.position = "fixed";
    readButton.style.bottom = "20px";
    readButton.style.right = "20px";
    readButton.style.zIndex = 1000;
    readButton.style.padding = "10px";
    readButton.style.borderRadius = "5px";
    readButton.style.backgroundColor = "#4CAF50";
    readButton.style.color = "white";
    readButton.style.border = "none";
    readButton.style.cursor = "pointer";
    document.body.appendChild(readButton);
*/
    // 按钮点击事件
    readButton.addEventListener('click', function() {
        var selectedText = window.getSelection().toString();
        if (selectedText) {
            TextToSpeech(selectedText);
        } else {
            alert("请先选择一些文本。");
        }
    });

    function TextToSpeech(s) {
        var sModelId = "tts-1";
        var sVoiceId = "nova";
        var API_KEY = "YOUR_API_KEY"; // 替换为你的 OpenAI API Key

        ShowSpinner();

        GM_xmlhttpRequest({
            method: "POST",
            url: "https://api.openai.com/v1/audio/speech",
            headers: {
                "Accept": "audio/mpeg",
                "Content-Type": "application/json",
                "Authorization": "Bearer " + API_KEY
            },
            data: JSON.stringify({
                model: sModelId,
                input: s,
                voice: sVoiceId
            }),
            responseType: "arraybuffer",

            onload: function (response) {
                HideSpinner();
                if (response.status === 200) {
                    var audioContext = new AudioContext();
                    audioContext.decodeAudioData(response.response, function(buffer) {
                        var source = audioContext.createBufferSource();
                        source.buffer = buffer;
                        source.connect(audioContext.destination);
                        source.start(0);
                    }, function(e) {
                        console.log("Audio error! ", e);
                    });
                }
            }
        });
    }


function ShowSpinner() {
    // 淡出文本
    textSpan.style.opacity = '0';
    setTimeout(function() {
        textSpan.textContent = "..."; // 更改文本
        textSpan.style.opacity = '1';
    }, 400); // 等待文本完全淡出后更改
    readButton.disabled = true; // 禁用按钮以防止重复点击
}

function HideSpinner() {
    // 淡出文本
    textSpan.style.opacity = '0';
    setTimeout(function() {
        textSpan.textContent = ">"; // 恢复原始文本
        textSpan.style.opacity = '1';
    }, 400); // 等待文本完全淡出后更改
    readButton.disabled = false; // 启用按钮
}
})();