您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Read selected text with OpenAI's TTS API (gpt-4o-mini-tts model) and adjustable volume and speed. Please enter the apikey before using.
// ==UserScript== // @name OpenAI TTS Text Reader (gpt-4o-mini-tts) // @namespace http://tampermonkey.net/ // @version 2.6.6 // Version incremented due to model change // @description Read selected text with OpenAI's TTS API (gpt-4o-mini-tts model) and adjustable volume and speed. Please enter the apikey before using. // @description:ar قراءة النص المحدد باستخدام واجهة برمجة تطبيقات تحويل النص إلى كلام من OpenAI (نموذج gpt-4o-mini-tts) مع إمكانية ضبط مستوى الصوت والسرعة. يرجى إدخال مفتاح الواجهة البرمجية (apikey) قبل الاستخدام. // @include * // @author wkf16 (Modified by AI assistant for Bahattab) // @license MIT // @grant GM_xmlhttpRequest // @connect api.openai.com // @antifeature cross-domain This script makes cross-domain API calls to OpenAI's TTS service, which may have implications for data security and privacy. // ==/UserScript== var YOUR_API_KEY = "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"; // استخدم مفتاح الواجهة البرمجية الخاص بك (function() { 'use strict'; var currentSource = null; var isPlaying = false; var audioContext = new AudioContext(); var gainNode = audioContext.createGain(); gainNode.connect(audioContext.destination); var playbackRate = 1; // إنشاء الزر var readButton = document.createElement("button"); styleButton(readButton); document.body.appendChild(readButton); // إنشاء وإضافة نص الزر var buttonText = document.createElement("span"); buttonText.textContent = ">"; styleButtonText(buttonText); readButton.appendChild(buttonText); // إنشاء لوحة التحكم var controlPanel = document.createElement("div"); styleControlPanel(controlPanel); document.body.appendChild(controlPanel); // إنشاء وإضافة منزلقات مستوى الصوت والسرعة إلى لوحة التحكم var volumeControl = createSlider("الصوت", 0, 1, 0.5, 0.01, function(value) { gainNode.gain.value = value; }); controlPanel.appendChild(volumeControl.wrapper); volumeControl.slider.value = 0.5; var speedControl = createSlider("السرعة", 0.5, 1.5, 1, 0.05, function(value) { playbackRate = value; }); controlPanel.appendChild(speedControl.wrapper); speedControl.slider.value = 1; // حدث النقر على الزر readButton.addEventListener('click', function() { var selectedText = window.getSelection().toString(); console.log("Setting gainNode.gain.value to: ", gainNode.gain.value); if (isPlaying) { if (currentSource) { currentSource.stop(); // إيقاف الصوت قيد التشغيل حاليًا } HideSpinner(buttonText); // Reset button to play state isPlaying = false; // Ensure state is updated } else{ if (selectedText) { textToSpeech(selectedText); } else { alert("رجاءً، قم بتحديد بعض النص أولاً."); } } }); // إنشاء وتنسيق لوحة التحكم والمنزلقات function createSlider(labelText, min, max, value, step, onChange) { var wrapper = document.createElement("div"); var label = document.createElement("label"); label.textContent = labelText; label.style.color = "white"; label.style.textAlign = "right"; label.style.flex = "1"; var slider = document.createElement("input"); slider.type = "range"; slider.min = min; slider.max = max; slider.step = step; wrapper.style.display = 'flex'; wrapper.style.alignItems = 'center'; wrapper.style.padding = '8px'; var styleSheet = document.createElement("style"); styleSheet.type = "text/css"; styleSheet.innerText = ` input[type='range'] { -webkit-appearance: none; appearance: none; width: 90%; height: 8px; border-radius: 8px; background: rgba(255, 255, 255, 0.2); outline: none; margin-right: 10px; margin-left: 0; } input[type='range']::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 16px; height: 16px; border-radius: 50%; background: #4CAF50; cursor: pointer; box-shadow: 0 0 2px #888; } input[type='range']:focus::-webkit-slider-thumb { background: #ccc; } `; document.head.appendChild(styleSheet); slider.oninput = function() { onChange(this.value); }; wrapper.appendChild(label); wrapper.appendChild(slider); return { wrapper: wrapper, slider: slider }; } // تعيين نمط لوحة التحكم function styleControlPanel(panel) { panel.style.position = 'fixed'; panel.style.bottom = '20px'; panel.style.right = '80px'; panel.style.width = '200px'; panel.style.background = 'rgba(0, 0, 0, 0.7)'; panel.style.borderRadius = '10px'; panel.style.padding = '10px'; panel.style.boxSizing = 'border-box'; panel.style.visibility = 'hidden'; panel.style.opacity = 0; panel.style.transition = 'opacity 0.5s, visibility 0.5s'; panel.style.display = 'flex'; panel.style.flexDirection = 'column'; panel.style.zIndex = '10000'; } // تعيين نمط الزر function styleButton(button) { button.style.position = 'fixed'; button.style.bottom = '80px'; button.style.right = '20px'; button.style.zIndex = '1000'; button.style.width = '40px'; button.style.height = '40px'; button.style.borderRadius = '50%'; button.style.backgroundColor = '#4CAF50'; button.style.border = 'none'; button.style.outline = 'none'; button.style.cursor = 'pointer'; button.style.transition = 'background-color 0.3s, opacity 0.4s ease'; } function styleButtonText(text) { text.style.transition = 'opacity 0.4s ease'; text.style.opacity = '1'; text.style.fontSize = "20px"; text.style.textAlign = "center"; text.style.lineHeight = "40px"; } function createVoiceSelect() { var selectWrapper = document.createElement("div"); var select = document.createElement("select"); var voices = ["nova", "onyx", "alloy", "echo", "fable", "shimmer"]; var voiceLabel = document.createElement("label"); voiceLabel.textContent = "الصوت:"; voiceLabel.style.color = "white"; voiceLabel.style.marginRight = "5px"; selectWrapper.appendChild(voiceLabel); for (var i = 0; i < voices.length; i++) { var option = document.createElement("option"); option.value = voices[i]; option.textContent = voices[i].charAt(0).toUpperCase() + voices[i].slice(1); select.appendChild(option); } selectWrapper.appendChild(select); styleSelect(selectWrapper, select); return { wrapper: selectWrapper, select: select }; } // تنسيق القائمة المنسدلة function styleSelect(wrapper, select) { wrapper.style.padding = '5px'; wrapper.style.marginBottom = '10px'; wrapper.style.display = 'flex'; wrapper.style.alignItems = 'center'; select.style.flexGrow = '1'; select.style.padding = '8px 10px'; select.style.borderRadius = '8px'; select.style.background = 'rgba(0, 0, 0, 0.7)'; select.style.border = '2px solid #4CAF50'; select.style.color = 'white'; select.style.fontFamily = 'Arial, sans-serif'; select.style.fontSize = '14px'; select.style.direction = 'ltr'; select.onmouseover = function() { this.style.backgroundColor = 'rgba(50, 50, 50, 0.5)'; }; select.onmouseout = function() { this.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; }; select.onfocus = function() { this.style.outline = 'none'; this.style.boxShadow = '0 0 5px rgba(81, 203, 238, 1)'; }; var styleSheet = document.createElement("style"); styleSheet.type = "text/css"; styleSheet.innerText = ` select option { background: rgba(0, 0, 0, 0.9); color: white; } select option:checked { background: #4CAF50; color: white; } select option:hover { background: rgba(50, 50, 50, 0.8); color: white; } `; document.head.appendChild(styleSheet); } // إضافة القائمة المنسدلة لاختيار الصوت إلى لوحة التحكم var voiceSelect = createVoiceSelect(); controlPanel.appendChild(voiceSelect.wrapper); function textToSpeech(s) { // ----- السطر الذي تم تعديله ----- var sModelId = "gpt-4o-mini-tts"; // Changed from "tts-1" to "gpt-4o-mini-tts" for higher quality // ----- نهاية السطر المعدل ----- var sVoiceId = voiceSelect.select.value; var API_KEY = YOUR_API_KEY; // Make sure this is set correctly at the top // Ensure API Key is present if (!API_KEY || API_KEY === "YOUR_API_KEY_HERE" || API_KEY.length < 10) { alert("يرجى إدخال مفتاح واجهة برمجة تطبيقات OpenAI صالح في بداية البرنامج النصي."); HideSpinner(buttonText); return; } ShowSpinner(buttonText); // إظهار مؤشر التحميل 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, // Now uses "gpt-4o-mini-tts" input: s, voice: sVoiceId, speed: playbackRate }), responseType: "arraybuffer", onload: function(response) { if (response.status === 200) { // Hide spinner isn't needed here, StopSpinner handles the transition audioContext.decodeAudioData(response.response, function(buffer) { var source = audioContext.createBufferSource(); source.buffer = buffer; source.connect(gainNode); source.start(0); currentSource = source; // حفظ مصدر الصوت الجديد isPlaying = true; StopSpinner(buttonText); // تحديث نص الزر إلى حالة الإيقاف المؤقت // الاستماع لحدث انتهاء الصوت source.onended = function() { isPlaying = false; currentSource = null; // Clear the source HideSpinner(buttonText); // تحديث نص الزر إلى حالة التشغيل } }, function(e) { console.error("Error decoding audio data: ", e); HideSpinner(buttonText); // Ensure spinner hides on decode error alert("حدث خطأ أثناء معالجة الصوت."); }); } else { HideSpinner(buttonText); console.error("Error loading TTS: ", response.status, response.statusText, response.response); try { var errorResponse = JSON.parse(new TextDecoder("utf-8").decode(response.response)); console.error("OpenAI Error:", errorResponse); // Check for specific common errors if (response.status === 401) { alert("خطأ في المصادقة (401). يرجى التحقق من مفتاح الواجهة البرمجية (API Key)."); } else if (errorResponse.error?.message) { alert("خطأ من OpenAI: " + errorResponse.error.message); } else { alert("حدث خطأ أثناء الاتصال بخدمة تحويل النص إلى كلام. الرمز: " + response.status); } } catch (e) { alert("حدث خطأ أثناء الاتصال بخدمة تحويل النص إلى كلام. الرمز: " + response.status); } } }, onerror: function(error) { HideSpinner(buttonText); console.error("GM_xmlhttpRequest error: ", error); alert("حدث خطأ في الشبكة أو في طلب الواجهة البرمجية."); } }); } // تأخير عرض وإخفاء لوحة التحكم var panelDisplayDelay = 700; var panelHideDelay = 500; var showPanelTimeout, hidePanelTimeout; readButton.addEventListener('mouseenter', function() { readButton.style.backgroundColor = '#45a049'; clearTimeout(hidePanelTimeout); showPanelTimeout = setTimeout(function() { controlPanel.style.visibility = 'visible'; controlPanel.style.opacity = 1; }, panelDisplayDelay); }); readButton.addEventListener('mouseleave', function() { readButton.style.backgroundColor = '#4CAF50'; clearTimeout(showPanelTimeout); hidePanelTimeout = setTimeout(function() { if (!controlPanel.matches(':hover')) { controlPanel.style.visibility = 'hidden'; controlPanel.style.opacity = 0; } }, panelHideDelay); }); controlPanel.addEventListener('mouseenter', function() { clearTimeout(hidePanelTimeout); controlPanel.style.visibility = 'visible'; controlPanel.style.opacity = 1; }); controlPanel.addEventListener('mouseleave', function() { hidePanelTimeout = setTimeout(function() { controlPanel.style.visibility = 'hidden'; controlPanel.style.opacity = 0; }, panelHideDelay); }); speedControl.slider.addEventListener('input', function() { playbackRate = this.value; }); function ShowSpinner(text) { text.style.opacity = '0'; setTimeout(function() { text.textContent = "..."; text.style.opacity = '1'; }, 400); readButton.disabled = true; } function HideSpinner(text) { // Resets button to 'Play' state text.style.opacity = '0'; setTimeout(function() { text.textContent = ">"; text.style.opacity = '1'; }, 400); readButton.disabled = false; } function StopSpinner(text) { // Sets button to 'Stop' state text.style.opacity = '0'; setTimeout(function() { text.textContent = "❚❚"; text.style.opacity = '1'; }, 400); readButton.disabled = false; // Keep button enabled to allow stopping } })();