您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Read aloud the current article in FreshRSS or the text from a webpage using a custom TTS API
当前为
// ==UserScript== // @name RSS: FreshRSS or Webpage Read Aloud // @namespace http://tampermonkey.net/ // @version 2.2 // @description Read aloud the current article in FreshRSS or the text from a webpage using a custom TTS API // @author Your Name // @match http://192.168.1.2:1030/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // ==/UserScript== // @match *://*/* (function() { 'use strict'; // Add minimal styles for the audio element GM_addStyle(` #tts-audio-container { position: fixed; bottom: 75px; left: 20px; z-index: 1000; display: flex; align-items: center; gap: 0; } #tts-audio { width: 250px; height: 20px; background: transparent; } #tts-audio::-webkit-media-controls-panel { background: white; } #tts-audio::-webkit-media-controls-timeline { cursor: pointer; } #tts-clear-button { height: 20px; width: 24px; padding: 0; background: white; border: 1px solid #ddd; border-left: none; border-radius: 0 2px 2px 0; cursor: pointer; font-size: 12px; color: #666; display: flex; align-items: center; justify-content: center; } #tts-clear-button:hover { background: #f0f0f0; color: #333; } #tts-clear-button:active { background: #e0e0e0; } `); // Function to extract text from the webpage or FreshRSS article function extractText() { const isFreshRSS = document.querySelector('.flux_content') !== null; if (isFreshRSS) { const articleContent = document.querySelector('.flux.active.current .flux_content .text'); if (articleContent) { let text = articleContent.innerText.trim(); // Remove "Summarize" from the beginning and end if (text.startsWith('Summarize')) { text = text.substring('Summarize'.length).trim(); } if (text.endsWith('Summarize')) { text = text.substring(0, text.length - 'Summarize'.length).trim(); } return text; } } else { const elementsToRemove = document.querySelectorAll('script, style'); elementsToRemove.forEach(el => el.remove()); return document.body.innerText.trim(); } return null; } // Function to fetch and set audio source async function fetchAudioSource() { const text = extractText(); if (!text) { throw new Error('No text content found on the webpage.'); } const audioPlayer = document.getElementById('tts-audio'); const apiUrl = `http://192.168.1.2:1202/api/tts?download=true&shardLength=500&thread=500&text=${encodeURIComponent(text)}`; return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', apiUrl, true); xhr.responseType = 'blob'; xhr.onload = function() { if (xhr.status === 200) { const blob = new Blob([xhr.response], { type: 'audio/mpeg' }); const url = URL.createObjectURL(blob); // Clean up old URL if it exists if (audioPlayer.dataset.blobUrl) { URL.revokeObjectURL(audioPlayer.dataset.blobUrl); } audioPlayer.dataset.blobUrl = url; audioPlayer.src = url; resolve(); } else { reject(new Error(`HTTP error! status: ${xhr.status}`)); } }; xhr.onerror = () => reject(new Error('Network request failed')); xhr.send(); }); } // Initialize the audio player function initializeAudioPlayer() { const container = document.createElement('div'); container.id = 'tts-audio-container'; const audioPlayer = document.createElement('audio'); audioPlayer.id = 'tts-audio'; audioPlayer.controls = true; audioPlayer.innerHTML = 'Your browser does not support the audio element.'; const clearButton = document.createElement('button'); clearButton.id = 'tts-clear-button'; clearButton.innerHTML = '✕'; clearButton.title = 'Clear audio'; let isInitialPlay = true; clearButton.addEventListener('click', () => { audioPlayer.pause(); if (audioPlayer.dataset.blobUrl) { URL.revokeObjectURL(audioPlayer.dataset.blobUrl); delete audioPlayer.dataset.blobUrl; } isInitialPlay = true; }); audioPlayer.addEventListener('play', async (e) => { if (isInitialPlay) { e.preventDefault(); audioPlayer.pause(); try { await fetchAudioSource(); isInitialPlay = false; audioPlayer.play(); } catch (error) { console.error('Error fetching audio:', error); isInitialPlay = true; } } }); audioPlayer.addEventListener('ended', () => { isInitialPlay = true; }); container.appendChild(audioPlayer); container.appendChild(clearButton); document.body.appendChild(container); } // Initialize the audio player initializeAudioPlayer(); })();