Read aloud the current article in FreshRSS or the text from a webpage using a custom TTS API
当前为
// ==UserScript==
// @name RSS: FreshRSS Read Aloud
// @namespace http://tampermonkey.net/
// @version 2.9
// @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
// ==/UserScript==
(function() {
'use strict';
// 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=10000&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';
container.style.cssText = 'position: fixed; top: 15px; left: 10px; z-index: 9999;';
const audioPlayer = document.createElement('audio');
audioPlayer.id = 'tts-audio';
audioPlayer.controls = true;
audioPlayer.style.cssText = 'width: 50px; height: 20px; opacity: 0.3;';
audioPlayer.innerHTML = 'Your browser does not support the audio element.';
let 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', () => {
if (audioPlayer.dataset.blobUrl) {
URL.revokeObjectURL(audioPlayer.dataset.blobUrl);
delete audioPlayer.dataset.blobUrl;
}
audioPlayer.src = '';
isInitialPlay = true;
});
container.appendChild(audioPlayer);
document.body.appendChild(container);
}
// Initialize the audio player
initializeAudioPlayer();
})();