您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Exibe dois botões, um deles é um ícone de Cronômetro para a Velocidade e o outro é uma Engrenagem para a Qualidade de Vídeo. A Qualidade e Velocidade escolhidas são salvas e usadas automaticamente nos próximos vídeos.
// ==UserScript== // @name Botão Duplo de Velocidade/Qualidade para o YouTube. // @namespace https://greasyfork.org/pt-BR/scripts/543571-modified // @version 3.2.3 // @description Exibe dois botões, um deles é um ícone de Cronômetro para a Velocidade e o outro é uma Engrenagem para a Qualidade de Vídeo. A Qualidade e Velocidade escolhidas são salvas e usadas automaticamente nos próximos vídeos. // @author matheus felipen // @match *://*.youtube.com/* // @grant none // @license GNU GPLv3 // @icon https://i.ibb.co/WNzVD92X/cronometro.png // ==/UserScript== (function () { 'use strict'; const texts = { pt: { speedLabel: '⏱️ Velocidade', speedMsg: v => `Velocidade: ${v.toFixed(2)}x`, quality: '⚙️ Qualidade', qualityMsg: q => `Qualidade: ${q}` }, en: { speedLabel: '⏱️ Speed', speedMsg: v => `Speed: ${v.toFixed(2)}x`, quality: '⚙️ Quality', qualityMsg: q => `Quality: ${q}` } }; const lang = (navigator.language || 'pt').slice(0, 2); const t = texts[lang] || texts.pt; const styles = { button: { backgroundColor: 'rgba(0,0,0,0.7)', color: 'white', border: 'none', padding: '10px', borderRadius: '5px', cursor: 'pointer', fontSize: '16px', minWidth: '85px', width: '100%', textAlign: 'center', boxSizing: 'border-box' }, dropdown: { position: 'absolute', right: '100%', bottom: '0', backgroundColor: 'rgba(0, 0, 0, 0.9)', padding: '5px', borderRadius: '5px', display: 'none', flexDirection: 'column', gap: '3px', zIndex: '9999' }, option: { backgroundColor: '#222', color: 'white', padding: '5px 10px', border: 'none', cursor: 'pointer', fontSize: '14px', whiteSpace: 'nowrap' } }; function applyStyle(el, styleObj) { Object.assign(el.style, styleObj); } function createButton(text, onClick) { const btn = document.createElement('button'); btn.textContent = text; applyStyle(btn, styles.button); btn.addEventListener('click', onClick); return btn; } function showMessage(text) { let msg = document.getElementById('custom-yt-message'); if (!msg) { msg = document.createElement('div'); msg.id = 'custom-yt-message'; Object.assign(msg.style, { position: 'fixed', bottom: '270px', right: '20px', backgroundColor: 'rgba(0,0,0,0.8)', color: 'white', padding: '5px 10px', borderRadius: '5px', fontSize: '16px', textAlign: 'center', pointerEvents: 'none', zIndex: '9999', display: 'none' }); document.body.appendChild(msg); } msg.textContent = text; msg.style.display = 'block'; clearTimeout(msg.timer); msg.timer = setTimeout(() => { msg.style.display = 'none'; }, 1500); } function isAdPlaying() { return !!document.querySelector('.ad-showing'); } function waitForVideo(callback) { const interval = setInterval(() => { const video = document.querySelector('video'); if (video) { clearInterval(interval); callback(video); const observer = new MutationObserver(() => { const savedSpeed = parseFloat(localStorage.getItem('youtubePlaybackSpeed')) || 1.0; if (!isAdPlaying()) { video.playbackRate = savedSpeed; } }); observer.observe(video, { attributes: true, attributeFilter: ['src'] }); } }, 100); } function adjustSpeed(delta) { waitForVideo(video => { if (isAdPlaying()) return; const newRate = delta === 0 ? 1.0 : Math.min(Math.max(video.playbackRate + delta, 0.25), 16); video.playbackRate = newRate; localStorage.setItem('youtubePlaybackSpeed', newRate); showMessage(t.speedMsg(newRate)); }); } function setSpeed(value) { waitForVideo(video => { if (isAdPlaying()) return; video.playbackRate = value; localStorage.setItem('youtubePlaybackSpeed', value); showMessage(t.speedMsg(value)); }); } function getSavedQuality() { return localStorage.getItem('youtubeQuality') || 'Auto'; } function saveQuality(quality) { localStorage.setItem('youtubeQuality', quality); } function setVideoQuality(qualityLabel) { if (qualityLabel === 'Auto') { saveQuality('Auto'); showMessage(t.qualityMsg('Automática')); return; } waitForVideo(() => { if (isAdPlaying()) return; const settingsButton = document.querySelector('.ytp-settings-button'); if (!settingsButton) return; settingsButton.click(); setTimeout(() => { const menuItems = document.querySelectorAll('.ytp-menuitem'); const qualityMenuItem = Array.from(menuItems).find(item => (item.textContent || '').includes('Qualidade') || (item.textContent || '').includes('Quality')); if (!qualityMenuItem) { settingsButton.click(); return; } qualityMenuItem.click(); setTimeout(() => { const qualityOptions = document.querySelectorAll('.ytp-quality-menu .ytp-menuitem'); const targetQualityOption = Array.from(qualityOptions).find(item => (item.textContent || '').startsWith(qualityLabel)); if (targetQualityOption) { targetQualityOption.click(); saveQuality(qualityLabel); showMessage(t.qualityMsg(qualityLabel)); } else { settingsButton.click(); } }, 300); }, 300); }); } function applySpeedAfterAd(speed) { const interval = setInterval(() => { const video = document.querySelector('video'); if (video && !isAdPlaying()) { clearInterval(interval); video.playbackRate = speed; } }, 500); } function applyQualityAfterAd(quality) { const interval = setInterval(() => { if (!isAdPlaying()) { clearInterval(interval); setVideoQuality(quality); } }, 500); } function addControls() { if (document.getElementById('controls-container')) return; const container = document.createElement('div'); container.id = 'controls-container'; Object.assign(container.style, { position: 'fixed', bottom: '40px', right: '20px', display: 'flex', flexDirection: 'column', gap: '5px', zIndex: '9999' }); const qualityWrapper = document.createElement('div'); qualityWrapper.style.position = 'relative'; const qualityBtn = createButton(t.quality, () => {}); const qualityDropdown = document.createElement('div'); applyStyle(qualityDropdown, styles.dropdown); const qualityOptions = ['Auto', '144p', '240p', '360p', '480p', '720p', '1080p', '1440p', '2160p']; qualityOptions.forEach(q => { const opt = createButton(q, e => { e.stopPropagation(); setVideoQuality(q); qualityDropdown.style.display = 'none'; }); applyStyle(opt, styles.option); opt.addEventListener('mouseenter', () => opt.style.backgroundColor = '#444'); opt.addEventListener('mouseleave', () => opt.style.backgroundColor = '#222'); qualityDropdown.appendChild(opt); }); qualityWrapper.appendChild(qualityBtn); qualityWrapper.appendChild(qualityDropdown); qualityWrapper.addEventListener('mouseenter', () => qualityDropdown.style.display = 'flex'); qualityWrapper.addEventListener('mouseleave', () => qualityDropdown.style.display = 'none'); const speedWrapper = document.createElement('div'); speedWrapper.style.position = 'relative'; const speedBtn = createButton(t.speedLabel, () => adjustSpeed(0)); const speedDropdown = document.createElement('div'); applyStyle(speedDropdown, styles.dropdown); const speeds = [3.0, 2.0, 1.5, 1.25, 1.0, 0.75, 0.5]; speeds.forEach(spd => { const opt = createButton(`${spd}x`, e => { e.stopPropagation(); setSpeed(spd); speedDropdown.style.display = 'none'; }); applyStyle(opt, styles.option); opt.addEventListener('mouseenter', () => opt.style.backgroundColor = '#444'); opt.addEventListener('mouseleave', () => opt.style.backgroundColor = '#222'); speedDropdown.appendChild(opt); }); speedWrapper.appendChild(speedBtn); speedWrapper.appendChild(speedDropdown); speedWrapper.addEventListener('mouseenter', () => speedDropdown.style.display = 'flex'); speedWrapper.addEventListener('mouseleave', () => speedDropdown.style.display = 'none'); container.appendChild(qualityWrapper); container.appendChild(speedWrapper); document.body.appendChild(container); function updateVisibility() { const controls = document.getElementById('controls-container'); if (controls) { controls.style.display = document.fullscreenElement ? 'none' : 'flex'; } } updateVisibility(); document.addEventListener('fullscreenchange', updateVisibility); } function removeControls() { const container = document.getElementById('controls-container'); if (container) container.remove(); } function handlePageChange() { setTimeout(() => { const isVideoPage = location.pathname.startsWith('/watch') || location.pathname.startsWith('/shorts'); if (isVideoPage) { addControls(); waitForVideo(video => {}); const savedQuality = getSavedQuality(); if (savedQuality && savedQuality !== 'Auto') { applyQualityAfterAd(savedQuality); } const savedSpeed = parseFloat(localStorage.getItem('youtubePlaybackSpeed')) || 1.0; applySpeedAfterAd(savedSpeed); } else { removeControls(); } }, 500); } document.addEventListener('yt-navigate-finish', handlePageChange); if (document.body) { handlePageChange(); } else { document.addEventListener('DOMContentLoaded', handlePageChange); } window.addEventListener('keydown', e => { if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName) || document.activeElement.isContentEditable) return; const keyMap = { '+': 0.25, ']': 0.25, '-': -0.25, '[': -0.25 }; if (keyMap[e.key] !== undefined) { adjustSpeed(keyMap[e.key]); } else if (e.key === '=') { adjustSpeed(0); } }); })();