您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Barra multifuncional fixa com ferramentas para YouTube com download de thumbnails, legendas, transcrições, tem encurtador de url, visualização de urls inteiras, rolagem rápida, remoção de bloqueios, cópia de imagens e links e modo leitura; dispõe de tradução via Google Translate, tenta desbloquear paywalls, download de livros do Scribd/Z-Library, busca Searx, visualizador de dados vazados pelo navegador e Turbo Loader que acelera o carregamento de sites; atalho CONTROL+F12.
// ==UserScript== // @name Barra de Botões Ferramentas Youtube // @namespace https://t.me/virumaniaa // @version 1.2.54 // @license MIT // @description Barra multifuncional fixa com ferramentas para YouTube com download de thumbnails, legendas, transcrições, tem encurtador de url, visualização de urls inteiras, rolagem rápida, remoção de bloqueios, cópia de imagens e links e modo leitura; dispõe de tradução via Google Translate, tenta desbloquear paywalls, download de livros do Scribd/Z-Library, busca Searx, visualizador de dados vazados pelo navegador e Turbo Loader que acelera o carregamento de sites; atalho CONTROL+F12. // @author // @match *://*/* // @icon https://www.youtube.com/favicon.ico // @grant GM_setClipboard // @grant GM_download // @grant GM_xmlhttpRequest // @grant GM.cookie // @grant GM_getValue // @grant GM_setValue // @connect is.gd // @connect translate.googleapis.com // @connect https://searx.bndkt.io/* // @connect * // @require https://cdn.jsdelivr.net/npm/@mozilla/[email protected]/Readability.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js // ==/UserScript== (function() { 'use strict'; console.log('Script iniciado para:', window.location.href); if (window.top !== window.self) { console.log('Barra não exibida em iframe'); return; } // Adicione este evento no início do seu script, com a flag "capture" como true // Adicione este evento no início do seu script, com a flag "capture" como true document.addEventListener('keydown', function(e) { if (e.ctrlKey && e.key === 'F12') { e.preventDefault(); e.stopPropagation(); console.log('Atalho CTRL+F12 detectado na fase de captura!'); if (typeof barHidden !== 'undefined') { if (barHidden) { showBar(); showOverlayConfirmationAutoClose("Barra Exibida", "A barra foi restaurada via atalho (CTRL+F12)."); } else { hideBar(); showOverlayConfirmationAutoClose("Barra Ocultada", "A barra foi ocultada via atalho (CTRL+F12)."); } } } }, true); // true indica a fase de captura // Configurações Searx const SEARX_CONFIG = { instanceUrl: 'https://searx.bndkt.io', animationDuration: 300, // ms safeSearch: 0, // 0 = desativada imageProxy: 1, // 1 = ativado urlTrackingRemove: 1 // 1 = ativado }; // Categorias e Motores Searx const SEARX_CATEGORIES = { general: 'Geral', images: 'Imagens', videos: 'Vídeos', news: 'Notícias', map: 'Mapas', files: 'Arquivos', it: 'Tecnologia', science: 'Ciência', socialmedia: 'Redes Sociais', music: 'Música' }; const SEARX_ENGINES = { // Principais duckduckgo: 'DuckDuckGo', google: 'Google', bing: 'Bing', brave: 'Brave', startpage: 'Startpage', mojeek: 'Mojeek', yahoo: 'Yahoo', qwant: 'Qwant', ecosia: 'Ecosia', // Conteúdo wikipedia: 'Wikipedia', reddit: 'Reddit', youtube: 'YouTube', // Idiomas dictzone: 'DictZone', libretranslate: 'LibreTranslate', lingva: 'Lingva', mozhi: 'Mozhi', 'mymemory translated': 'MyMemory', // Alternativos presearch: 'Presearch', 'presearch videos': 'Presearch Videos', wiby: 'Wiby', seznam: 'Seznam', goo: 'Goo', naver: 'Naver', // Wiki wikibooks: 'WikiBooks', wikiquote: 'WikiQuote', wikisource: 'WikiSource', wikispecies: 'WikiSpecies', wikiversity: 'WikiVersity', wikivoyage: 'WikiVoyage', wikidata: 'WikiData', // Outros '360search': '360 Search', alexandria: 'Alexandria', ask: 'Ask', cloudflareai: 'Cloudflare AI', crowdview: 'CrowdView', curlie: 'Curlie', currency: 'Currency', 'ddg definitions': 'DDG Definitions', encyclosearch: 'EncycloSearch', mwmbl: 'Mwmbl', 'right dao': 'Right DAO', searchmysite: 'SearchMySite', sogou: 'Sogou', stract: 'Stract', tineye: 'TinEye', wolframalpha: 'WolframAlpha', yacy: 'YaCy', yep: 'Yep', bpb: 'BPB', tagesschau: 'Tagesschau', wikimini: 'WikiMini' }; const zlConfig = { domains: [ 'https://singlelogin.re', 'https://zlibrary-global.se', 'https://zlibrary-east.se', 'https://zlibrary-au.se' ], retryDelay: 3000, maxAttempts: 5, stealthMode: true }; const downloadManager = { attempts: 0, currentDomainIndex: 0, getDomain() { return zlConfig.domains[this.currentDomainIndex]; }, rotateDomain() { this.currentDomainIndex = (this.currentDomainIndex + 1) % zlConfig.domains.length; this.attempts = 0; }, async fetchBook(bookId) { const domain = this.getDomain(); const url = `${domain}/download/${bookId}?rand=${Math.random().toString(36).substr(2)}`; try { const response = await this._makeRequest(url); if (response.redirected) return response.url; throw new Error('Redirecionamento falhou'); } catch (error) { this.attempts++; if (this.attempts >= zlConfig.maxAttempts) this.rotateDomain(); return this.fetchBook(bookId); } }, _makeRequest(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: url, anonymous: true, onload: function(response) { if (response.status === 200) resolve(response); else reject(new Error(`Status ${response.status}`)); }, onerror: reject }); }); } }; function generateStealthLink(baseUrl) { const params = new URLSearchParams({ ts: Date.now(), ref: document.referrer || 'direct', rand: Math.random().toString(36).substr(2, 8) }); return `${baseUrl}&${params.toString()}`; } async function startDownload(bookId) { try { const downloadUrl = await downloadManager.fetchBook(bookId); const finalUrl = generateStealthLink(downloadUrl); if (zlConfig.stealthMode) { const iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = finalUrl; document.body.appendChild(iframe); } else { window.open(finalUrl, '_blank'); } } catch (error) { showStatus('Erro: Tente novamente em alguns minutos', 'red'); } } function showStatus(message, color = '#28a745') { const existing = document.getElementById('download-status'); if (existing) existing.remove(); const status = document.createElement('div'); status.id = 'download-status'; Object.assign(status.style, { position: 'fixed', top: '20px', right: '20px', backgroundColor: color, color: 'white', padding: '10px 20px', borderRadius: '5px', zIndex: '1000000' }); status.textContent = message; document.body.appendChild(status); setTimeout(() => status.remove(), 3000); } function createButton(text, onClick, color = '#007bff') { const btn = document.createElement('button'); btn.textContent = text; btn.style.backgroundColor = color; btn.style.color = 'white'; btn.style.border = 'none'; btn.style.borderRadius = '4px'; btn.style.padding = '8px 16px'; btn.style.margin = '5px'; btn.style.cursor = 'pointer'; btn.style.fontSize = '14px'; btn.addEventListener('click', onClick); return btn; } let originalTextNodes = []; function toggleGoogleTranslate() { if (document.body.getAttribute('data-translated') === 'true') { originalTextNodes.forEach(({ node, originalText }) => { node.textContent = originalText; }); document.body.removeAttribute('data-translated'); showOverlayConfirmationAutoClose("Sucesso", "Texto original restaurado!"); updateGoogleTradButtonStyle(btnGoogleTrad); } else { translatePage(); } } async function translatePage() { try { console.log('Iniciando tradução da página'); const elements = document.body.getElementsByTagName('*'); originalTextNodes = []; for (let element of elements) { if (element.nodeType === 1 && window.getComputedStyle(element).display !== 'none' && !element.closest('#userToolbar')) { for (let node of element.childNodes) { if (node.nodeType === 3 && node.textContent.trim()) { originalTextNodes.push({ node, originalText: node.textContent.trim() }); } } } } if (originalTextNodes.length === 0) { showOverlayConfirmationAutoClose("Erro", "Nenhum texto encontrado para traduzir."); console.log('Nenhum texto encontrado para traduzir'); return; } console.log(`Encontrados ${originalTextNodes.length} nós de texto para traduzir`); const texts = originalTextNodes.map(t => t.originalText); const batchSize = 5000; let translatedTexts = []; for (let i = 0; i < texts.length; i += batchSize) { const batch = texts.slice(i, i + batchSize).join('\n'); console.log(`Traduzindo lote de ${i} a ${Math.min(i + batchSize, texts.length)}`); const translatedBatch = await translateText(batch); translatedTexts = translatedTexts.concat(translatedBatch.split('\n')); } console.log(`Aplicando ${translatedTexts.length} textos traduzidos`); originalTextNodes.forEach((nodeObj, index) => { if (translatedTexts[index]) { nodeObj.node.textContent = translatedTexts[index]; } }); document.body.setAttribute('data-translated', 'true'); showOverlayConfirmationAutoClose("Sucesso", "Página traduzida para português!"); updateGoogleTradButtonStyle(btnGoogleTrad); } catch (error) { console.error('Erro ao traduzir a página:', error); showOverlayConfirmationAutoClose("Erro", "Falha ao traduzir a página: " + error.message); } } async function translateText(text) { return new Promise((resolve, reject) => { const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=pt&dt=t&q=${encodeURIComponent(text)}`; console.log('Enviando requisição para:', url); GM_xmlhttpRequest({ method: 'GET', url: url, headers: { "Content-Type": "application/json" }, onload: function(response) { console.log('Resposta recebida:', response.status, response.responseText); if (response.status === 200) { try { const data = JSON.parse(response.responseText); const translated = data[0].map(item => item[0]).join(''); resolve(translated); } catch (e) { console.error('Erro ao parsear resposta:', e); reject(new Error("Erro ao processar a resposta da tradução: " + e.message)); } } else { reject(new Error(`Erro na requisição: Status ${response.status}`)); } }, onerror: function(error) { console.error('Erro na requisição GM_xmlhttpRequest:', error); reject(new Error("Falha na requisição de tradução: " + error.message)); } }); }); } function updateGoogleTradButtonStyle(btn) { if (document.body.getAttribute('data-translated') === 'true') { btn.textContent = 'Trad. OFF'; btn.className = 'btn-default'; btn.style.backgroundColor = '#00ff00'; btn.style.color = '#000'; btn.setAttribute("data-active", "true"); } else { btn.textContent = 'Google trad.'; btn.className = 'btn-default btn-googletrad'; btn.style.backgroundColor = '#4285F4'; btn.style.color = 'white'; btn.removeAttribute("data-active"); } } const barOriginalHeight = 40; const marginValue = `${barOriginalHeight}px`; const hostname = location.hostname; let youtubeApp = null, youtubeMasthead = null, protonHeader = null, protonMain = null; if (hostname.includes('youtube.com')) { console.log('Detectado YouTube, buscando elementos'); youtubeApp = document.querySelector('ytd-app'); youtubeMasthead = document.querySelector('ytd-masthead#masthead'); console.log('youtubeApp:', youtubeApp, 'youtubeMasthead:', youtubeMasthead); } else if (hostname.includes('proton.me')) { protonHeader = document.querySelector('header'); protonMain = document.querySelector('main'); } const updateMargin = (elements, value) => { elements.forEach(el => { if (el) el.style.marginTop = value; }); }; function applyPushDown() { console.log('Aplicando push down'); if (youtubeApp && youtubeMasthead) { youtubeMasthead.style.setProperty('margin-top', marginValue, 'important'); console.log('Masthead ajustado para margin-top:', marginValue); const miniGuide = youtubeApp.querySelector('ytd-mini-guide-renderer, ytd-guide-renderer'); if (miniGuide) { miniGuide.style.setProperty('margin-top', marginValue, 'important'); console.log('Mini-guide ajustado para margin-top:', marginValue); } const content = youtubeApp.querySelector('#content'); if (content) { content.style.setProperty('margin-top', `calc(${marginValue} + 56px)`, 'important'); console.log('Content ajustado para margin-top: calc(40px + 56px)'); } youtubeApp.style.paddingTop = '0'; } else if (protonHeader && protonMain) { updateMargin([protonHeader, protonMain], marginValue); } else { updateMargin([document.body], marginValue); } } function removePushDown() { console.log('Removendo push down'); if (youtubeMasthead) { youtubeMasthead.style.setProperty('margin-top', '0px', 'important'); const miniGuide = youtubeApp ? youtubeApp.querySelector('ytd-mini-guide-renderer, ytd-guide-renderer') : null; if (miniGuide) { miniGuide.style.setProperty('margin-top', '0px', 'important'); } const content = youtubeApp ? youtubeApp.querySelector('#content') : null; if (content) { content.style.setProperty('margin-top', '0px', 'important'); } } updateMargin([youtubeApp, protonHeader, protonMain, document.body], '0px'); } const customStyle = document.createElement('style'); customStyle.textContent = ` .btn-default { background-color: #ff0000 !important; color: white !important; border: none !important; border-radius: 4px !important; padding: 5px 8px !important; font-size: 12px !important; font-weight: bold !important; cursor: pointer !important; } .btn-default:active { background-color: #00ff00 !important; color: black !important; } .btn-default[data-active="true"] { background-color: #00ff00 !important; color: black !important; } .btn-toggle { background-color: #ffff00 !important; color: black !important; border: none !important; border-radius: 4px !important; padding: 5px 8px !important; font-size: 12px !important; font-weight: bold !important; cursor: pointer !important; } .btn-toggle:active { background-color: #00ff00 !important; color: black !important; } .btn-toggle[data-active="true"] { background-color: #00ff00 !important; color: black !important; } .btn-blue { background-color: #007bff !important; color: white !important; border: none !important; border-radius: 4px !important; padding: 3px 8px !important; font-size: 10px !important; cursor: pointer !important; } .btn-blue:active { background-color: #00ff00 !important; color: black !important; } .btn-blue[data-active="true"] { background-color: #00ff00 !important; color: black !important; } .btn-green { background-color: #28a745 !important; color: white !important; border: none !important; border-radius: 4px !important; padding: 5px 8px !important; font-size: 12px !important; font-weight: bold !important; cursor: pointer !important; } .btn-green:active { background-color: #00ff00 !important; color: black !important; } .btn-green[data-active="true"] { background-color: #00ff00 !important; color: black !important; } .btn-scribd { background-color: #2e9fa2 !important; color: white !important; border: none !important; border-radius: 4px !important; padding: 5px 8px !important; font-size: 12px !important; font-weight: bold !important; cursor: pointer !important; } .btn-scribd:active { background-color: #00ff00 !important; color: black !important; } .btn-scribd[data-active="true"] { background-color: #00ff00 !important; color: black !important; } .btn-googletrad { background-color: #4285F4 !important; color: white !important; border: none !important; border-radius: 4px !important; padding: 5px 8px !important; font-size: 12px !important; font-weight: bold !important; cursor: pointer !important; } .btn-googletrad:active { background-color: #00ff00 !important; color: black !important; } .btn-searx { background-color: #006400 !important; color: white !important; border: none !important; border-radius: 4px !important; padding: 5px 8px !important; font-size: 12px !important; font-weight: bold !important; cursor: pointer !important; } .btn-searx:active { background-color: #00ff00 !important; color: black !important; } .btn-searx[data-active="true"] { background-color: #00ff00 !important; color: black !important; } .btn-data-exposed { background-color: #000000 !important; color: white !important; border: 2px solid red !important; border-radius: 4px !important; padding: 5px 8px !important; font-size: 12px !important; font-weight: bold !important; cursor: pointer !important; } .btn-data-exposed:active { background-color: #00ff00 !important; color: black !important; } .btn-data-exposed[data-active="true"] { background-color: #00ff00 !important; color: black !important; border: 2px solid red !important; } .btn-reading-mode { background-color: rgba(26, 115, 232, 0.9) !important; color: white !important; border: none !important; border-radius: 4px !important; padding: 5px 8px !important; font-size: 12px !important; font-weight: bold !important; cursor: pointer !important; } .btn-reading-mode:hover { background-color: rgba(26, 115, 232, 1) !important; } .btn-reading-mode:active { background-color: #00ff00 !important; color: black !important; } .btn-reading-mode[data-active="true"] { background-color: #00ff00 !important; color: black !important; } .draggable-menu { position: fixed; zIndex: 1000000; background: rgba(0, 0, 0, 0.8); color: #fff; padding: 20px; border-radius: 4px; box-shadow: 0 0 10px rgba(0,0,0,0.5); cursor: move; } .thumbnail-menu { width: 960px; height: 540px; display: flex; flex-direction: column; justify-content: flex-start; align-items: center; } .draggable-menu h3 { margin-top: 0; margin-bottom: 10px; color: yellow; cursor: move; } .draggable-menu .image-container { flex-grow: 1; display: flex; justify-content: center; align-items: center; max-height: 460px; overflow: hidden; } .draggable-menu .button-container { display: flex; justify-content: center; gap: 10px; padding-top: 10px; } .draggable-menu .container { display: flex; flex-wrap: wrap; gap: 15px; justify-content: center; overflow: auto; } /* Estilos Searx */ .searx-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 99999999; display: flex; justify-content: center; align-items: center; backdrop-filter: blur(4px); opacity: 0; transition: opacity ${SEARX_CONFIG.animationDuration}ms ease; } .searx-modal { background: #1a1a2e; border-radius: 8px; width: 90%; max-width: 850px; max-height: 85vh; overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); transform: translateY(20px); transition: transform ${SEARX_CONFIG.animationDuration}ms ease; color: #f0f0f0; position: absolute; } .searx-header { padding: 20px; background: #292945; border-bottom: 1px solid rgba(255, 255, 255, 0.1); cursor: move; display: flex; flex-direction: column; user-select: none; } .searx-header h3 { margin: 0 0 15px 0; font-size: 1.4rem; color: #f0f0f0; user-select: none; } .searx-search-container { display: flex; gap: 10px; width: 100%; position: relative; z-index: 5; } .searx-search-container input { flex-grow: 1; padding: 12px; border-radius: 8px; border: 1px solid rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.1); color: #f0f0f0; font-size: 16px; transition: all 0.3s ease; } .searx-search-container input:focus { outline: none; border-color: #6c5ce7; box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.3); } .searx-content { padding: 0 20px; overflow-y: auto; flex-grow: 1; } .searx-section { margin: 15px 0; position: relative; } .searx-collapsible-header { padding: 12px 15px; background: #292945; border-radius: 8px; font-weight: bold; cursor: pointer; display: flex; justify-content: space-between; align-items: center; transition: background 0.2s ease; user-select: none; } .searx-collapsible-header:hover { background: rgba(255, 255, 255, 0.15); } .searx-collapsible-header .searx-section-title { margin: 0; font-size: 1.1rem; display: flex; align-items: center; } .searx-collapsible-header .searx-toggle-icon { transition: transform 0.3s ease; } .searx-collapsible-header.active .searx-toggle-icon { transform: rotate(180deg); } .searx-collapsible-content { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; background: rgba(255, 255, 255, 0.03); border-radius: 0 0 8px 8px; } .searx-collapsible-content.open { max-height: 500px; overflow-y: auto; } .searx-section-title span { margin-left: 8px; font-size: 0.8rem; color: #b8b8b8; font-weight: normal; } .searx-checkbox-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 8px; padding: 15px; } .searx-checkbox-item { display: flex; align-items: center; padding: 6px 10px; background: #292945; border-radius: 8px; transition: all 0.2s ease; } .searx-checkbox-item:hover { background: rgba(255, 255, 255, 0.1); } .searx-checkbox-item label { margin-left: 6px; cursor: pointer; font-size: 0.9rem; user-select: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-grow: 1; } .searx-checkbox { appearance: none; -webkit-appearance: none; width: 18px; height: 18px; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 4px; cursor: pointer; position: relative; transition: all 0.2s ease; flex-shrink: 0; } .searx-checkbox:checked { background: #6c5ce7; border-color: #6c5ce7; } .searx-checkbox:checked::after { content: ''; position: absolute; top: 3px; left: 6px; width: 4px; height: 8px; border: solid white; border-width: 0 2px 2px 0; transform: rotate(45deg); } .searx-footer { padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; background: #292945; border-top: 1px solid rgba(255, 255, 255, 0.1); } .searx-switcher { display: flex; align-items: center; } .searx-switcher label { font-size: 0.9rem; margin-left: 8px; user-select: none; } .searx-switch { position: relative; display: inline-block; width: 40px; height: 22px; } .searx-switch input { opacity: 0; width: 0; height: 0; } .searx-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(255, 255, 255, 0.2); transition: .4s; border-radius: 22px; } .searx-slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; } .searx-switch input:checked + .searx-slider { background-color: #6c5ce7; } .searx-switch input:checked + .searx-slider:before { transform: translateX(18px); } .searx-btn-group { display: flex; gap: 10px; } .searx-button { padding: 8px 16px; border-radius: 8px; font-weight: 500; border: none; cursor: pointer; transition: all 0.2s ease; } .searx-button-primary { background: #6c5ce7; color: white; } .searx-button-primary:hover { background: #5b4bc9; } .searx-button-secondary { background: rgba(255, 255, 255, 0.1); color: #f0f0f0; } .searx-button-secondary:hover { background: rgba(255, 255, 255, 0.2); } .searx-button-danger { background: #e74c3c; color: white; } .searx-button-danger:hover { background: #c0392b; } .searx-search-history { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 6px; } .searx-history-item { padding: 5px 10px; background: rgba(255, 255, 255, 0.1); border-radius: 20px; font-size: 0.8rem; cursor: pointer; transition: all 0.2s ease; } .searx-history-item:hover { background: rgba(255, 255, 255, 0.2); } .searx-toast { position: fixed; bottom: 80px; right: 20px; background: #2ecc71; color: white; padding: 12px 16px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); z-index: 10001; opacity: 0; transform: translateY(20px); transition: all 0.3s ease; } .searx-toast.show { opacity: 1; transform: translateY(0); } .searx-fade-in { opacity: 1; } .searx-move-up { transform: translateY(0); } .searx-shortcut { display: inline-block; padding: 2px 6px; border-radius: 4px; background: rgba(255, 255, 255, 0.1); font-size: 0.8rem; margin-left: 8px; } @keyframes searx-pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .searx-search-btn { animation: searx-pulse 1.5s infinite; } /* Estilos do Modo de Leitura */ #speedreader-overlay { contain: strict; z-index: 2147483647; } #reader-loading-indicator { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2147483647; background: rgba(0, 0, 0, 0.7); color: white; padding: 20px 30px; border-radius: 8px; font-family: system-ui, sans-serif; font-size: 16px; display: flex; align-items: center; gap: 12px; } #reader-error-message { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2147483647; background: rgba(220, 53, 69, 0.9); color: white; padding: 20px 30px; border-radius: 8px; font-family: system-ui, sans-serif; font-size: 16px; max-width: 400px; text-align: center; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } @keyframes reader_spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @media print { #speedreader-overlay { position: relative !important; height: auto !important; } #speedreader-overlay .controls { display: none !important; } } `; document.head.appendChild(customStyle); console.log('Estilos personalizados adicionados'); function updateLayoutAfterUrlBar() { if (!urlBarActive && urlBarContainer) { urlBarContainer.style.display = 'none'; applyPushDown(); // Restaura o layout padrão sem a barra de URL } } function makeDraggable(element) { let isDragging = false; let startX, startY, initialX, initialY; const setFixedSize = () => { const rect = element.getBoundingClientRect(); element.style.width = `${rect.width}px`; element.style.height = `${rect.height}px`; }; setTimeout(setFixedSize, 0); element.addEventListener('mousedown', (e) => { if (e.target.tagName === 'BUTTON' || e.target.closest('button')) return; isDragging = true; startX = e.clientX; startY = e.clientY; const rect = element.getBoundingClientRect(); initialX = rect.left; initialY = rect.top; element.style.transition = 'none'; e.preventDefault(); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const deltaX = e.clientX - startX; const deltaY = e.clientY - startY; element.style.left = `${initialX + deltaX}px`; element.style.top = `${initialY + deltaY}px`; element.style.transform = 'none'; }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; element.style.transition = 'opacity 0.2s'; } }); } // Função para tornar um elemento arrastável (Searx modal) function makeSearxDraggable(element, handle) { let isDragging = false; let startX, startY, initialLeft, initialTop; handle.addEventListener('mousedown', function(e) { // Não iniciar arrasto se o clique foi em elementos interativos if (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON' || e.target.closest('button')) { return; } e.preventDefault(); isDragging = true; element.classList.add('dragging'); // Posição inicial do mouse startX = e.clientX; startY = e.clientY; // Posição inicial do elemento const rect = element.getBoundingClientRect(); initialLeft = rect.left; initialTop = rect.top; document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); }); function drag(e) { if (!isDragging) return; // Calcular o deslocamento const dx = e.clientX - startX; const dy = e.clientY - startY; // Aplicar nova posição element.style.left = (initialLeft + dx) + 'px'; element.style.top = (initialTop + dy) + 'px'; } function stopDrag() { if (isDragging) { isDragging = false; element.classList.remove('dragging'); document.removeEventListener('mousemove', drag); document.removeEventListener('mouseup', stopDrag); } } } const toolbar = document.createElement('div'); toolbar.id = 'userToolbar'; toolbar.title = 'Para ocultar ou mostrar a barra use o atalho CONTROL+F12'; Object.assign(toolbar.style, { position: 'fixed', top: '0', left: '0', width: '100%', height: `${barOriginalHeight}px`, zIndex: '10000000', backgroundColor: '#000', display: 'flex', alignItems: 'center', padding: '0 10px', boxShadow: '0 2px 5px rgba(0,0,0,0.5)', color: '#fff', fontFamily: 'Arial, sans-serif', fontSize: '14px' }); console.log('Toolbar criada'); const toolbarStyle = document.createElement('style'); toolbarStyle.textContent = ` #userToolbar button { cursor: pointer !important; }`; document.head.appendChild(toolbarStyle); function showOverlayConfirmationAutoClose(titulo, mensagem, timeout = 4000) { const overlay = document.createElement('div'); Object.assign(overlay.style, { position: 'fixed', top: '20%', left: '50%', transform: 'translateX(-50%)', zIndex: '1000000', background: 'rgba(0, 0, 0, 0.8)', color: '#fff', padding: '20px', borderRadius: '4px', fontSize: '14px', fontFamily: 'Arial, sans-serif', minWidth: '300px', boxShadow: '0 0 10px rgba(0,0,0,0.5)' }); const tituloElement = document.createElement('h3'); Object.assign(tituloElement.style, { marginTop: '0', marginBottom: '10px', color: 'yellow' }); tituloElement.textContent = titulo; overlay.appendChild(tituloElement); const mensagemElement = document.createElement('p'); mensagemElement.style.margin = '0'; mensagemElement.textContent = mensagem; overlay.appendChild(mensagemElement); document.body.appendChild(overlay); setTimeout(() => overlay.remove(), timeout); } function getToolbarStateGlobal() { return GM_getValue("toolbar_hidden_global", false); } function setToolbarStateGlobal(hidden) { GM_setValue("toolbar_hidden_global", hidden); } let barHidden = getToolbarStateGlobal(); let urlBarActive = false; let urlBarContainer = null; const DRAG_THRESHOLD = 5; function hideBar() { console.log('Ocultando barra, estado anterior:', barHidden); barHidden = true; if (toolbar) toolbar.style.display = 'none'; if (urlBarContainer) { urlBarContainer.style.display = 'none'; } removePushDown(); setToolbarStateGlobal(true); console.log('Barra ocultada, novo estado:', barHidden); } function showBar() { console.log('Exibindo barra, estado anterior:', barHidden); barHidden = false; if (toolbar) toolbar.style.display = 'flex'; if (urlBarActive && urlBarContainer) { urlBarContainer.style.display = 'block'; urlBarContainer.querySelector('#fullUrlInput').value = window.location.href; applyPushDownWithUrlBar(); } else { if (urlBarContainer) urlBarContainer.style.display = 'none'; applyPushDown(); } setToolbarStateGlobal(false); console.log('Barra exibida, novo estado:', barHidden); } const centerContainer = document.createElement('div'); Object.assign(centerContainer.style, { flex: '1', display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '5px', overflowX: 'auto', whiteSpace: 'nowrap' }); toolbar.appendChild(centerContainer); console.log('Container central adicionado à toolbar'); function setupKeyboardShortcut() { function handleKeyDown(e) { if (e.ctrlKey && e.key === 'F12') { e.preventDefault(); e.stopPropagation(); console.log('Atalho CTRL+F12 detectado!'); if (barHidden) { showBar(); showOverlayConfirmationAutoClose("Barra Exibida", "A barra foi restaurada via atalho (CTRL+F12)."); } else { hideBar(); showOverlayConfirmationAutoClose("Barra Ocultada", "A barra foi ocultada via atalho (CTRL+F12)."); } } } document.addEventListener('keydown', handleKeyDown); window.addEventListener('keydown', handleKeyDown); } window.addEventListener('popstate', () => { console.log('Navegação detectada via histórico (voltar/avançar)'); if (getToolbarStateGlobal()) { hideBar(); } else { showBar(); } }); let lastUrl = window.location.href; setInterval(() => { if (window.location.href !== lastUrl) { lastUrl = window.location.href; updateUrlBarOnNavigation(); } }, 500); function updateComentariosButtonStyle(btn, isActive) { if (isActive) { btn.style.backgroundColor = "#00ff00"; btn.style.color = "#000"; btn.setAttribute("data-active", "true"); } else { btn.style.backgroundColor = "#ff0000"; btn.style.color = "#fff"; btn.removeAttribute("data-active"); } } function toggleComentarios() { if (!location.href.includes("youtube.com/watch")) { showOverlayConfirmationAutoClose("Aviso", "Somente no YouTube."); return; } const videoContainer = document.getElementById("player") || document.querySelector("ytd-player"); const commentsContainer = document.getElementById("comments") || document.querySelector("ytd-comments"); if (!videoContainer || !commentsContainer) { showOverlayConfirmationAutoClose("Erro", "Não foi possível localizar o player ou os comentários."); return; } if (!toggleComentarios.ativado) { toggleComentarios.originalDisplay = window.getComputedStyle(videoContainer).display; videoContainer.style.display = "none"; toggleComentarios.originalParent = commentsContainer.parentNode; toggleComentarios.originalNext = commentsContainer.nextSibling; videoContainer.parentNode.insertBefore(commentsContainer, videoContainer); toggleComentarios.ativado = true; updateComentariosButtonStyle(this, true); } else { videoContainer.style.display = toggleComentarios.originalDisplay; if (toggleComentarios.originalParent) { if (toggleComentarios.originalNext) { toggleComentarios.originalParent.insertBefore(commentsContainer, toggleComentarios.originalNext); } else { toggleComentarios.originalParent.appendChild(commentsContainer); } } toggleComentarios.ativado = false; updateComentariosButtonStyle(this, false); } } function createComentariosButton() { const btn = document.createElement('button'); btn.textContent = 'Comentários'; btn.className = 'btn-default'; btn.style.cursor = 'pointer'; btn.title = 'Mostra comentários e oculta vídeo'; btn.addEventListener('click', function(e) { toggleComentarios.call(this, e); }); return btn; } function createIsgdButton() { const btn = document.createElement('button'); btn.textContent = 'Encurtador-is.gd'; btn.className = 'btn-default'; btn.title = 'Encurta link com o is.gd'; btn.addEventListener('click', shortenWithIsgd); return btn; } function shortenWithIsgd() { const originalUrl = window.location.href; const apiUrl = `https://is.gd/create.php?format=simple&url=${encodeURIComponent(originalUrl)}`; GM_xmlhttpRequest({ method: 'GET', url: apiUrl, nocache: true, onload: (response) => { if (response.status === 200) { if (response.responseText.startsWith('Error:')) { showOverlayConfirmationAutoClose("Erro ao Encurtar", response.responseText); } else { const shortenedUrl = response.responseText.trim(); GM_setClipboard(shortenedUrl, 'text'); showOverlayConfirmationAutoClose("URL Encurtada!", `A URL foi encurtada e copiada:\n${shortenedUrl}`); } } else { showOverlayConfirmationAutoClose("Erro ao Encurtar", `Código de status HTTP: ${response.status}`); } }, onerror: () => { showOverlayConfirmationAutoClose("Erro ao Encurtar", "Falha na requisição GM_xmlhttpRequest."); } }); } async function copyTranscript() { if (!location.hostname.includes('youtube.com')) { showOverlayConfirmationAutoClose("Aviso", "Somente no YouTube."); return; } try { const transcript = await getTranscript(); if (transcript) { GM_setClipboard(transcript, 'text'); showOverlayConfirmationAutoClose("Transcrição Copiada!", "O texto completo foi copiado."); } } catch (error) { showOverlayConfirmationAutoClose("Erro na Transcrição", error.message); } } async function getTranscript() { try { const transcriptButton = document.querySelector('button[aria-label*="transcrição"]'); if (transcriptButton && !transcriptButton.getAttribute('aria-pressed')) { transcriptButton.click(); await new Promise(resolve => setTimeout(resolve, 1000)); } const startTime = Date.now(); return await new Promise((resolve, reject) => { const interval = setInterval(() => { const segments = document.querySelectorAll('ytd-transcript-segment-renderer'); if (segments.length > 0) { clearInterval(interval); const transcriptText = Array.from(segments) .map(segment => { const time = segment.querySelector('.segment-timestamp')?.textContent?.trim() || '[00:00]'; const text = segment.querySelector('.segment-text')?.textContent?.trim() || ''; return `${time} ${text}`; }) .join('\n'); if (transcriptButton) transcriptButton.click(); resolve(transcriptText); } else if (Date.now() - startTime > 5000) { clearInterval(interval); reject(new Error("Tempo limite excedido ao carregar a transcrição")); } }, 500); }); } catch (error) { throw new Error(`Erro: ${error.message}`); } } function createLegendasMenu(subtitles) { const menu = document.createElement('div'); menu.className = 'draggable-menu'; Object.assign(menu.style, { top: '20%', left: '50%', transform: 'translateX(-50%)', textAlign: 'center', minWidth: '300px' }); const h3 = document.createElement('h3'); h3.textContent = 'Legendas Disponíveis'; Object.assign(h3.style, { fontSize: '12px' }); menu.appendChild(h3); const container = document.createElement('div'); container.className = 'container'; subtitles.forEach(subtitle => { const div = document.createElement('div'); div.style.marginBottom = '10px'; const label = document.createElement('span'); label.textContent = subtitle.languageName + (subtitle.kind ? " (ASR)" : " (Dono)"); Object.assign(label.style, { display: 'block', marginBottom: '5px', color: ((subtitle.languageName.toLowerCase().includes("português") && !subtitle.kind) || subtitle.languageName.toLowerCase().includes("inglês")) ? "yellow" : "white", fontSize: '10px' }); div.appendChild(label); const btnDownOrig = document.createElement('button'); btnDownOrig.textContent = 'Baixar Original'; btnDownOrig.className = 'btn-default'; btnDownOrig.addEventListener('click', event => { event.stopPropagation(); downloadSubtitle(subtitle); }); div.appendChild(btnDownOrig); const btnDownTrad = document.createElement('button'); btnDownTrad.textContent = 'Traduzido (PT-BR)'; btnDownTrad.className = 'btn-blue'; btnDownTrad.addEventListener('click', event => { event.stopPropagation(); downloadTranslatedSubtitle(subtitle); }); div.appendChild(btnDownTrad); container.appendChild(div); }); menu.appendChild(container); const btnFechar = document.createElement('button'); btnFechar.textContent = 'Fechar'; Object.assign(btnFechar.style, { background: '#ffff00', border: 'none', color: '#000', padding: '3px 8px', cursor: 'pointer', borderRadius: '3px', fontSize: '10px', marginTop: '10px' }); btnFechar.addEventListener('click', () => menu.remove()); menu.appendChild(btnFechar); makeDraggable(menu); return menu; } async function showLegendasMenu() { if (!location.hostname.includes('youtube.com')) { showOverlayConfirmationAutoClose("Aviso", "Somente no YouTube."); return; } try { const subtitles = await getAvailableSubtitles(); if (!subtitles.length) { alert("Nenhuma legenda disponível para este vídeo."); return; } const menu = createLegendasMenu(subtitles); document.body.appendChild(menu); } catch (error) { alert(`Erro: ${error.message}`); } } function decodeHTMLEntities(text) { if (typeof text !== 'string') { console.error('Entrada inválida para decodeHTMLEntities:', text); return ''; } console.log('Texto bruto para decodificar:', text); const entities = { '&': '&', '<': '<', '>': '>', '"': '"', '\'': '\'', // Apóstrofo escapado '\u00A0': ' ', // Espaço não separável '"': '"', // Aspas duplas ''': '\'', // Apóstrofo ' ': ' ', // Espaço não separável '&': '&', // E comercial '<': '<', // Menor que '>': '>', // Maior que '¡': '¡', '¢': '¢', '£': '£', '¤': '¤', '¥': '¥', '¦': '¦', '§': '§', '¨': '¨', '©': '©', 'ª': 'ª', '»': '»', '¬': '¬', '­': '', '®': '®', '¯': '¯', '°': '°', '±': '±', '²': '²', '³': '³', '´': '´', 'µ': 'µ', '¶': '¶', '·': '·', '¸': '¸', '¹': '¹', 'º': 'º', '«': '«', '¼': '¼', '½': '½', '¾': '¾', '¿': '¿', '×': '×', '÷': '÷' }; try { let decoded = text.replace(/<[^>]+>/g, ""); // Remove todas as tags HTML primeiro decoded = decoded.replace(/&(?:[a-zA-Z0-9]+|#x?[0-9A-Fa-f]+);/g, match => { const result = entities[match] || match; console.log(`Entidade ${match} decodificada como: ${result}`); return result; }); // Trata entidades numéricas e hexadecimais manualmente, caso não estejam no mapeamento decoded = decoded.replace(/&#(\d+);/g, (_, num) => { const result = String.fromCharCode(parseInt(num, 10)) || match; console.log(`Entidade numérica &#${num}; decodificada como: ${result}`); return result; }); decoded = decoded.replace(/&#x([0-9A-Fa-f]+);/gi, (_, hex) => { const result = String.fromCharCode(parseInt(hex, 16)) || match; console.log(`Entidade hexadecimal &#x${hex}; decodificada como: ${result}`); return result; }); console.log('Texto decodificado:', decoded); return decoded.trim(); } catch (error) { console.error('Erro ao decodificar entidades:', error); return text.trim() || ''; // Retorna o texto original limpo se houver erro } } function convertXmlToSrt(xmlContent) { console.log('XML para SRT:', xmlContent.slice(0, 200)); const lines = []; let index = 1; const textRegex = /<text start="([\d.]+)" dur="([\d.]+)">([\s\S]*?)<\/text>/gi; let match; while ((match = textRegex.exec(xmlContent)) !== null) { const start = parseFloat(match[1]); const duration = parseFloat(match[2]); const end = start + duration; const startTime = formatTime(start); const endTime = formatTime(end); const rawText = match[3] || ''; const text = decodeHTMLEntities(rawText.replace(/<[^>]+>/g, "").trim()); if (text) { lines.push(`${index}\n${startTime} --> ${endTime}\n${text}\n\n`); console.log(`Linha ${index}: ${text}`); index++; } } const result = lines.join(''); if (!result) console.log('Nenhuma legenda encontrada no XML'); return result; } function convertXmlToPlainText(xmlContent) { console.log('XML para TXT:', xmlContent.slice(0, 200)); const textSegments = []; const textRegex = /<text[^>]*>([\s\S]*?)<\/text>/gi; let match; while ((match = textRegex.exec(xmlContent)) !== null) { const rawText = match[1] || ''; const cleanText = decodeHTMLEntities(rawText.replace(/<[^>]+>/g, "").replace(/\n/g, " ").trim()); if (cleanText) { textSegments.push(cleanText); console.log('Segmento TXT:', cleanText); } } const result = textSegments.join(" ").trim(); if (!result) console.log('Nenhum texto puro extraído do XML'); return result; } function formatTime(seconds) { const hours = Math.floor(seconds / 3600).toString().padStart(2, '0'); const minutes = Math.floor((seconds % 3600) / 60).toString().padStart(2, '0'); const secs = Math.floor(seconds % 60).toString().padStart(2, '0'); const millis = Math.floor((seconds % 1) * 1000).toString().padStart(3, '0'); return `${hours}:${minutes}:${secs},${millis}`; } async function downloadSubtitle(subtitle) { try { const response = await fetch(subtitle.baseUrl); if (!response.ok) throw new Error('Falha ao buscar legenda'); const xmlContent = await response.text(); const srtContent = convertXmlToSrt(xmlContent); const blob = new Blob([srtContent], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); GM_download({ url, name: `${subtitle.languageName}.srt`, saveAs: true }); } catch (error) { alert(`Erro ao baixar legenda: ${error.message}`); } } async function downloadTranslatedSubtitle(subtitle) { try { const translatedUrl = `${subtitle.baseUrl}&tlang=pt`; const response = await fetch(translatedUrl); if (!response.ok) throw new Error('Falha ao buscar legenda traduzida'); const xmlContent = await response.text(); const srtContent = convertXmlToSrt(xmlContent); const blob = new Blob([srtContent], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); GM_download({ url, name: `${subtitle.languageName}_PT-BR.srt`, saveAs: true }); } catch (error) { alert(`Erro ao baixar legenda traduzida: ${error.message}`); } } async function getAvailableSubtitles() { let playerResponse = window.ytInitialPlayerResponse; if (location.hostname.includes('youtube.com')) { const player = document.querySelector('#movie_player') || document.querySelector('ytd-player'); if (player && player.getPlayerResponse) { playerResponse = player.getPlayerResponse(); } else { const videoId = getVideoIdFromUrl(); if (videoId) { try { const response = await fetch(`/watch?v=${videoId}`, { method: 'GET' }); const html = await response.text(); const match = html.match(/ytInitialPlayerResponse\s*=\s*({.*?});/); if (match && match[1]) { playerResponse = JSON.parse(match[1]); } } catch (e) { console.log("Erro ao tentar atualizar playerResponse:", e); } } } } if (!playerResponse || !playerResponse.captions) { throw new Error("Nenhuma legenda encontrada para o vídeo atual."); } const captions = playerResponse.captions.playerCaptionsTracklistRenderer.captionTracks; return captions.map(track => ({ languageCode: track.languageCode, languageName: track.name.simpleText, baseUrl: track.baseUrl, kind: track.kind })); } async function downloadSubtitleTxt(subtitle) { try { console.log('Tentando baixar TXT puro para:', subtitle.baseUrl); const response = await fetch(subtitle.baseUrl); if (!response.ok) throw new Error(`Falha ao buscar legenda: Status ${response.status}`); const xmlContent = await response.text(); console.log('XML recebido:', xmlContent.slice(0, 100)); // Log parcial para debug const plainText = convertXmlToPlainText(xmlContent); if (!plainText) throw new Error('Nenhum texto puro gerado a partir do XML'); console.log('Texto puro gerado:', plainText.slice(0, 100)); // Log parcial const blob = new Blob([plainText], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); const fileName = subtitle.languageName ? `${subtitle.languageName}_txt-puro.txt` : 'subtitulo_txt-puro.txt'; GM_download({ url, name: fileName, saveAs: true }); console.log('Download iniciado para:', fileName); } catch (error) { console.error('Erro em downloadSubtitleTxt:', error); showOverlayConfirmationAutoClose("Erro ao baixar legenda TXT-puro", error.message); } } async function getThumbnailUrl() { if (!location.hostname.includes('youtube.com')) { throw new Error("Somente no YouTube."); } const videoId = getVideoIdFromUrl(); if (!videoId) { throw new Error("Nenhum ID de vídeo encontrado na URL."); } const thumbnailUrls = [ `https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`, `https://i.ytimg.com/vi/${videoId}/sddefault.jpg`, `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`, `https://i.ytimg.com/vi/${videoId}/mqdefault.jpg`, `https://i.ytimg.com/vi/${videoId}/default.jpg`, `https://i.ytimg.com/vi/${videoId}/maxresdefault_v.jpg`, `https://i.ytimg.com/vi/${videoId}/sddefault_v.jpg`, `https://i.ytimg.com/vi/${videoId}/hqdefault_v.jpg`, `https://i.ytimg.com/vi/${videoId}/mqdefault_v.jpg` ]; let thumbnailUrl = null; for (const url of thumbnailUrls) { const resp = await fetch(url, { method: 'HEAD' }); if (resp.ok) { thumbnailUrl = url; break; } } if (!thumbnailUrl) { throw new Error("Nenhuma thumbnail disponível para este vídeo."); } return { thumbnailUrl, videoId }; } async function showThumbnailMenu() { try { const { thumbnailUrl, videoId } = await getThumbnailUrl(); const menu = document.createElement('div'); menu.className = 'draggable-menu thumbnail-menu'; Object.assign(menu.style, { top: '50%', left: '50%', transform: 'translate(-50%, -50%)', textAlign: 'center' }); const h3 = document.createElement('h3'); h3.textContent = 'Thumbnail'; menu.appendChild(h3); const imageContainer = document.createElement('div'); imageContainer.className = 'image-container'; const img = document.createElement('img'); img.src = thumbnailUrl; img.onload = () => { const naturalWidth = img.naturalWidth; const naturalHeight = img.naturalHeight; h3.textContent = `Thumbnail melhor qualidade ${naturalWidth}x${naturalHeight}`; if (naturalWidth <= 960 && naturalHeight <= 460) { img.style.width = `${naturalWidth}px`; img.style.height = `${naturalHeight}px`; } else { const aspectRatio = naturalWidth / naturalHeight; if (naturalWidth > 960) { img.style.width = '960px'; img.style.height = `${960 / aspectRatio}px`; } if (parseInt(img.style.height) > 460) { img.style.height = '460px'; img.style.width = `${460 * aspectRatio}px`; } } }; Object.assign(img.style, { maxWidth: '960px', maxHeight: '460px' }); imageContainer.appendChild(img); menu.appendChild(imageContainer); const buttonContainer = document.createElement('div'); buttonContainer.className = 'button-container'; const downloadBtn = document.createElement('button'); downloadBtn.textContent = 'Baixar'; downloadBtn.className = 'btn-default'; downloadBtn.addEventListener('click', () => { GM_download({ url: thumbnailUrl, name: `thumbnail_${videoId}.jpg`, saveAs: true }); }); buttonContainer.appendChild(downloadBtn); const closeBtn = document.createElement('button'); closeBtn.textContent = 'Fechar'; closeBtn.className = 'btn-toggle'; closeBtn.addEventListener('click', () => menu.remove()); buttonContainer.appendChild(closeBtn); menu.appendChild(buttonContainer); document.body.appendChild(menu); makeDraggable(menu); } catch (error) { showOverlayConfirmationAutoClose("Aviso", error.message); } } async function downloadThumbnail() { try { const { thumbnailUrl, videoId } = await getThumbnailUrl(); GM_download({ url: thumbnailUrl, name: `thumbnail_${videoId}.jpg`, saveAs: true }); showOverlayConfirmationAutoClose("Thumbnail Baixada!", "A thumbnail foi baixada com sucesso."); } catch (error) { showOverlayConfirmationAutoClose("Aviso", error.message); } } async function copyThumbnailImage() { if (!location.hostname.includes('youtube.com')) { showOverlayConfirmationAutoClose("Aviso", "Somente no YouTube."); return; } try { const { thumbnailUrl } = await getThumbnailUrl(); const img = new Image(); img.crossOrigin = "Anonymous"; img.src = thumbnailUrl; await new Promise((resolve, reject) => { img.onload = resolve; img.onerror = reject; }); const canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png")); const clipboardItem = new ClipboardItem({ "image/png": blob }); await navigator.clipboard.write([clipboardItem]); showOverlayConfirmationAutoClose("Thumbnail Copiada!", "A imagem foi copiada para a área de transferência."); } catch (error) { showOverlayConfirmationAutoClose("Aviso", error.message); } } function getVideoIdFromUrl() { const url = new URL(window.location.href); if (url.pathname.includes('/shorts/')) { return url.pathname.split('/shorts/')[1]?.split('?')[0]; } const urlParams = new URLSearchParams(url.search); return urlParams.get('v'); } async function scribdDownLogic() { if (!location.hostname.includes('scribd.com')) { showOverlayConfirmationAutoClose("Aviso", "Somente no Scribd."); return; } const url = window.location.href; const match = url.match(/\/document\/(\d+)\/([^\/]+)/); if (match) { const [, documentId, documentName] = match; const newUrl = `https://scribd.downloader.tips/document/${documentId}/${documentName}`; window.open(newUrl, '_blank'); } else { showOverlayConfirmationAutoClose("Erro", "Não foi possível encontrar o ID/nome do documento na URL."); } } function createScribdDownButton() { const btn = document.createElement('button'); btn.textContent = 'Scribd down'; btn.className = 'btn-default'; btn.title = 'Estando na página de leitura, baixa o livro'; btn.addEventListener('click', scribdDownLogic); return btn; } function createZLibraryButton() { const btn = document.createElement('button'); btn.textContent = 'Z-Library'; btn.className = 'btn-default'; btn.title = 'Baixa o livro'; btn.addEventListener('click', () => { const originalDownloadLink = document.querySelector('a.addDownloadedBook')?.href; if (originalDownloadLink) { window.open(originalDownloadLink, '_blank'); } else { showOverlayConfirmationAutoClose("Erro", "Link de download da Z-Library não encontrado."); } }); return btn; } function createYoutubeCopyLinkYTButton() { const btn = document.createElement('button'); btn.textContent = 'copy-link-yt'; btn.className = 'btn-default'; btn.title = 'Copia o link do YouTube já com o comando de download'; btn.addEventListener('click', () => { const url = window.location.href; if (location.hostname.includes('youtube.com')) { const command = `yt-dlp -f "136+140" ${url}`; GM_setClipboard(command, 'text'); showOverlayConfirmationAutoClose("Link Copiado!", "O link com comando yt-dlp foi copiado para a área de transferência."); } else { GM_setClipboard(url, 'text'); showOverlayConfirmationAutoClose("Aviso", "Não é YouTube. Link simples foi copiado para a área de transferência."); } }); return btn; } function createCopyLinkButton() { const btn = document.createElement('button'); btn.textContent = 'copy-link'; btn.className = 'btn-default'; btn.title = 'Copia o link normal atual'; btn.addEventListener('click', () => { GM_setClipboard(window.location.href, 'text'); showOverlayConfirmationAutoClose("Link Copiado!", "O link foi copiado para a área de transferência."); }); return btn; } function createActivateClickCopyButton() { const btn = document.createElement('button'); btn.textContent = 'Ativa click-copy'; btn.className = 'btn-default'; btn.title = 'Remove bloqueio de selecionar e copiar'; btn.addEventListener('click', activateClickCopy); return btn; } function activateClickCopy() { const styleElement = document.createElement("style"); styleElement.type = "text/css"; styleElement.textContent = `* { -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; user-select: text !important; }`; document.head.appendChild(styleElement); document.oncontextmenu = null; document.onselectstart = null; document.ondragstart = null; document.onmousedown = null; if (document.body) { document.body.oncontextmenu = null; document.body.onselectstart = null; document.body.ondragstart = null; document.body.onmousedown = null; document.body.oncut = null; document.body.oncopy = null; document.body.onpaste = null; } ['copy', 'cut', 'paste', 'select', 'selectstart'].forEach(eventType => { document.addEventListener(eventType, e => e.stopPropagation(), true); }); showOverlayConfirmationAutoClose("Ativa click-copy", "Agora pode selecionar e copiar!"); } function togglePaywallOff() { if (window.location.href.includes("page.ke/urls/read")) { const params = new URLSearchParams(window.location.search); const originalUrl = params.get("url"); if (originalUrl) { window.location.href = originalUrl; } } else { const currentUrl = window.location.href; const pageTitle = document.title; const noPaywallUrl = `https://page.ke/urls/read?url=${encodeURIComponent(currentUrl)}&title=${encodeURIComponent(pageTitle)}`; window.location.href = noPaywallUrl; } } function updatePaywallOffButtonStyle(btn) { if (window.location.href.includes("page.ke/urls/read")) { btn.style.backgroundColor = "#00ff00"; btn.style.color = "#000"; btn.textContent = "Paywall OFF"; btn.setAttribute("data-active", "true"); } else { btn.style.backgroundColor = "#ff0000"; btn.style.color = "#fff"; btn.textContent = "Paywall"; btn.removeAttribute("data-active"); } } function createPaywallOffButton() { const btn = document.createElement('button'); btn.setAttribute('translate', 'no'); btn.className = 'btn-default'; btn.title = 'Tenta remover o bloqueio que exige assinatura em sites de notícias'; btn.addEventListener('click', togglePaywallOff); updatePaywallOffButtonStyle(btn); setInterval(() => updatePaywallOffButtonStyle(btn), 500); return btn; } function createSubsTxtPuroButton() { const btn = document.createElement('button'); btn.textContent = 'Subs → txt-puro'; btn.className = 'btn-default'; btn.title = 'Copia transcrição em texto puro e em linha única'; btn.addEventListener('click', showSubsTxtMenu); return btn; } async function showSubsTxtMenu() { if (!location.hostname.includes('youtube.com')) { showOverlayConfirmationAutoClose("Aviso", "Somente no YouTube."); return; } try { const subtitles = await getAvailableSubtitles(); if (subtitles.length === 0) { alert("Nenhuma legenda disponível para este vídeo."); return; } const menu = createSubsTxtMenu(subtitles); document.body.appendChild(menu); } catch (error) { alert(`Erro: ${error.message}`); } } function createSubsTxtMenu(subtitles) { const menu = document.createElement('div'); menu.className = 'draggable-menu'; Object.assign(menu.style, { top: '20%', left: '50%', transform: 'translateX(-50%)', textAlign: 'center', minWidth: '300px' }); const header = document.createElement('h3'); header.textContent = 'Legendas TXT-puro Disponíveis'; Object.assign(header.style, { fontSize: '12px' }); menu.appendChild(header); const container = document.createElement('div'); container.className = 'container'; subtitles.forEach(subtitle => { const div = document.createElement('div'); div.style.marginBottom = '10px'; const label = document.createElement('span'); label.textContent = subtitle.languageName + (subtitle.kind ? " (ASR)" : " (Dono)"); Object.assign(label.style, { display: 'block', marginBottom: '5px', color: ((subtitle.languageName.toLowerCase().includes("português") && !subtitle.kind) || subtitle.languageName.toLowerCase().includes("inglês")) ? "yellow" : "white", fontSize: '10px' }); div.appendChild(label); const btnDownload = document.createElement('button'); btnDownload.textContent = 'Baixar TXT-puro'; btnDownload.className = 'btn-default'; btnDownload.addEventListener('click', async (event) => { event.stopPropagation(); await downloadSubtitleTxt(subtitle); }); div.appendChild(btnDownload); container.appendChild(div); }); menu.appendChild(container); const btnFechar = document.createElement('button'); btnFechar.textContent = 'Fechar'; Object.assign(btnFechar.style, { background: '#ffff00', border: 'none', color: '#000', padding: '3px 8px', cursor: 'pointer', borderRadius: '3px', fontSize: '10px', marginTop: '10px' }); btnFechar.addEventListener('click', () => menu.remove()); menu.appendChild(btnFechar); makeDraggable(menu); return menu; } function createYoutubeTranscriptButton() { const btn = document.createElement('button'); btn.textContent = '📥 Transcrição'; btn.className = 'btn-default'; btn.title = 'Copia a transcrição'; btn.addEventListener('click', copyTranscript); return btn; } function createYoutubeLegendasButton() { const btn = document.createElement('button'); btn.textContent = '📖 Legendas'; btn.className = 'btn-default'; btn.title = 'Copia legendas'; btn.addEventListener('click', showLegendasMenu); return btn; } function createYoutubeThumbnailButton() { const btn = document.createElement('button'); btn.textContent = '🖼️ Thumbnail'; btn.className = 'btn-default'; btn.title = 'Veja a thumbnail e baixe'; btn.addEventListener('click', showThumbnailMenu); return btn; } function createYoutubeCopyThumbButton() { const btn = document.createElement('button'); btn.textContent = '📋 Copy Thumb'; btn.className = 'btn-default'; btn.title = 'Copia a thumbnail diretamente'; btn.addEventListener('click', copyThumbnailImage); return btn; } function createYoutubeMenuButton() { const container = document.createElement('div'); container.style.position = 'relative'; container.style.display = 'inline-block'; const mainBtn = document.createElement('button'); mainBtn.textContent = 'YOUTUBE'; mainBtn.className = 'btn-default'; mainBtn.title = 'Menu com funções do YouTube'; mainBtn.addEventListener('click', (e) => { e.stopPropagation(); if (submenu.style.visibility === 'visible') { submenu.style.visibility = 'hidden'; submenu.style.opacity = '0'; mainBtn.removeAttribute("data-active"); } else { submenu.style.visibility = 'visible'; submenu.style.opacity = '1'; mainBtn.setAttribute("data-active", "true"); } }); container.appendChild(mainBtn); const submenu = document.createElement('div'); submenu.style.position = 'fixed'; submenu.style.visibility = 'hidden'; submenu.style.opacity = '0'; submenu.style.backgroundColor = '#000'; submenu.style.border = '1px solid #fff'; submenu.style.borderRadius = '4px'; submenu.style.padding = '8px'; submenu.style.zIndex = '10000001'; submenu.style.transition = 'opacity 0.2s'; submenu.style.minWidth = '120px'; submenu.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)'; const btnComentarios = createComentariosButton(); const btnTranscript = createYoutubeTranscriptButton(); const btnLegendas = createYoutubeLegendasButton(); const btnSubsTxtPuro = createSubsTxtPuroButton(); const btnThumbnail = createYoutubeThumbnailButton(); const btnCopyThumb = createYoutubeCopyThumbButton(); const btnCopyLinkYT = createYoutubeCopyLinkYTButton(); [btnComentarios, btnTranscript, btnLegendas, btnSubsTxtPuro, btnThumbnail, btnCopyThumb, btnCopyLinkYT].forEach(btn => { btn.style.display = 'block'; btn.style.width = '100%'; btn.style.margin = '4px 0'; submenu.appendChild(btn); }); mainBtn.addEventListener('click', (e) => { const rect = mainBtn.getBoundingClientRect(); submenu.style.top = (rect.bottom + 5) + 'px'; submenu.style.left = rect.left + 'px'; }); document.body.appendChild(submenu); document.addEventListener('click', function(event) { if (event.target !== mainBtn && !submenu.contains(event.target)) { submenu.style.visibility = 'hidden'; submenu.style.opacity = '0'; mainBtn.removeAttribute("data-active"); } }); return container; } // Função para criar o menu PDFs (submenu) function createPDFsMenuButton() { const container = document.createElement('div'); container.style.position = 'relative'; container.style.display = 'inline-block'; const mainBtn = document.createElement('button'); mainBtn.textContent = 'PDFs'; mainBtn.className = 'btn-default'; mainBtn.title = 'Menu com opções de downloads de PDFs'; mainBtn.addEventListener('click', (e) => { e.stopPropagation(); if (submenu.style.visibility === 'visible') { submenu.style.visibility = 'hidden'; submenu.style.opacity = '0'; mainBtn.removeAttribute("data-active"); } else { submenu.style.visibility = 'visible'; submenu.style.opacity = '1'; mainBtn.setAttribute("data-active", "true"); } }); container.appendChild(mainBtn); const submenu = document.createElement('div'); submenu.style.position = 'fixed'; submenu.style.visibility = 'hidden'; submenu.style.opacity = '0'; submenu.style.backgroundColor = '#000'; submenu.style.border = '1px solid #fff'; submenu.style.borderRadius = '4px'; submenu.style.padding = '8px'; submenu.style.zIndex = '10000001'; submenu.style.transition = 'opacity 0.2s'; submenu.style.minWidth = '120px'; submenu.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)'; const btnScribdDown = createScribdDownButton(); const btnZLibrary = createZLibraryButton(); [btnScribdDown, btnZLibrary].forEach(btn => { btn.style.display = 'block'; btn.style.width = '100%'; btn.style.margin = '4px 0'; submenu.appendChild(btn); }); mainBtn.addEventListener('click', (e) => { const rect = mainBtn.getBoundingClientRect(); submenu.style.top = (rect.bottom + 5) + 'px'; submenu.style.left = rect.left + 'px'; }); document.body.appendChild(submenu); document.addEventListener('click', function(event) { if (event.target !== mainBtn && !submenu.contains(event.target)) { submenu.style.visibility = 'hidden'; submenu.style.opacity = '0'; mainBtn.removeAttribute("data-active"); } }); return container; } // Turbo Loader function createTurboLoaderButton() { const btn = document.createElement('button'); btn.textContent = 'Turbo Loader'; btn.className = 'btn-default'; btn.title = 'Acelera o carregamento de sites e imagens'; btn.id = 'btnTurboLoader'; // Verificar estado salvo const isTurboActive = GM_getValue('turbo_loader_active', false); if (isTurboActive) { btn.setAttribute("data-active", "true"); btn.style.backgroundColor = '#00ff00'; btn.style.color = '#000'; } btn.addEventListener('click', toggleTurboLoader); return btn; } let turboLoaderActive = GM_getValue('turbo_loader_active', false); let turboObserver = null; let activeRequests = 0; const TURBO_CONFIG = { maxThreads: 8, requestDelay: 50, chunkSize: 10, maxActiveRequests: 20 }; function toggleTurboLoader() { const btn = document.getElementById('btnTurboLoader'); turboLoaderActive = !turboLoaderActive; if (turboLoaderActive) { // Não ativar no YouTube if (window.location.hostname.includes('youtube.com')) { showOverlayConfirmationAutoClose("Aviso", "Turbo Loader não funciona no YouTube"); turboLoaderActive = false; GM_setValue('turbo_loader_active', false); return; } btn.setAttribute("data-active", "true"); btn.style.backgroundColor = '#00ff00'; btn.style.color = '#000'; initTurboLoader(); showOverlayConfirmationAutoClose("Turbo Loader Ativado", "Carregamento acelerado está ligado"); } else { btn.removeAttribute("data-active"); btn.style.backgroundColor = '#ff0000'; btn.style.color = '#fff'; disableTurboLoader(); showOverlayConfirmationAutoClose("Turbo Loader Desativado", "Carregamento acelerado está desligado"); } // Salvar estado GM_setValue('turbo_loader_active', turboLoaderActive); } function initTurboLoader() { if (window.location.hostname.includes('youtube.com')) return; if (turboObserver) turboObserver.disconnect(); // Primeiro, aceleramos as imagens e recursos já existentes accelerateExistingResources(); // Observer para novos elementos turboObserver = new MutationObserver(mutations => { if (!turboLoaderActive) return; mutations.forEach(({addedNodes}) => { addedNodes.forEach(node => { if (node.nodeType === 1) { // ELEMENT_NODE accelerateElement(node); // Procurar por elementos dentro do nó adicionado if (node.querySelectorAll) { node.querySelectorAll('img, link[rel="stylesheet"], script[src]').forEach(el => { accelerateElement(el); }); } } }); }); }); turboObserver.observe(document, { childList: true, subtree: true }); // Interceptar fetch e XMLHttpRequest setupFetchInterceptor(); } function disableTurboLoader() { if (turboObserver) { turboObserver.disconnect(); turboObserver = null; } // Restaurar os interceptadores originais if (window.originalFetch) { window.fetch = window.originalFetch; } } function accelerateExistingResources() { // Acelerar imagens document.querySelectorAll('img:not([data-turbo-accelerated])').forEach(img => { accelerateElement(img); }); // Acelerar CSS document.querySelectorAll('link[rel="stylesheet"]:not([data-turbo-accelerated])').forEach(link => { accelerateElement(link); }); // Acelerar scripts (com cuidado) document.querySelectorAll('script[src]:not([data-turbo-accelerated])').forEach(script => { accelerateElement(script); }); } function accelerateElement(element) { if (!element || element.hasAttribute('data-turbo-accelerated')) return; element.setAttribute('data-turbo-accelerated', 'true'); if (element.tagName === 'IMG' && element.src && !element.src.startsWith('data:')) { accelerateImage(element); } else if (element.tagName === 'LINK' && element.rel === 'stylesheet' && element.href) { prefetchResource(element.href, 'style'); } else if (element.tagName === 'SCRIPT' && element.src) { prefetchResource(element.src, 'script'); } } function accelerateImage(img) { if (!img.src || img.complete || img.src.startsWith('data:')) return; // Não mexer com vídeos ou YouTube if ( img.closest('video') || window.location.hostname.includes('youtube.com') || img.src.includes('youtube.com') || img.src.includes('youtu.be') ) return; // Para imagens, vamos fazer um fetch em chunks if (activeRequests < TURBO_CONFIG.maxActiveRequests) { activeRequests++; const originalSrc = img.src; // Adicionar parâmetro para evitar cache const turboSrc = originalSrc.includes('?') ? `${originalSrc}&turbo_nocache=${Date.now()}` : `${originalSrc}?turbo_nocache=${Date.now()}`; fetchInChunks(turboSrc) .then(blob => { if (blob) { const url = URL.createObjectURL(blob); const newImg = new Image(); newImg.onload = () => { if (img.parentNode) { img.src = url; } URL.revokeObjectURL(url); }; newImg.src = url; } }) .catch(() => {}) .finally(() => { activeRequests--; }); } } function fetchInChunks(url) { return new Promise((resolve, reject) => { try { const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'blob'; // Configurar para receber em chunks xhr.onprogress = function(event) { if (event.lengthComputable && event.loaded === event.total) { // Já temos tudo! resolve(xhr.response); } }; xhr.onload = function() { if (xhr.status === 200) { resolve(xhr.response); } else { reject(new Error(`Status ${xhr.status}`)); } }; xhr.onerror = reject; xhr.send(); } catch (e) { reject(e); } }); } function prefetchResource(url, type) { if (activeRequests >= TURBO_CONFIG.maxActiveRequests) return; activeRequests++; const link = document.createElement('link'); link.rel = 'prefetch'; link.as = type; link.href = url; link.onload = link.onerror = () => { activeRequests--; setTimeout(() => link.remove(), 5000); }; document.head.appendChild(link); } function setupFetchInterceptor() { // Salvar o fetch original if (!window.originalFetch) { window.originalFetch = window.fetch; } // Substituir o fetch window.fetch = function(...args) { if (!turboLoaderActive) { return window.originalFetch.apply(this, args); } const [resource, config] = args; // Não interferir com requisições para o YouTube if (typeof resource === 'string' && ( resource.includes('youtube.com') || resource.includes('youtu.be') || window.location.hostname.includes('youtube.com'))) { return window.originalFetch.apply(this, args); } // Para outras requisições, adicionar cabeçalhos de performance const newConfig = {...config}; if (!newConfig.headers) { newConfig.headers = {}; } // Isto ajuda a obter respostas mais rápidas if (newConfig.headers instanceof Headers) { newConfig.headers.append('Cache-Control', 'no-store'); newConfig.headers.append('Pragma', 'no-cache'); } else { newConfig.headers['Cache-Control'] = 'no-store'; newConfig.headers['Pragma'] = 'no-cache'; } // Adicionar timestamp para evitar cache let url = resource; if (typeof url === 'string') { url = url.includes('?') ? `${url}&_turbo=${Date.now()}` : `${url}?_turbo=${Date.now()}`; } // Criar promise com timeout mais curto return Promise.race([ window.originalFetch.call(this, url, newConfig), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 15000) ) ]).catch(() => window.originalFetch.apply(this, args)); }; } function createUrlBar() { if (!urlBarContainer) { urlBarContainer = document.createElement('div'); urlBarContainer.id = 'urlBarContainer'; Object.assign(urlBarContainer.style, { position: 'fixed', top: `${barOriginalHeight}px`, left: '0', width: '100%', backgroundColor: '#d3d3d3', zIndex: '9999999', padding: '5px 10px', boxSizing: 'border-box', borderBottom: '4px solid #000', display: 'none' }); const urlInput = document.createElement('input'); urlInput.type = 'text'; urlInput.id = 'fullUrlInput'; Object.assign(urlInput.style, { width: '100%', border: '1px solid #ccc', padding: '5px', fontSize: '10px', outline: 'none', boxSizing: 'border-box', backgroundColor: '#fff', color: '#000' }); urlInput.value = window.location.href; urlInput.addEventListener('click', () => urlInput.select()); urlInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { let newUrl = urlInput.value.trim(); if (!newUrl.match(/^https?:\/\//)) { newUrl = 'https://' + newUrl; } try { window.location.href = newUrl; } catch (err) { console.error('Erro ao navegar para o URL:', err); showOverlayConfirmationAutoClose("Erro", "URL inválido. Verifique e tente novamente."); } } }); urlBarContainer.appendChild(urlInput); document.body.appendChild(urlBarContainer); console.log('Barra de URL criada e adicionada ao DOM'); } return urlBarContainer; } function toggleUrlBar() { const btnUrlFull = document.getElementById('btnUrlFull'); if (!urlBarActive) { if (!urlBarContainer) { createUrlBar(); } urlBarContainer.style.display = 'block'; urlBarContainer.querySelector('#fullUrlInput').value = window.location.href; btnUrlFull.style.backgroundColor = '#00ff00'; btnUrlFull.style.color = '#000'; btnUrlFull.setAttribute('data-active', 'true'); urlBarActive = true; if (!barHidden) applyPushDownWithUrlBar(); console.log('Barra de URL exibida'); } else { urlBarContainer.style.display = 'none'; btnUrlFull.style.backgroundColor = '#ff0000'; btnUrlFull.style.color = '#fff'; btnUrlFull.removeAttribute('data-active'); urlBarActive = false; if (!barHidden) applyPushDown(); console.log('Barra de URL oculta'); } } function applyPushDownWithUrlBar() { console.log('Aplicando push down com barra de URL'); if (youtubeApp && youtubeMasthead) { const urlBarHeight = '80px'; // Altura da barra fixa (40px) + barra de URL (40px) youtubeMasthead.style.setProperty('margin-top', urlBarHeight, 'important'); console.log('Masthead ajustado para margin-top com URL bar:', urlBarHeight); const miniGuide = youtubeApp.querySelector('ytd-mini-guide-renderer, ytd-guide-renderer'); if (miniGuide) { miniGuide.style.setProperty('margin-top', urlBarHeight, 'important'); console.log('Mini-guide ajustado para margin-top com URL bar:', urlBarHeight); } const content = youtubeApp.querySelector('#content'); if (content) { content.style.setProperty('margin-top', `calc(${urlBarHeight} + 56px)`, 'important'); console.log('Content ajustado para margin-top com URL bar: calc(80px + 56px)'); } youtubeApp.style.paddingTop = '0'; } else if (protonHeader && protonMain) { updateMargin([protonHeader, protonMain], '80px'); } else { updateMargin([document.body], '80px'); } } function updateUrlBarOnNavigation() { if (urlBarActive && urlBarContainer) { urlBarContainer.querySelector('#fullUrlInput').value = window.location.href; console.log('URL atualizada na barra:', window.location.href); } } function createUrlFullButton() { const btn = document.createElement('button'); btn.id = 'btnUrlFull'; btn.textContent = 'URL-FULL'; btn.className = 'btn-default'; btn.title = 'Veja o link inteiro'; btn.addEventListener('click', toggleUrlBar); return btn; } let btnIMGs; let imgsActive = false; function getBestSrcset(img) { if (img.srcset) { const srcsetEntries = img.srcset.split(',').map(entry => entry.trim().split(' ')); let bestUrl = img.src; let maxWidth = 0; for (const [url, descriptor] of srcsetEntries) { if (descriptor) { const width = parseFloat(descriptor.replace('w', '')); if (width > maxWidth) { maxWidth = width; bestUrl = url; } } else { bestUrl = url; } } return bestUrl; } return img.src; } function blobToBase64(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); }); } function addImgOverlays() { const allImgs = document.querySelectorAll("img"); allImgs.forEach(img => { const parent = img.parentElement; if (window.getComputedStyle(parent).position === "static") { parent.style.position = "relative"; } if (!parent.querySelector(".img-overlay-container")) { const overlayContainer = document.createElement("div"); overlayContainer.classList.add("img-overlay-container"); Object.assign(overlayContainer.style, { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", display: "flex", gap: "10px", zIndex: "10000" }); const overlayLink = document.createElement("div"); overlayLink.textContent = "🔗"; Object.assign(overlayLink.style, { backgroundColor: "rgba(0,0,0,0.5)", color: "white", padding: "2px 4px", fontSize: "12px", cursor: "pointer" }); overlayLink.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); const url = getBestSrcset(img); console.log("Tentando copiar URL direto da imagem:", url); if (url) { navigator.clipboard.writeText(url).then(() => { overlayLink.style.backgroundColor = "#01cd5d"; setTimeout(() => overlayLink.style.backgroundColor = "rgba(0,0,0,0.5)", 500); }).catch(err => { console.error("Falha ao copiar o link da imagem:", err); showOverlayConfirmationAutoClose("Erro", "Não foi possível copiar o link da imagem."); }); } else { console.error("Nenhum URL direto encontrado para a imagem."); showOverlayConfirmationAutoClose("Erro", "Imagem sem URL direto disponível."); } }); overlayContainer.appendChild(overlayLink); const overlayCopy = document.createElement("div"); overlayCopy.textContent = "📋"; Object.assign(overlayCopy.style, { backgroundColor: "rgba(0,0,0,0.5)", color: "white", padding: "2px 4px", fontSize: "12px", cursor: "pointer" }); overlayCopy.addEventListener("click", async (e) => { e.preventDefault(); e.stopPropagation(); const url = getBestSrcset(img); console.log("Tentando copiar imagem da URL:", url); const tryStandardMethod = () => { return new Promise((resolve, reject) => { const image = new Image(); image.crossOrigin = "Anonymous"; image.src = url; image.onload = () => resolve(image); image.onerror = () => reject(new Error("Falha ao carregar a imagem (CORS ou outro erro)")); }); }; const tryGMXmlHttpRequest = () => { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: url, headers: { "Referer": window.location.origin + "/" }, responseType: "blob", onload: (response) => { if (response.status === 200) { blobToBase64(response.response).then(base64 => { const image = new Image(); image.src = base64; image.onload = () => resolve(image); image.onerror = () => reject(new Error("Falha ao carregar a imagem do base64")); }).catch(err => reject(err)); } else { reject(new Error(`Falha na requisição GM_xmlhttpRequest: Status ${response.status}`)); } }, onerror: () => reject(new Error("Erro na requisição GM_xmlhttpRequest")) }); }); }; try { let image; try { image = await tryStandardMethod(); console.log("Método padrão bem-sucedido para:", url); } catch (err) { console.log("Método padrão falhou, tentando GM_xmlhttpRequest:", err); image = await tryGMXmlHttpRequest(); console.log("Método GM_xmlhttpRequest bem-sucedido para:", url); } const canvas = document.createElement("canvas"); canvas.width = image.width; canvas.height = image.height; const ctx = canvas.getContext("2d"); ctx.drawImage(image, 0, 0); const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png")); const item = new ClipboardItem({ "image/png": blob }); await navigator.clipboard.write([item]); overlayCopy.style.backgroundColor = "#01cd5d"; setTimeout(() => overlayCopy.style.backgroundColor = "rgba(0,0,0,0.5)", 500); } catch (err) { console.error("Erro ao copiar imagem:", err); showOverlayConfirmationAutoClose("Erro", "Não foi possível copiar a imagem: " + err.message); } }); overlayContainer.appendChild(overlayCopy); parent.appendChild(overlayContainer); } }); } function removeImgOverlays() { const allContainers = document.querySelectorAll(".img-overlay-container"); allContainers.forEach(el => el.remove()); } function toggleImgs() { imgsActive = !imgsActive; if (imgsActive) { btnIMGs.setAttribute("data-active", "true"); addImgOverlays(); } else { btnIMGs.removeAttribute("data-active"); removeImgOverlays(); } } let btnDataExposed; let dataExposedActive = false; let dataExposedMenu = null; async function collectExposedData() { const startTime = performance.now(); const availableData = []; const unavailableData = []; let ipAddress = "Não disponível diretamente (requer API externa)"; try { const ipResponse = await fetch('https://api.ipify.org?format=json'); const ipData = await ipResponse.json(); ipAddress = ipData.ip; } catch (e) { console.log("Erro ao obter IP:", e); } if (ipAddress === "Não disponível diretamente (requer API externa)") { unavailableData.push(`Endereço IP: ${ipAddress} - Identifica sua conexão na internet.`); } else { availableData.push(`Endereço IP: ${ipAddress} - Identifica sua conexão na internet.`); } let geolocation = "Não disponível (permissão não concedida)"; if (navigator.geolocation) { try { const position = await new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition(resolve, reject, { timeout: 5000 }); }); geolocation = `Latitude: ${position.coords.latitude}, Longitude: ${position.coords.longitude}`; } catch (e) { console.log("Erro ao obter geolocalização:", e); } } if (geolocation === "Não disponível (permissão não concedida)") { unavailableData.push(`Localização geográfica: ${geolocation} - Rastreia sua posição física.`); } else { availableData.push(`Localização geográfica: ${geolocation} - Rastreia sua posição física.`); } const userAgent = navigator.userAgent; let browserName = "Desconhecido"; if (navigator.brave && await navigator.brave.isBrave()) { browserName = "Brave"; } else if (userAgent.includes("Vivaldi")) { browserName = "Vivaldi"; } else if (userAgent.includes("UCBrowser")) { browserName = "UC Browser"; } else if (userAgent.includes("SamsungBrowser")) { browserName = "Samsung Internet"; } else if (userAgent.includes("Opera") || userAgent.includes("OPR")) { browserName = "Opera"; } else if (userAgent.includes("Edg")) { browserName = "Microsoft Edge"; } else if (userAgent.includes("Firefox") && !userAgent.includes("Seamonkey")) { browserName = "Firefox"; } else if (userAgent.includes("Chrome") && !userAgent.includes("Chromium")) { browserName = "Google Chrome"; } else if (userAgent.includes("Chromium")) { browserName = "Chromium"; } else if (userAgent.includes("Safari") && !userAgent.includes("Chrome") && !userAgent.includes("Chromium")) { browserName = "Safari"; } else if (userAgent.includes("Seamonkey")) { browserName = "SeaMonkey"; } else if (userAgent.includes("Tor")) { browserName = "Tor Browser"; } availableData.push(`Navegador detectado: ${browserName} - Identificado a partir do User Agent: ${userAgent}`); const osMatch = navigator.userAgent.match(/(Windows|Mac OS|Linux|Android|iOS)/i); const os = osMatch ? osMatch[0] : "Desconhecido"; availableData.push(`Sistema operacional: ${os} - Revela o SO em uso.`); availableData.push(`Resolução da tela: ${window.screen.width}x${window.screen.height} - Tamanho da tela.`); availableData.push(`Idioma do navegador: ${navigator.language || navigator.userLanguage} - Idioma preferido.`); availableData.push(`Cookies: ${document.cookie || "Nenhum cookie visível"} - Armazena informações de sessão.`); const sessionData = Object.entries(sessionStorage).map(([key, value]) => `${key}: ${value}`).join("; ") || "Nenhum dado"; availableData.push(`Dados de sessão (sessionStorage): ${sessionData} - Informações temporárias.`); const localData = Object.entries(localStorage).map(([key, value]) => `${key}: ${value}`).join("; ") || "Nenhum dado"; availableData.push(`Armazenamento local (localStorage): ${localData} - Informações persistentes.`); availableData.push(`URL atual: ${window.location.href} - Página visitada.`); availableData.push(`URL de referência: ${document.referrer || "Direto"} - Origem da navegação.`); const formData = Array.from(document.querySelectorAll('input, textarea')).map(input => `${input.name || input.id || "sem nome"}: ${input.value || "vazio"}`).join("; ") || "Nenhum formulário"; availableData.push(`Dados de formulários preenchidos: ${formData} - Campos preenchidos na página.`); let batteryInfo = "Não disponível (API Battery Status não suportada)"; if (navigator.getBattery) { try { const battery = await navigator.getBattery(); batteryInfo = `Nível: ${(battery.level * 100).toFixed(0)}%, Carregando: ${battery.charging ? "Sim" : "Não"}`; } catch (e) { console.log("Erro ao obter bateria:", e); } } if (batteryInfo === "Não disponível (API Battery Status não suportada)") { unavailableData.push(`Bateria do dispositivo: ${batteryInfo} - Estado da bateria.`); } else { availableData.push(`Bateria do dispositivo: ${batteryInfo} - Estado da bateria.`); } let usbDevices = "Não disponível (sem permissão ou suporte)"; if (navigator.usb) { try { const devices = await navigator.usb.getDevices(); usbDevices = devices.length > 0 ? devices.map(d => `${d.productName || "Desconhecido"} (ID: ${d.productId})`).join("; ") : "Nenhum dispositivo USB detectado"; if (usbDevices === "Nenhum dispositivo USB detectado") { usbDevices += " - Acesso bloqueado pelas Big Techs! Dispositivos como pen drives, mouse e teclado estão ocultos por padrão, exigindo permissão explícita."; } } catch (e) { console.log("Erro ao obter dispositivos USB:", e); usbDevices = "Erro ao acessar dispositivos USB - Controle restrito pelas Big Techs!"; } } if (usbDevices === "Não disponível (sem permissão ou suporte)") { unavailableData.push(`Dispositivos USB conectados: ${usbDevices} - Dispositivos USB detectados.`); } else { availableData.push(`Dispositivos USB conectados: ${usbDevices} - Dispositivos USB detectados.`); } let bluetoothDevices = "Não disponível (sem permissão ou suporte)"; if (navigator.bluetooth) { try { const device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true }); bluetoothDevices = device.name || "Dispositivo Bluetooth genérico"; } catch (e) { console.log("Erro ao obter dispositivos Bluetooth:", e); bluetoothDevices = "Permissão negada ou erro"; } } if (bluetoothDevices === "Não disponível (sem permissão ou suporte)" || bluetoothDevices === "Permissão negada ou erro") { unavailableData.push(`Dispositivos Bluetooth conectados: ${bluetoothDevices} - Dispositivos Bluetooth detectados.`); } else { availableData.push(`Dispositivos Bluetooth conectados: ${bluetoothDevices} - Dispositivos Bluetooth detectados.`); } const cpuCores = navigator.hardwareConcurrency || "Desconhecido"; if (cpuCores === "Desconhecido") { unavailableData.push(`Número de CPUs: ${cpuCores} - Núcleos disponíveis (As Big Techs escondem isso, mas ainda podem acessar seus dados de hardware para lucrar!).`); } else { availableData.push(`Número de CPUs: ${cpuCores} - Núcleos disponíveis (Vazado para as Big Techs sem seu consentimento, usado para rastreamento e lucro!).`); } const deviceMemory = navigator.deviceMemory || "Não disponível"; if (deviceMemory === "Não disponível") { unavailableData.push(`Memória do dispositivo: ${deviceMemory} GB - Estimativa de RAM (As Big Techs limitam o que você vê, mas ainda coletam seus dados de memória para venda!).`); } else { availableData.push(`Memória do dispositivo: ${deviceMemory} GB - Estimativa de RAM (Exposto às Big Techs sem permissão, comercializado para lucrar com seu perfil!).`); } let webrtcLeaks = "Não disponível"; if (window.RTCPeerConnection) { try { const pc = new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }] }); pc.createDataChannel(""); await pc.createOffer().then(offer => pc.setLocalDescription(offer)); await new Promise(resolve => setTimeout(resolve, 1000)); const sdp = pc.localDescription.sdp; const ipRegex = /(\d+\.\d+\.\d+\.\d+)/g; const ips = sdp.match(ipRegex) || []; webrtcLeaks = ips.length > 0 ? ips.join(", ") : "Nenhum IP detectado diretamente"; pc.close(); } catch (e) { console.log("Erro ao obter WebRTC Leaks:", e); } } if (webrtcLeaks === "Não disponível") { unavailableData.push(`WebRTC Leaks: ${webrtcLeaks} - IPs locais/públicos expostos.`); } else { availableData.push(`WebRTC Leaks: ${webrtcLeaks} - IPs locais/públicos expostos.`); } const dnsPrefetch = Array.from(document.querySelectorAll('link[rel="dns-prefetch"]')).map(link => link.href).join("; ") || "Nenhum detectado"; availableData.push(`DNS Prefetching: ${dnsPrefetch} - Domínios pré-carregados.`); const fontCheck = ["Arial", "Times New Roman", "Comic Sans MS"].map(font => { const span = document.createElement("span"); span.style.fontFamily = font; document.body.appendChild(span); const available = window.getComputedStyle(span).fontFamily.includes(font); span.remove(); return available ? font : null; }).filter(Boolean).join(", ") || "Nenhum teste conclusivo"; availableData.push(`Fontes instaladas (exemplo): ${fontCheck} - Usado para fingerprinting.`); const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); ctx.font = "14px Arial"; ctx.fillText("Fingerprint Test", 2, 20); const canvasFingerprint = canvas.toDataURL(); availableData.push(`Canvas Fingerprinting: ${canvasFingerprint.slice(0, 50)}... - Hash único de renderização.`); let webglInfo = "Não disponível"; if (canvas.getContext('webgl')) { const gl = canvas.getContext('webgl'); webglInfo = `Vendor: ${gl.getParameter(gl.VENDOR)}, Renderer: ${gl.getParameter(gl.RENDERER)}`; } if (webglInfo === "Não disponível") { unavailableData.push(`WebGL Fingerprinting: ${webglInfo} - Detalhes da GPU.`); } else { availableData.push(`WebGL Fingerprinting: ${webglInfo} - Detalhes da GPU.`); } let audioFingerprint = "Não disponível"; try { const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioCtx.createOscillator(); oscillator.type = "sine"; oscillator.frequency.value = 440; const analyser = audioCtx.createAnalyser(); oscillator.connect(analyser); analyser.connect(audioCtx.destination); oscillator.start(); const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); analyser.getByteFrequencyData(dataArray); audioFingerprint = dataArray.join(",").slice(0, 50) + "..."; oscillator.stop(); audioCtx.close(); } catch (e) { console.log("Erro ao obter AudioContext Fingerprint:", e); } if (audioFingerprint === "Não disponível") { unavailableData.push(`AudioContext Fingerprinting: ${audioFingerprint} - Variações de áudio.`); } else { availableData.push(`AudioContext Fingerprinting: ${audioFingerprint} - Variações de áudio.`); } let connectionType = "Não disponível"; let connectionSpeed = "Não disponível"; if (navigator.connection) { connectionType = navigator.connection.effectiveType || "Desconhecido"; connectionSpeed = `${navigator.connection.downlink || "N/A"} Mbps`; } if (connectionType === "Não disponível") { unavailableData.push(`Tipo de conexão: ${connectionType} - Tipo de rede (Wi-Fi, 4G, etc.).`); } else { availableData.push(`Tipo de conexão: ${connectionType} - Tipo de rede (Wi-Fi, 4G, etc.).`); } if (connectionSpeed === "Não disponível") { unavailableData.push(`Velocidade de conexão: ${connectionSpeed} - Largura de banda estimada.`); } else { availableData.push(`Velocidade de conexão: ${connectionSpeed} - Largura de banda estimada.`); } const networkInfo = webrtcLeaks !== "Não disponível" ? "Detectável via WebRTC" : "Não disponível"; if (networkInfo === "Não disponível") { unavailableData.push(`Informações de rede (NAT/Firewall): ${networkInfo} - Configurações de rede.`); } else { availableData.push(`Informações de rede (NAT/Firewall): ${networkInfo} - Configurações de rede.`); } const screenOrientation = screen.orientation?.type || "Desconhecido"; availableData.push(`Orientação da tela: ${screenOrientation} - Retrato ou paisagem.`); let sensorData = "Não disponível"; try { window.addEventListener('deviceorientation', (event) => { sensorData = `Alpha: ${event.alpha || "N/A"}, Beta: ${event.beta || "N/A"}, Gamma: ${event.gamma || "N/A"}`; }, { once: true }); } catch (e) { console.log("Erro ao obter dados de sensores:", e); } if (sensorData === "Não disponível") { unavailableData.push(`Sensores do dispositivo (Acelerômetro/Giroscópio): ${sensorData} - Movimentos do dispositivo.`); } else { availableData.push(`Sensores do dispositivo (Acelerômetro/Giroscópio): ${sensorData} - Movimentos do dispositivo.`); } let lightData = "Não disponível"; if (window.AmbientLightSensor) { try { const sensor = new AmbientLightSensor(); sensor.start(); await new Promise(resolve => sensor.onreading = resolve); lightData = `${sensor.illuminance} lux`; sensor.stop(); } catch (e) { console.log("Erro ao obter sensor de luz:", e); } } if (lightData === "Não disponível") { unavailableData.push(`Sensor de luz ambiente: ${lightData} - Nível de luminosidade.`); } else { availableData.push(`Sensor de luz ambiente: ${lightData} - Nível de luminosidade.`); } let proximityData = "Não disponível"; if (window.ProximitySensor) { try { const sensor = new ProximitySensor(); sensor.start(); await new Promise(resolve => sensor.onreading = resolve); proximityData = `${sensor.distance} cm`; sensor.stop(); } catch (e) { console.log("Erro ao obter sensor de proximidade:", e); } } if (proximityData === "Não disponível") { unavailableData.push(`Sensor de proximidade: ${proximityData} - Distância de objetos.`); } else { availableData.push(`Sensor de proximidade: ${proximityData} - Distância de objetos.`); } const timing = performance.now(); availableData.push(`Tempo de resposta do sistema: ${timing.toFixed(2)}ms - Precisão de timers.`); unavailableData.push(`HTTP/3 e QUIC: Não detectável diretamente via JavaScript - Protocolos de conexão modernos.`); unavailableData.push(`TLS Fingerprinting: Não disponível (requer análise de handshake TLS) - Assinatura única de SSL/TLS.`); const hsts = document.location.protocol === "https:" ? "Possível via HSTS" : "Não detectado"; availableData.push(`HSTS Super Cookies: ${hsts} - Rastreamento via HSTS.`); unavailableData.push(`ETag Tracking: Não disponível diretamente (requer análise de headers) - Identificadores persistentes.`); unavailableData.push(`Cache Timing Attacks: Não implementado (requer teste específico) - Inferência de histórico.`); let keyDynamics = "Não disponível"; try { let keyTimes = []; document.addEventListener('keydown', (e) => { keyTimes.push(performance.now()); if (keyTimes.length > 2) keyTimes.shift(); keyDynamics = keyTimes.length > 1 ? `Diferença média: ${((keyTimes[1] - keyTimes[0]) / 1000).toFixed(3)}s` : "Insuficiente"; }, { once: true }); } catch (e) { console.log("Erro ao obter keyboard dynamics:", e); } if (keyDynamics === "Não disponível") { unavailableData.push(`Keyboard Dynamics: ${keyDynamics} - Padrões de digitação.`); } else { availableData.push(`Keyboard Dynamics: ${keyDynamics} - Padrões de digitação.`); } let mouseDynamics = "Não disponível"; try { let lastMove = 0; document.addEventListener('mousemove', (e) => { const now = performance.now(); if (lastMove) mouseDynamics = `Velocidade: ${((e.clientX - lastMove) / (now - lastMove)).toFixed(2)} px/ms`; lastMove = now; }, { once: true }); } catch (e) { console.log("Erro ao obter mouse dynamics:", e); } if (mouseDynamics === "Não disponível") { unavailableData.push(`Mouse Dynamics: ${mouseDynamics} - Padrões de movimento do cursor.`); } else { availableData.push(`Mouse Dynamics: ${mouseDynamics} - Padrões de movimento do cursor.`); } const permissionsCamera = await navigator.permissions.query({ name: "camera" }).then(res => `Câmera: ${res.state}`).catch(() => "Câmera: Não verificado"); const permissionsMic = await navigator.permissions.query({ name: "microphone" }).then(res => `Microfone: ${res.state}`).catch(() => "Microfone: Não verificado"); availableData.push(`Permissões concedidas: ${permissionsCamera}; ${permissionsMic} - Permissões do usuário.`); const webAuthn = navigator.credentials ? "Disponível (biometria/hardware)" : "Não disponível"; if (webAuthn === "Não disponível") { unavailableData.push(`WebAuthn Credentials: ${webAuthn} - Autenticação avançada.`); } else { availableData.push(`WebAuthn Credentials: ${webAuthn} - Autenticação avançada.`); } const crossOrigin = Array.from(document.querySelectorAll('script[src], img[src], link[href]')).map(el => el.src || el.href).filter(src => src && !src.includes(window.location.hostname)).join("; ") || "Nenhum detectado"; availableData.push(`Cross-Origin Requests: ${crossOrigin} - Solicitações a terceiros.`); const serviceWorkers = navigator.serviceWorker ? "Disponível" : "Não disponível"; if (serviceWorkers === "Não disponível") { unavailableData.push(`Service Workers: ${serviceWorkers} - Scripts em background.`); } else { availableData.push(`Service Workers: ${serviceWorkers} - Scripts em background.`); } unavailableData.push(`Autofill/Password Managers: Não detectável diretamente - Dados preenchidos automaticamente.`); const webSocket = window.WebSocket ? "Disponível" : "Não disponível"; if (webSocket === "Não disponível") { unavailableData.push(`WebSocket Negotiation: ${webSocket} - Comunicação em tempo real.`); } else { availableData.push(`WebSocket Negotiation: ${webSocket} - Comunicação em tempo real.`); } unavailableData.push(`CSP Violations: Não disponível (requer relatório do navegador) - Violações de segurança.`); unavailableData.push(`Browser Extensions: Não detectável diretamente (pode vazar via recursos) - Extensões instaladas.`); availableData.push(`Time Zone Offset: ${new Date().getTimezoneOffset()} minutos - Diferença horária.`); unavailableData.push(`Redes sociais logadas: Não detectável diretamente (ex.: Facebook Connect) - Rastreamento de login.`); let webglErrors = "Não disponível"; if (canvas.getContext('webgl')) { try { const gl = canvas.getContext('webgl'); gl.getError(); webglErrors = "Detectável via erros específicos"; } catch (e) { webglErrors = e.message; } } if (webglErrors === "Não disponível") { unavailableData.push(`WebGL Errors: ${webglErrors} - Erros de renderização GPU.`); } else { availableData.push(`WebGL Errors: ${webglErrors} - Erros de renderização GPU.`); } const protocolHandlers = navigator.registerProtocolHandler ? "Disponível" : "Não disponível"; if (protocolHandlers === "Não disponível") { unavailableData.push(`Protocol Handlers: ${protocolHandlers} - Aplicativos padrão (mailto:, tel:).`); } else { availableData.push(`Protocol Handlers: ${protocolHandlers} - Aplicativos padrão (mailto:, tel:).`); } const resources = performance.getEntriesByType('resource').map(entry => `${entry.name}: ${entry.duration.toFixed(2)}ms`).join("; ") || "Nenhum recurso"; availableData.push(`Resource Timing API: ${resources} - Tempos de carregamento.`); unavailableData.push(`X-Client-Data Headers: Não disponível (Chrome-specific, via headers) - Dados de experimentos.`); availableData.push(`Prefers Color Scheme: ${window.matchMedia('(prefers-color-scheme: dark)').matches ? "Escuro" : "Claro"} - Tema do sistema.`); availableData.push(`Reduced Motion: ${window.matchMedia('(prefers-reduced-motion: reduce)').matches ? "Sim" : "Não"} - Preferência de acessibilidade.`); unavailableData.push(`COOP Leaks: Não disponível diretamente - Política de abertura cross-origin.`); const speechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition ? "Disponível" : "Não disponível"; if (speechRecognition === "Não disponível") { unavailableData.push(`Speech Recognition Data: ${speechRecognition} - Dados de voz.`); } else { availableData.push(`Speech Recognition Data: ${speechRecognition} - Dados de voz.`); } let gamepadData = "Não disponível"; if (navigator.getGamepads) { try { const gamepads = navigator.getGamepads(); gamepadData = Array.from(gamepads).filter(g => g).map(g => `${g.id}`).join("; ") || "Nenhum conectado"; } catch (e) { gamepadData = "Bloqueado pela política de permissões"; console.log("Erro ao acessar Gamepad API:", e.message); } } if (gamepadData === "Não disponível" || gamepadData === "Bloqueado pela política de permissões") { unavailableData.push(`Gamepad API: ${gamepadData} - Controles conectados.`); } else { availableData.push(`Gamepad API: ${gamepadData} - Controles conectados.`); } const deviceMemoryAPI = navigator.deviceMemory || "Não disponível"; if (deviceMemoryAPI === "Não disponível") { unavailableData.push(`Device Memory API: ${deviceMemoryAPI} GB - Capacidade de RAM.`); } else { availableData.push(`Device Memory API: ${deviceMemoryAPI} GB - Capacidade de RAM.`); } const networkQuality = navigator.connection?.rtt !== undefined ? `${navigator.connection.rtt} ms` : "Não disponível"; if (networkQuality === "Não disponível") { unavailableData.push(`Network Quality Hint: ${networkQuality} - Latência estimada.`); } else { availableData.push(`Network Quality Hint: ${networkQuality} - Latência estimada.`); } const vibrationAPI = navigator.vibrate ? "Disponível" : "Não disponível"; if (vibrationAPI === "Não disponível") { unavailableData.push(`Vibration API: ${vibrationAPI} - Suporte a vibração.`); } else { availableData.push(`Vibration API: ${vibrationAPI} - Suporte a vibração.`); } const presentationAPI = navigator.presentation ? "Disponível" : "Não disponível"; if (presentationAPI === "Não disponível") { unavailableData.push(`Presentation API: ${presentationAPI} - Telas secundárias.`); } else { availableData.push(`Presentation API: ${presentationAPI} - Telas secundárias.`); } const credentialAPI = navigator.credentials ? "Disponível" : "Não disponível"; if (credentialAPI === "Não disponível") { unavailableData.push(`Credential Management API: ${credentialAPI} - Credenciais salvas.`); } else { availableData.push(`Credential Management API: ${credentialAPI} - Credenciais salvas.`); } const paymentAPI = window.PaymentRequest ? "Disponível" : "Não disponível"; if (paymentAPI === "Não disponível") { unavailableData.push(`Payment Request API: ${paymentAPI} - Métodos de pagamento.`); } else { availableData.push(`Payment Request API: ${paymentAPI} - Métodos de pagamento.`); } const idleAPI = window.IdleDetector ? "Disponível" : "Não disponível"; if (idleAPI === "Não disponível") { unavailableData.push(`Idle Detection API: ${idleAPI} - Status de inatividade.`); } else { availableData.push(`Idle Detection API: ${idleAPI} - Status de inatividade.`); } const clipboardAPI = navigator.clipboard ? "Disponível" : "Não disponível"; if (clipboardAPI === "Não disponível") { unavailableData.push(`Clipboard API: ${clipboardAPI} - Acesso ao clipboard.`); } else { availableData.push(`Clipboard API: ${clipboardAPI} - Acesso ao clipboard.`); } const fileSystemAPI = window.showOpenFilePicker ? "Disponível" : "Não disponível"; if (fileSystemAPI === "Não disponível") { unavailableData.push(`File System Access API: ${fileSystemAPI} - Acesso a arquivos.`); } else { availableData.push(`File System Access API: ${fileSystemAPI} - Acesso a arquivos.`); } const midiAPI = navigator.requestMIDIAccess ? "Disponível" : "Não disponível"; if (midiAPI === "Não disponível") { unavailableData.push(`Web MIDI API: ${midiAPI} - Dispositivos MIDI.`); } else { availableData.push(`Web MIDI API: ${midiAPI} - Dispositivos MIDI.`); } const hidAPI = navigator.hid ? "Disponível" : "Não disponível"; if (hidAPI === "Não disponível") { unavailableData.push(`WebHID API: ${hidAPI} - Dispositivos HID.`); } else { availableData.push(`WebHID API: ${hidAPI} - Dispositivos HID.`); } const permissionsAPI = navigator.permissions ? "Disponível" : "Não disponível"; if (permissionsAPI === "Não disponível") { unavailableData.push(`Permissions API: ${permissionsAPI} - Lista de permissões.`); } else { availableData.push(`Permissions API: ${permissionsAPI} - Lista de permissões.`); } const trustTokens = document.featurePolicy?.allowsFeature('trust-token-issuance') ? "Disponível" : "Não disponível"; if (trustTokens === "Não disponível") { unavailableData.push(`Trust Tokens: ${trustTokens} - Tokens antifraude.`); } else { availableData.push(`Trust Tokens: ${trustTokens} - Tokens antifraude.`); } const storageAccessAPI = document.requestStorageAccess ? "Disponível" : "Não disponível"; if (storageAccessAPI === "Não disponível") { unavailableData.push(`Storage Access API: ${storageAccessAPI} - Acesso a cookies.`); } else { availableData.push(`Storage Access API: ${storageAccessAPI} - Acesso a cookies.`); } unavailableData.push(`Reporting API: Não disponível diretamente - Relatórios de segurança.`); const userTimingAPI = performance.mark ? "Disponível" : "Não disponível"; if (userTimingAPI === "Não disponível") { unavailableData.push(`User Timing API: ${userTimingAPI} - Marcadores de desempenho.`); } else { availableData.push(`User Timing API: ${userTimingAPI} - Marcadores de desempenho.`); } const webTransportAPI = window.WebTransport ? "Disponível" : "Não disponível"; if (webTransportAPI === "Não disponível") { unavailableData.push(`WebTransport API: ${webTransportAPI} - Protocolos alternativos.`); } else { availableData.push(`WebTransport API: ${webTransportAPI} - Protocolos alternativos.`); } const broadcastChannelAPI = window.BroadcastChannel ? "Disponível" : "Não disponível"; if (broadcastChannelAPI === "Não disponível") { unavailableData.push(`Broadcast Channel API: ${broadcastChannelAPI} - Comunicação entre abas.`); } else { availableData.push(`Broadcast Channel API: ${broadcastChannelAPI} - Comunicação entre abas.`); } const webLocksAPI = navigator.locks ? "Disponível" : "Não disponível"; if (webLocksAPI === "Não disponível") { unavailableData.push(`Web Locks API: ${webLocksAPI} - Locks de recursos.`); } else { availableData.push(`Web Locks API: ${webLocksAPI} - Locks de recursos.`); } const wasmAPI = window.WebAssembly ? "Disponível" : "Não disponível"; if (wasmAPI === "Não disponível") { unavailableData.push(`WebAssembly Capabilities: ${wasmAPI} - Suporte a WASM.`); } else { availableData.push(`WebAssembly Capabilities: ${wasmAPI} - Suporte a WASM.`); } const intersectionAPI = window.IntersectionObserver ? "Disponível" : "Não disponível"; if (intersectionAPI === "Não disponível") { unavailableData.push(`Intersection Observer API: ${intersectionAPI} - Visibilidade de elementos.`); } else { availableData.push(`Intersection Observer API: ${intersectionAPI} - Visibilidade de elementos.`); } availableData.push(`Page Visibility API: ${document.visibilityState} - Estado da página.`); const pushAPI = navigator.serviceWorker && 'pushManager' in ServiceWorkerRegistration.prototype ? "Disponível" : "Não disponível"; if (pushAPI === "Não disponível") { unavailableData.push(`Push API: ${pushAPI} - Notificações push.`); } else { availableData.push(`Push API: ${pushAPI} - Notificações push.`); } const notificationsAPI = window.Notification ? "Disponível" : "Não disponível"; if (notificationsAPI === "Não disponível") { unavailableData.push(`Web Notifications: ${notificationsAPI} - Suporte a notificações.`); } else { availableData.push(`Web Notifications: ${notificationsAPI} - Suporte a notificações.`); } const backgroundSyncAPI = navigator.serviceWorker && 'sync' in ServiceWorkerRegistration.prototype ? "Disponível" : "Não disponível"; if (backgroundSyncAPI === "Não disponível") { unavailableData.push(`Background Sync: ${backgroundSyncAPI} - Sincronização em segundo plano.`); } else { availableData.push(`Background Sync: ${backgroundSyncAPI} - Sincronização em segundo plano.`); } const webAudioAPI = window.AudioContext || window.webkitAudioContext ? "Disponível" : "Não disponível"; if (webAudioAPI === "Não disponível") { unavailableData.push(`Web Audio API: ${webAudioAPI} - Processamento de áudio.`); } else { availableData.push(`Web Audio API: ${webAudioAPI} - Processamento de áudio.`); } const mediaCapabilitiesAPI = navigator.mediaCapabilities ? "Disponível" : "Não disponível"; if (mediaCapabilitiesAPI === "Não disponível") { unavailableData.push(`Media Capabilities API: ${mediaCapabilitiesAPI} - Codecs suportados.`); } else { availableData.push(`Media Capabilities API: ${mediaCapabilitiesAPI} - Codecs suportados.`); } const mediaSessionAPI = navigator.mediaSession ? "Disponível" : "Não disponível"; if (mediaSessionAPI === "Não disponível") { unavailableData.push(`Media Session API: ${mediaSessionAPI} - Metadados de mídia.`); } else { availableData.push(`Media Session API: ${mediaSessionAPI} - Metadados de mídia.`); } const wakeLockAPI = navigator.wakeLock ? "Disponível" : "Não disponível"; if (wakeLockAPI === "Não disponível") { unavailableData.push(`Screen Wake Lock API: ${wakeLockAPI} - Manter tela ativa.`); } else { availableData.push(`Screen Wake Lock API: ${wakeLockAPI} - Manter tela ativa.`); } const webXRAPI = navigator.xr ? "Disponível" : "Não disponível"; if (webXRAPI === "Não disponível") { unavailableData.push(`WebXR Device API: ${webXRAPI} - Realidade virtual/aumentada.`); } else { availableData.push(`WebXR Device API: ${webXRAPI} - Realidade virtual/aumentada.`); } unavailableData.push(`Geofencing API: Não disponível diretamente - Cercas virtuais.`); const contactPickerAPI = navigator.contacts ? "Disponível" : "Não disponível"; if (contactPickerAPI === "Não disponível") { unavailableData.push(`Contact Picker API: ${contactPickerAPI} - Seleção de contatos.`); } else { availableData.push(`Contact Picker API: ${contactPickerAPI} - Seleção de contatos.`); } const badgingAPI = navigator.setAppBadge ? "Disponível" : "Não disponível"; if (badgingAPI === "Não disponível") { unavailableData.push(`Badging API: ${badgingAPI} - Badges de aplicativos.`); } else { availableData.push(`Badging API: ${badgingAPI} - Badges de aplicativos.`); } const periodicSyncAPI = navigator.serviceWorker && 'periodicSync' in ServiceWorkerRegistration.prototype ? "Disponível" : "Não disponível"; if (periodicSyncAPI === "Não disponível") { unavailableData.push(`Periodic Background Sync: ${periodicSyncAPI} - Atualizações regulares.`); } else { availableData.push(`Periodic Background Sync: ${periodicSyncAPI} - Atualizações regulares.`); } const webAuthnDetails = navigator.credentials ? "Disponível" : "Não disponível"; if (webAuthnDetails === "Não disponível") { unavailableData.push(`WebAuthn Authenticator Details: ${webAuthnDetails} - Tipos de autenticadores.`); } else { availableData.push(`WebAuthn Authenticator Details: ${webAuthnDetails} - Tipos de autenticadores.`); } unavailableData.push(`Cross-Device Tracking: Não detectável diretamente - Vinculação de dispositivos.`); unavailableData.push(`DNS over HTTPS (DoH): Não detectável diretamente - DNS criptografado.`); unavailableData.push(`HTTP/2 Server Push: Não detectável diretamente - Recursos pré-empurrados.`); const thirdPartyScripts = crossOrigin ? "Detectado" : "Não detectado"; availableData.push(`Third-Party Script Behavior: ${thirdPartyScripts} - Trackers de terceiros.`); unavailableData.push(`Inferred Privacy Settings: Não disponível (requer análise de headers) - Bloqueio de cookies.`); unavailableData.push(`Ad Blocker Presence: Não detectável diretamente - Bloqueadores de anúncios.`); availableData.push(`Browser Theme Detection: ${window.matchMedia('(prefers-color-scheme: dark)').matches ? "Escuro" : "Claro"} - Tema detectado.`); unavailableData.push(`Autoplay Policies: Não disponível diretamente - Comportamento de mídia.`); const contentIndexingAPI = navigator.serviceWorker && 'contentIndex' in ServiceWorkerRegistration.prototype ? "Disponível" : "Não disponível"; if (contentIndexingAPI === "Não disponível") { unavailableData.push(`Content Indexing API: ${contentIndexingAPI} - Conteúdo offline.`); } else { availableData.push(`Content Indexing API: ${contentIndexingAPI} - Conteúdo offline.`); } let webglExtensions = "Não disponível"; if (canvas.getContext('webgl')) { const gl = canvas.getContext('webgl'); webglExtensions = gl.getSupportedExtensions().slice(0, 3).join(", ") + "..."; } if (webglExtensions === "Não disponível") { unavailableData.push(`WebGL Extensions: ${webglExtensions} - Extensões suportadas.`); } else { availableData.push(`WebGL Extensions: ${webglExtensions} - Extensões suportadas.`); } let voices = "Não disponível"; if (window.speechSynthesis) { voices = speechSynthesis.getVoices().map(v => v.name).slice(0, 3).join(", ") + "..." || "Carregando"; } if (voices === "Não disponível") { unavailableData.push(`Speech Synthesis Voices: ${voices} - Vozes de TTS.`); } else { availableData.push(`Speech Synthesis Voices: ${voices} - Vozes de TTS.`); } const scanTime = ((performance.now() - startTime) / 1000).toFixed(2); availableData.push(`Tempo de execução do scan: ${scanTime} segundos - Duração do scan.`); return { availableData, unavailableData }; } function createDataExposedMenu() { console.log('Criando popup do Data Exposed'); const menu = document.createElement('div'); menu.className = 'draggable-menu'; Object.assign(menu.style, { position: 'fixed', top: '20%', left: '50%', transform: 'translateX(-50%)', textAlign: 'left', width: '1280px', height: '720px', overflowY: 'auto', zIndex: '9999' }); const headerContainer = document.createElement('div'); headerContainer.style.display = 'flex'; headerContainer.style.alignItems = 'center'; headerContainer.style.justifyContent = 'space-between'; headerContainer.style.marginBottom = '10px'; const h3 = document.createElement('h3'); h3.textContent = 'Dados Vazados pelo Seu Navegador para os Servidores das Big Techs'; Object.assign(h3.style, { fontSize: '18px', margin: '0', color: 'yellow' }); const copyIcon = document.createElement('span'); copyIcon.textContent = '📋'; Object.assign(copyIcon.style, { fontSize: '18px', marginLeft: '10px', cursor: 'pointer', position: 'relative' }); copyIcon.title = 'Copie o texto'; const btnFecharHeader = document.createElement('button'); btnFecharHeader.textContent = 'Fechar'; Object.assign(btnFecharHeader.style, { background: '#ffff00', border: 'none', color: '#000', padding: '3px 8px', cursor: 'pointer', borderRadius: '3px', fontSize: '10px', marginLeft: '10px' }); btnFecharHeader.addEventListener('click', toggleDataExposed); headerContainer.appendChild(h3); headerContainer.appendChild(copyIcon); headerContainer.appendChild(btnFecharHeader); menu.appendChild(headerContainer); const availableList = document.createElement('ul'); availableList.style.paddingLeft = '20px'; const divider = document.createElement('hr'); Object.assign(divider.style, { border: 'none', height: '4px', backgroundColor: 'yellow', margin: '20px 0' }); const unavailableTitle = document.createElement('h4'); unavailableTitle.textContent = 'Dados Secretamente Coletados pelas Big Techs Além do Alcance do Seu Navegador'; Object.assign(unavailableTitle.style, { color: 'yellow', fontSize: '16px', margin: '10px 0' }); const unavailableList = document.createElement('ul'); unavailableList.style.paddingLeft = '20px'; collectExposedData().then(({ availableData, unavailableData }) => { console.log('Dados coletados:', { availableData, unavailableData }); availableData.forEach(item => { const li = document.createElement('li'); li.style.marginBottom = '10px'; li.style.fontSize = '12px'; const colonIndex = item.indexOf(':'); if (colonIndex !== -1) { const prefix = item.substring(0, colonIndex + 1); const suffix = item.substring(colonIndex + 1).trim(); const prefixSpan = document.createElement('span'); prefixSpan.style.color = 'yellow'; prefixSpan.textContent = prefix; const suffixText = document.createTextNode(suffix); li.appendChild(prefixSpan); li.appendChild(suffixText); } else { li.textContent = item; } availableList.appendChild(li); }); unavailableData.forEach(item => { const li = document.createElement('li'); li.style.marginBottom = '10px'; li.style.fontSize = '12px'; const colonIndex = item.indexOf(':'); if (colonIndex !== -1) { const prefix = item.substring(0, colonIndex + 1); const suffix = item.substring(colonIndex + 1).trim(); const prefixSpan = document.createElement('span'); prefixSpan.style.color = 'yellow'; prefixSpan.textContent = prefix; const suffixText = document.createTextNode(suffix); li.appendChild(prefixSpan); li.appendChild(suffixText); } else { li.textContent = item; } unavailableList.appendChild(li); }); copyIcon.addEventListener('click', () => { const textToCopy = [ "Dados Disponíveis:\n" + availableData.join('\n'), "\nDados Não Disponíveis Devido a Restrições do Navegador ou Servidor:\n" + unavailableData.join('\n') ].join('\n'); navigator.clipboard.writeText(textToCopy).then(() => { showOverlayConfirmationAutoClose("Sucesso", "Texto copiado para a área de transferência!"); }).catch(err => { console.error("Erro ao copiar texto:", err); showOverlayConfirmationAutoClose("Erro", "Falha ao copiar o texto."); }); }); }).catch(err => { console.error("Erro ao coletar dados expostos:", err); const li = document.createElement('li'); li.textContent = `Erro ao coletar dados: ${err.message}`; availableList.appendChild(li); }); menu.appendChild(availableList); menu.appendChild(divider); menu.appendChild(unavailableTitle); menu.appendChild(unavailableList); const btnFechar = document.createElement('button'); btnFechar.textContent = 'Fechar'; Object.assign(btnFechar.style, { background: '#ffff00', border: 'none', color: '#000', padding: '3px 8px', cursor: 'pointer', borderRadius: '3px', fontSize: '10px', marginTop: '10px', display: 'block', marginLeft: 'auto', marginRight: 'auto' }); btnFechar.addEventListener('click', toggleDataExposed); menu.appendChild(btnFechar); makeDraggable(menu); return menu; } function toggleDataExposed() { if (!dataExposedActive) { dataExposedMenu = createDataExposedMenu(); document.body.appendChild(dataExposedMenu); btnDataExposed.setAttribute("data-active", "true"); dataExposedActive = true; } else { if (dataExposedMenu) { dataExposedMenu.remove(); dataExposedMenu = null; } btnDataExposed.removeAttribute("data-active"); dataExposedActive = false; } } function createDataExposedButton() { const btn = document.createElement('button'); btn.textContent = 'Data Exposed'; btn.className = 'btn-data-exposed'; btn.title = 'Veja os dados reais que o site pode estar coletando sobre você'; btn.addEventListener('click', toggleDataExposed); return btn; } // Modo Leitura - Variáveis e configurações let readerOverlay = null; let readerArticleData = null; let readerScrollPosition = 0; let readerLastScrollTime = 0; let readerIsUIVisible = true; const READER_CONFIG = { theme: GM_getValue('reader_theme', 'light'), fontSize: GM_getValue('reader_fontSize', 19), lineHeight: GM_getValue('reader_lineHeight', 1.7), maxWidth: GM_getValue('reader_maxWidth', 740), fontFamily: GM_getValue('reader_fontFamily', 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif'), paragraphSpacing: GM_getValue('reader_paragraphSpacing', 1.4), imageSize: GM_getValue('reader_imageSize', 'large'), enableFootnotes: GM_getValue('reader_enableFootnotes', true), autoHideUI: GM_getValue('reader_autoHideUI', true), readerMargin: GM_getValue('reader_readerMargin', 60), imageAlignment: GM_getValue('reader_imageAlignment', 'center'), animationSpeed: GM_getValue('reader_animationSpeed', 250) }; function createReadingModeButton() { const btn = document.createElement('button'); btn.textContent = 'Modo Leitura'; btn.className = 'btn-reading-mode'; btn.title = 'Ativa o modo de leitura para melhorar a experiência de leitura'; btn.addEventListener('click', toggleReadingMode); return btn; } function toggleReadingMode() { if (readerOverlay) { closeReaderOverlay(); } else { showReaderLoadingIndicator(); extractReaderContent().then(() => { removeReaderLoadingIndicator(); if (readerArticleData && readerArticleData.content) { createReaderOverlay(); } else { showReaderErrorMessage("Não foi possível extrair o conteúdo desta página."); } }).catch(error => { removeReaderLoadingIndicator(); console.error('Erro no modo de leitura:', error); showReaderErrorMessage("Erro ao processar a página: " + error.message); }); } } function showReaderLoadingIndicator() { const loadingIndicator = document.createElement('div'); loadingIndicator.id = 'reader-loading-indicator'; loadingIndicator.innerHTML = ` <div style="width: 24px; height: 24px; border: 3px solid #fff; border-top: 3px solid transparent; border-radius: 50%; animation: reader_spin 1s linear infinite;"></div> <span>Preparando modo de leitura...</span> `; document.body.appendChild(loadingIndicator); } function removeReaderLoadingIndicator() { const loadingIndicator = document.getElementById('reader-loading-indicator'); if (loadingIndicator) { loadingIndicator.remove(); } } function showReaderErrorMessage(message) { const errorBox = document.createElement('div'); errorBox.id = 'reader-error-message'; errorBox.innerHTML = ` <div style="margin-bottom: 15px; font-size: 24px;">⚠️</div> <div>${message}</div> <button style="margin-top: 15px; padding: 8px 16px; background: white; color: #dc3545; border: none; border-radius: 4px; cursor: pointer; font-weight: bold;">Fechar</button> `; errorBox.querySelector('button').addEventListener('click', () => { errorBox.remove(); }); document.body.appendChild(errorBox); // Auto-remove após 5 segundos setTimeout(() => { if (document.body.contains(errorBox)) { errorBox.remove(); } }, 5000); } async function extractReaderContent() { try { // Clone do documento para evitar modificar o original const documentClone = document.cloneNode(true); // Extrair com Readability const reader = new Readability(documentClone); readerArticleData = reader.parse(); // Se não conseguir extrair conteúdo adequado, tenta método alternativo if (!readerArticleData || !readerArticleData.content || readerArticleData.textContent.length < 500) { console.log('Conteúdo extraído insuficiente, tentando alternativa'); // Aqui poderia implementar métodos alternativos de extração // Por simplicidade, estamos apenas limpando o conteúdo melhor if (readerArticleData && readerArticleData.content) { readerArticleData.content = DOMPurify.sanitize(readerArticleData.content); } } console.log('Conteúdo extraído:', readerArticleData); return readerArticleData; } catch (error) { console.error('Erro na extração:', error); throw error; } } function createReaderOverlay() { // Criar o overlay principal readerOverlay = document.createElement('div'); readerOverlay.id = 'speedreader-overlay'; readerOverlay.setAttribute('aria-label', 'Modo de leitura'); // Usar shadow DOM para isolar os estilos const shadow = readerOverlay.attachShadow({ mode: 'open' }); // CSS para o shadow DOM const style = document.createElement('style'); style.textContent = generateReaderCss(); shadow.appendChild(style); // Criar o conteúdo const containerOuter = document.createElement('div'); containerOuter.className = 'container-outer'; shadow.appendChild(containerOuter); // Barra de controles superior const controls = document.createElement('div'); controls.className = 'controls'; controls.innerHTML = ` <div class="control-group"> <button class="control-btn close-btn" data-tooltip="Fechar (Esc)"> <svg viewBox="0 0 24 24"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg> Fechar </button> </div> <div class="control-group secondary"> <button class="control-btn icon font-decrease-btn" data-tooltip="Diminuir fonte (-)"> <svg viewBox="0 0 24 24"><path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z"/></svg> </button> <button class="control-btn icon font-increase-btn" data-tooltip="Aumentar fonte (+)"> <svg viewBox="0 0 24 24"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"/></svg> </button> </div> <div class="control-group"> <button class="control-btn theme-btn" data-tooltip="Mudar tema (T)"> <svg viewBox="0 0 24 24"><path d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/></svg> </button> </div> `; containerOuter.appendChild(controls); // Container para o conteúdo const container = document.createElement('div'); container.className = 'container'; // Criar cabeçalho com meta informações let metaHTML = ''; if (readerArticleData.byline || readerArticleData.siteName || readerArticleData.publishedTime) { metaHTML = `<div class="article-meta">`; if (readerArticleData.byline) { metaHTML += `<span class="article-author">Por ${readerArticleData.byline}</span>`; } if (readerArticleData.publishedTime) { const date = new Date(readerArticleData.publishedTime); const formattedDate = isNaN(date.getTime()) ? readerArticleData.publishedTime : date.toLocaleDateString('pt-BR', { year: 'numeric', month: 'long', day: 'numeric' }); metaHTML += `<span class="article-date">${formattedDate}</span>`; } if (readerArticleData.siteName) { metaHTML += `<span class="article-site">${readerArticleData.siteName}</span>`; } metaHTML += `</div>`; } // Adicionar título, meta informações e conteúdo container.innerHTML = ` ${readerArticleData.title ? `<h1>${readerArticleData.title}</h1>` : ''} ${metaHTML} <div class="article-content"> ${readerArticleData.content || '<p>Não foi possível extrair o conteúdo.</p>'} </div> `; containerOuter.appendChild(container); // Adicionar o overlay ao body document.body.appendChild(readerOverlay); // Adicionar listeners para controles addReaderControlListeners(shadow); // Animar a entrada do overlay containerOuter.style.opacity = '0'; setTimeout(() => { containerOuter.style.opacity = '1'; }, 10); // Prevenir scroll do body document.body.style.overflow = 'hidden'; } function generateReaderCss() { const themeColors = getReaderThemeColors(); return ` :host { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 2147483647; font-family: ${READER_CONFIG.fontFamily}; } .container-outer { width: 100%; height: 100%; overflow-y: auto; overflow-x: hidden; background: ${themeColors.background}; color: ${themeColors.text}; padding: 0; margin: 0; scroll-behavior: smooth; opacity: 1; transition: opacity ${READER_CONFIG.animationSpeed}ms ease; } .container { max-width: ${READER_CONFIG.maxWidth}px; margin: 0 auto; padding: ${READER_CONFIG.readerMargin}px; padding-top: 80px; padding-bottom: 100px; line-height: ${READER_CONFIG.lineHeight}; font-size: ${READER_CONFIG.fontSize}px; box-sizing: border-box; } h1 { font-size: 2.2em; margin: 0 0 0.8em 0; line-height: 1.2; font-weight: 700; color: ${themeColors.title}; } h2 { font-size: 1.6em; margin: 1.4em 0 0.8em 0; line-height: 1.3; } h3 { font-size: 1.3em; margin: 1.2em 0 0.6em 0; } p { margin-bottom: ${READER_CONFIG.paragraphSpacing}em; overflow-wrap: break-word; } a { color: ${themeColors.link}; text-decoration: none; border-bottom: 1px solid ${themeColors.linkUnderline}; transition: border-color 0.2s; } a:hover { border-bottom-color: ${themeColors.linkHover}; } img { max-width: 100%; height: auto; margin: 2em auto; display: block; border-radius: 4px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } figure { margin: 2em 0; text-align: center; } figcaption { font-size: 0.9em; color: ${themeColors.caption}; margin-top: 0.8em; font-style: italic; } blockquote { margin: 2em 0; padding-left: 1.5em; border-left: 4px solid ${themeColors.blockquoteBorder}; color: ${themeColors.blockquote}; font-style: italic; } pre, code { background-color: ${themeColors.codeBackground}; font-family: monospace; border-radius: 3px; overflow-x: auto; } code { padding: 0.2em 0.4em; font-size: 0.9em; } pre { padding: 1em; margin: 1.5em 0; } pre code { padding: 0; background: none; } .controls { position: fixed; top: 0; left: 0; right: 0; height: 60px; background: ${themeColors.controlsBackground || 'rgba(255, 255, 255, 0.9)'}; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); display: flex; justify-content: space-between; align-items: center; padding: 0 20px; z-index: 10; box-shadow: 0 1px 4px rgba(0,0,0,0.1); transition: transform 0.3s ease; } .control-group { display: flex; align-items: center; gap: 10px; } .control-btn { background: ${themeColors.buttonBackground || 'rgba(0, 0, 0, 0.05)'}; color: ${themeColors.buttonText || '#333'}; border: none; border-radius: 4px; padding: 8px 12px; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 6px; transition: background-color 0.2s; } .control-btn:hover { background: ${themeColors.buttonHover || 'rgba(0, 0, 0, 0.1)'}; } .control-btn.icon { width: 36px; height: 36px; padding: 0; justify-content: center; } .control-btn svg { width: 18px; height: 18px; fill: currentColor; } .article-meta { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 2em; color: ${themeColors.meta || '#666'}; font-size: 0.9em; } .article-meta > span:not(:last-child)::after { content: "•"; margin-left: 12px; } @media (max-width: 768px) { .container { padding: 70px 20px 80px; } .controls { padding: 0 10px; } .control-group.secondary { display: none; } h1 { font-size: 1.8em; } } `; } function getReaderThemeColors() { const themes = { light: { background: '#ffffff', text: 'rgba(0, 0, 0, 0.87)', title: '#121212', link: '#1a73e8', linkUnderline: 'rgba(26, 115, 232, 0.2)', linkHover: 'rgba(26, 115, 232, 0.7)', blockquote: '#555', blockquoteBorder: '#ddd', caption: '#666', meta: '#666', codeBackground: '#f5f5f5', controlsBackground: 'rgba(255, 255, 255, 0.9)', buttonBackground: 'rgba(0, 0, 0, 0.05)', buttonText: '#333', buttonHover: 'rgba(0, 0, 0, 0.1)' }, dark: { background: '#1a1a1a', text: 'rgba(255, 255, 255, 0.87)', title: '#fff', link: '#81b4ff', linkUnderline: 'rgba(129, 180, 255, 0.2)', linkHover: 'rgba(129, 180, 255, 0.7)', blockquote: '#aaa', blockquoteBorder: '#444', caption: '#999', meta: '#999', codeBackground: '#2d2d2d', controlsBackground: 'rgba(26, 26, 26, 0.9)', buttonBackground: 'rgba(255, 255, 255, 0.1)', buttonText: '#eee', buttonHover: 'rgba(255, 255, 255, 0.2)' }, sepia: { background: '#f4ecd8', text: 'rgba(50, 40, 30, 0.87)', title: '#442b1d', link: '#a05d1e', linkUnderline: 'rgba(160, 93, 30, 0.2)', linkHover: 'rgba(160, 93, 30, 0.7)', blockquote: '#7a6c62', blockquoteBorder: '#d3c2a9', caption: '#7a6c62', meta: '#7a6c62', codeBackground: '#ebe1c9', controlsBackground: 'rgba(244, 236, 216, 0.9)', buttonBackground: 'rgba(50, 40, 30, 0.05)', buttonText: '#442b1d', buttonHover: 'rgba(50, 40, 30, 0.1)' } }; return themes[READER_CONFIG.theme] || themes.light; } function addReaderControlListeners(shadow) { // Botão fechar shadow.querySelector('.close-btn').addEventListener('click', () => { closeReaderOverlay(); }); // Botões de fonte shadow.querySelector('.font-decrease-btn').addEventListener('click', () => { changeReaderFontSize(-1); }); shadow.querySelector('.font-increase-btn').addEventListener('click', () => { changeReaderFontSize(1); }); // Botão de tema shadow.querySelector('.theme-btn').addEventListener('click', () => { cycleReaderTheme(); }); // Também adicionar atalhos de teclado document.addEventListener('keydown', handleReaderKeyboard); } function handleReaderKeyboard(e) { if (!readerOverlay) return; if (e.key === 'Escape') { closeReaderOverlay(); } else if (e.key === '+' || e.key === '=') { changeReaderFontSize(1); } else if (e.key === '-') { changeReaderFontSize(-1); } else if (e.key.toLowerCase() === 't') { cycleReaderTheme(); } } function changeReaderFontSize(delta) { READER_CONFIG.fontSize = Math.max(14, Math.min(26, READER_CONFIG.fontSize + delta)); GM_setValue('reader_fontSize', READER_CONFIG.fontSize); const container = readerOverlay.shadowRoot.querySelector('.container'); container.style.fontSize = `${READER_CONFIG.fontSize}px`; } function cycleReaderTheme() { const themes = ['light', 'sepia', 'dark']; const currentIndex = themes.indexOf(READER_CONFIG.theme); const nextIndex = (currentIndex + 1) % themes.length; READER_CONFIG.theme = themes[nextIndex]; GM_setValue('reader_theme', READER_CONFIG.theme); // Atualizar o CSS com o novo tema const style = readerOverlay.shadowRoot.querySelector('style'); style.textContent = generateReaderCss(); } function closeReaderOverlay() { if (readerOverlay) { // Animar saída const containerOuter = readerOverlay.shadowRoot.querySelector('.container-outer'); containerOuter.style.opacity = '0'; // Remover evento de teclado document.removeEventListener('keydown', handleReaderKeyboard); setTimeout(() => { readerOverlay.remove(); readerOverlay = null; // Restaurar scroll do body document.body.style.overflow = ''; }, READER_CONFIG.animationSpeed); } } function createSearxButton() { const btn = document.createElement('button'); btn.textContent = 'Searx'; btn.className = 'btn-searx'; btn.title = 'Abre o buscador Searx personalizado'; btn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); console.log("Botão Searx clicado"); showSearchModal(); }); return btn; } // Searx - Carregar e salvar preferências function loadSearxPreferences() { try { return { categories: JSON.parse(localStorage.getItem('searx_categories')) || Object.keys(SEARX_CATEGORIES).slice(0, 3), engines: JSON.parse(localStorage.getItem('searx_engines')) || Object.keys(SEARX_ENGINES).slice(0, 5), newTab: localStorage.getItem('searx_new_tab') !== 'false', history: JSON.parse(localStorage.getItem('searx_history')) || [] }; } catch (error) { console.error("Erro ao carregar preferências do Searx:", error); return { categories: Object.keys(SEARX_CATEGORIES).slice(0, 3), engines: Object.keys(SEARX_ENGINES).slice(0, 5), newTab: true, history: [] }; } } function saveSearxPreferences(prefs) { try { localStorage.setItem('searx_categories', JSON.stringify(prefs.categories)); localStorage.setItem('searx_engines', JSON.stringify(prefs.engines)); localStorage.setItem('searx_new_tab', prefs.newTab); localStorage.setItem('searx_history', JSON.stringify(prefs.history)); } catch (error) { console.error("Erro ao salvar preferências do Searx:", error); showOverlayConfirmationAutoClose("Erro", "Falha ao salvar preferências do Searx", 5000); } } // Searx - Salvar pesquisa no histórico function saveSearxToHistory(query) { const prefs = loadSearxPreferences(); // Limitar histórico a 5 itens e remover duplicatas const history = [query, ...prefs.history.filter(item => item !== query)].slice(0, 5); saveSearxPreferences({...prefs, history}); } // Searx - Exibir toast function showSearxToast(message, duration = 3000) { const toast = document.createElement('div'); toast.className = 'searx-toast'; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => toast.classList.add('show'), 10); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => toast.remove(), 300); }, duration); } // Searx - Modal de pesquisa function showSearchModal() { try { console.log("Iniciando showSearchModal"); const prefs = loadSearxPreferences(); const overlay = document.createElement('div'); overlay.className = 'searx-overlay'; // Template do modal overlay.innerHTML = ` <div class="searx-modal"> <div class="searx-header"> <h3>Pesquisa Personalizada Searx <span class="searx-shortcut">Alt+S</span></h3> <div class="searx-search-container"> <input type="text" id="searx-query" placeholder="O que você deseja encontrar?" autofocus> <button class="searx-button searx-button-primary searx-search-btn" id="searx-inline-search">Pesquisar</button> </div> <div class="searx-search-history" id="searx-history"></div> </div> <div class="searx-content"> <div class="searx-section"> <div class="searx-collapsible-header"> <div class="searx-section-title"> Categorias <span id="categories-counter"></span> </div> <div class="searx-toggle-icon">▼</div> </div> <div class="searx-collapsible-content"> <div class="searx-checkbox-grid" id="categories-grid"></div> </div> </div> <div class="searx-divider"></div> <div class="searx-section"> <div class="searx-collapsible-header"> <div class="searx-section-title"> Motores de Busca <span id="engines-counter"></span> </div> <div class="searx-toggle-icon">▼</div> </div> <div class="searx-collapsible-content"> <div class="searx-checkbox-grid" id="engines-grid"></div> </div> </div> </div> <div class="searx-footer"> <div class="searx-switcher"> <label class="searx-switch"> <input type="checkbox" id="searx-new-tab" ${prefs.newTab ? 'checked' : ''}> <span class="searx-slider"></span> </label> <label for="searx-new-tab">Abrir em nova aba</label> </div> <div class="searx-btn-group"> <button class="searx-button searx-button-secondary" id="searx-save">Salvar Preferências</button> <button class="searx-button searx-button-danger" id="searx-close">×</button> </div> </div> </div> `; document.body.appendChild(overlay); console.log("Overlay do Searx adicionado ao DOM"); // Tornar a modal arrastável pelo cabeçalho const modal = overlay.querySelector('.searx-modal'); const header = modal.querySelector('.searx-header'); makeSearxDraggable(modal, header); // Posicionar a modal no centro inicialmente const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; const modalWidth = modal.offsetWidth; const modalHeight = modal.offsetHeight; modal.style.top = `${(viewportHeight - modalHeight) / 2}px`; modal.style.left = `${(viewportWidth - modalWidth) / 2}px`; // Configurar seções colapsáveis const collapsibleHeaders = document.querySelectorAll('.searx-collapsible-header'); collapsibleHeaders.forEach(header => { header.addEventListener('click', () => { header.classList.toggle('active'); const content = header.nextElementSibling; content.classList.toggle('open'); }); }); // Preencher histórico const historyContainer = document.getElementById('searx-history'); prefs.history.forEach(query => { const item = document.createElement('div'); item.className = 'searx-history-item'; item.textContent = query; item.addEventListener('click', () => { document.getElementById('searx-query').value = query; }); historyContainer.appendChild(item); }); // Preencher categorias const categoriesGrid = document.getElementById('categories-grid'); Object.entries(SEARX_CATEGORIES).forEach(([value, label]) => { const item = document.createElement('div'); item.className = 'searx-checkbox-item'; item.innerHTML = ` <input type="checkbox" class="searx-checkbox searx-category" value="${value}" id="cat-${value}" ${prefs.categories.includes(value) ? 'checked' : ''}> <label for="cat-${value}">${label}</label> `; categoriesGrid.appendChild(item); }); // Preencher motores const enginesGrid = document.getElementById('engines-grid'); Object.entries(SEARX_ENGINES).forEach(([value, label]) => { const item = document.createElement('div'); item.className = 'searx-checkbox-item'; item.innerHTML = ` <input type="checkbox" class="searx-checkbox searx-engine" value="${value}" id="eng-${value}" ${prefs.engines.includes(value) ? 'checked' : ''}> <label for="eng-${value}">${label}</label> `; enginesGrid.appendChild(item); }); // Atualizar contadores updateSearxCounters(); // Event Listeners document.getElementById('searx-save').addEventListener('click', () => { const categories = Array.from(document.querySelectorAll('.searx-category:checked')).map(el => el.value); const engines = Array.from(document.querySelectorAll('.searx-engine:checked')).map(el => el.value); const newTab = document.getElementById('searx-new-tab').checked; saveSearxPreferences({ categories, engines, newTab, history: prefs.history }); showSearxToast('Preferências salvas com sucesso!'); }); document.getElementById('searx-inline-search').addEventListener('click', performSearxSearch); document.getElementById('searx-close').addEventListener('click', closeSearxModal); // Fechar ao clicar fora overlay.addEventListener('click', e => { if (e.target === overlay) closeSearxModal(); }); // Shortcuts document.getElementById('searx-query').addEventListener('keydown', e => { if (e.key === 'Enter') performSearxSearch(); if (e.key === 'Escape') closeSearxModal(); }); // Adicionar animação com requestAnimationFrame para melhor performance requestAnimationFrame(() => { overlay.classList.add('searx-fade-in'); modal.classList.add('searx-move-up'); }); // Função de fechar modal com animação function closeSearxModal() { overlay.classList.remove('searx-fade-in'); overlay.querySelector('.searx-modal').classList.remove('searx-move-up'); setTimeout(() => overlay.remove(), SEARX_CONFIG.animationDuration); } // Realizar pesquisa function performSearxSearch() { const query = document.getElementById('searx-query').value.trim(); if (!query) { document.getElementById('searx-query').focus(); return; } // Obter categorias e motores selecionados const categories = Array.from(document.querySelectorAll('.searx-category:checked')).map(el => el.value); const engines = Array.from(document.querySelectorAll('.searx-engine:checked')).map(el => el.value); const newTab = document.getElementById('searx-new-tab').checked; // Salvar preferências e seleções saveSearxPreferences({ categories, engines, newTab, history: prefs.history }); // Construir URL const searchUrl = `${SEARX_CONFIG.instanceUrl}/?q=${encodeURIComponent(query)}&categories=${categories.join(',')}&engines=${engines.join(',')}&language=auto&safesearch=${SEARX_CONFIG.safeSearch}&image_proxy=${SEARX_CONFIG.imageProxy}&url_tracking_remove=${SEARX_CONFIG.urlTrackingRemove}`; // Salvar no histórico saveSearxToHistory(query); // Abrir pesquisa window.open(searchUrl, newTab ? '_blank' : '_self'); closeSearxModal(); } // Atualizar contadores de seleção function updateSearxCounters() { const catCount = document.querySelectorAll('.searx-category:checked').length; const engCount = document.querySelectorAll('.searx-engine:checked').length; document.getElementById('categories-counter').textContent = `${catCount} de ${Object.keys(SEARX_CATEGORIES).length} selecionadas`; document.getElementById('engines-counter').textContent = `${engCount} de ${Object.keys(SEARX_ENGINES).length} selecionados`; } // Auto-foco na caixa de pesquisa setTimeout(() => document.getElementById('searx-query').focus(), 100); } catch (error) { console.error("Erro ao mostrar modal Searx:", error); showOverlayConfirmationAutoClose("Erro", "Falha ao abrir o Searx: " + error.message); } } const btnCopyLink = createCopyLinkButton(); const btnUrlFull = createUrlFullButton(); const btnIsgd = createIsgdButton(); const btnActivateClickCopy = createActivateClickCopyButton(); const btnPaywallOff = createPaywallOffButton(); const btnGoogleTrad = (function createGoogleTradButton() { const btn = document.createElement('button'); btn.textContent = 'Google trad.'; btn.className = 'btn-default btn-googletrad'; btn.title = 'Traduza a página'; btn.addEventListener('click', function() { toggleGoogleTranslate(); updateGoogleTradButtonStyle(btn); }); updateGoogleTradButtonStyle(btn); return btn; })(); const btnSearx = createSearxButton(); const btnTopBottom = createButton("Topo/Fundo", toggleTopBottom); btnTopBottom.className = 'btn-default'; btnTopBottom.title = 'Ativa/desativa o recurso de rolagem para o topo e fundo (apenas nesta página)'; btnTopBottom.style.margin = "0"; btnIMGs = createButton("IMGs", toggleImgs); btnIMGs.className = 'btn-default'; btnIMGs.title = 'Ativa/desativa ícones para copiar imagem ou link em todas as imagens da página'; btnIMGs.style.margin = "0"; const btnReadingMode = createReadingModeButton(); btnDataExposed = createDataExposedButton(); const btnTurboLoader = createTurboLoaderButton(); const youtubeMenuButton = createYoutubeMenuButton(); const pdfMenuButton = createPDFsMenuButton(); centerContainer.appendChild(youtubeMenuButton); centerContainer.appendChild(btnUrlFull); centerContainer.appendChild(btnCopyLink); centerContainer.appendChild(btnIsgd); centerContainer.appendChild(pdfMenuButton); centerContainer.appendChild(btnActivateClickCopy); centerContainer.appendChild(btnPaywallOff); centerContainer.appendChild(btnGoogleTrad); centerContainer.appendChild(btnSearx); centerContainer.appendChild(btnTopBottom); centerContainer.appendChild(btnIMGs); centerContainer.appendChild(btnReadingMode); centerContainer.appendChild(btnDataExposed); centerContainer.appendChild(btnTurboLoader); console.log('Botões adicionados ao container central'); let topBottomElements = null; function initializeTopBottom() { if (location.hostname.includes('youtube.com')) { showOverlayConfirmationAutoClose("Aviso", "No YouTube não funciona devido a bloqueios internos."); return; } let scrollElement = document.documentElement; const buttonTop = document.createElement('div'); buttonTop.className = 'GO_TO_TOP_button'; buttonTop.innerHTML = ` <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"> <path d="M825.568 555.328l-287.392-289.28C531.808 259.648 523.488 256.576 515.2 256.64 514.08 256.544 513.12 256 512 256c-4.672 0-9.024 1.088-13.024 2.88-4.032 1.536-7.872 3.872-11.136 7.136l-259.328 258.88c-12.512 12.48-12.544 32.736-0.032 45.248 6.24 6.272 14.432 9.408 22.656 9.408 8.192 0 16.352-3.136 22.624-9.344L480 364.288V928c0 17.696 14.336 32 32 32s32-14.304 32-32V362.72l236.192 237.728c6.24 6.272 14.496 9.44 22.688 9.44s16.32-3.104 22.56-9.312C838.016 588.128 838.048 567.84 825.568 555.328z"/> <path d="M864 192H160C142.336 192 128 177.664 128 160s14.336-32 32-32h704c17.696 0 32 14.336 32 32S881.696 192 864 192z"/> </svg>`; Object.assign(buttonTop.style, { position: 'fixed', right: '14px', top: 'calc(50% - 40px)', width: '30px', height: '30px', borderRadius: '5px', backgroundColor: 'white', opacity: '0.8', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', zIndex: '10000001' }); buttonTop.addEventListener('click', () => { console.log('Botão Topo clicado'); if (scrollElement) { scrollElement.scrollTop = 0; console.log('Rolando para o topo do elemento:', scrollElement.tagName); } else { console.error('Elemento de rolagem não encontrado'); window.scrollTo({ top: 0, behavior: 'smooth' }); } }); const buttonBottom = document.createElement('div'); buttonBottom.className = 'GO_TO_BOTTOM_button'; buttonBottom.innerHTML = ` <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"> <path d="M198.4 468.352l287.392 289.28c6.368 6.4 14.688 9.472 22.976 9.408 1.12 0.096 2.08 0.64 3.2 0.64 4.672 0 9.024-1.088 13.024-2.88 4.032-1.536 7.872-3.872 11.136-7.136l259.328-258.88c12.512-12.48 12.544-32.736 0.032-45.248-6.24-6.272-14.432-9.408-22.656-9.408-8.192 0-16.352 3.136-22.624 9.344L544 659.712V96c0-17.696-14.336-32-32-32s-32 14.304-32 32v565.28L243.808 423.552c-6.24-6.272-14.496-9.44-22.688-9.44s-16.32 3.104-22.56 9.312c-12.48 12.512-12.512 32.8-0.032 45.312z"/> <path d="M160 832h704c17.664 0 32 14.336 32 32s-14.336 32-32 32H160c-17.664 0-32-14.336-32-32s14.336-32 32-32z"/> </svg>`; Object.assign(buttonBottom.style, { position: 'fixed', right: '14px', top: 'calc(50% + 10px)', width: '30px', height: '30px', borderRadius: '5px', backgroundColor: 'white', opacity: '0.8', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', zIndex: '10000001' }); let scrollingInterval; let isScrolling = false; buttonBottom.addEventListener('click', () => { console.log('Botão Fundo clicado'); if (isScrolling) { clearInterval(scrollingInterval); buttonBottom.style.backgroundColor = 'white'; isScrolling = false; console.log('Parando rolagem contínua'); } else { if (scrollElement) { scrollingInterval = setInterval(() => { const targetHeight = scrollElement.scrollHeight - scrollElement.clientHeight; scrollElement.scrollTop = targetHeight; console.log('Rolando para o fundo do elemento:', scrollElement.scrollHeight); }, 1000); buttonBottom.style.backgroundColor = 'green'; isScrolling = true; console.log('Iniciando rolagem contínua'); } else { console.error('Elemento de rolagem não encontrado'); window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); } } }); topBottomElements = { buttonTop, buttonBottom }; document.body.appendChild(buttonTop); document.body.appendChild(buttonBottom); } function removeTopBottom() { if (topBottomElements) { topBottomElements.buttonTop.remove(); topBottomElements.buttonBottom.remove(); topBottomElements = null; } } function toggleTopBottom() { if (topBottomElements) { removeTopBottom(); btnTopBottom.removeAttribute("data-active"); } else { initializeTopBottom(); if (!location.hostname.includes('youtube.com')) { btnTopBottom.setAttribute("data-active", "true"); } } } try { console.log('Tentando inicializar a barra'); // Verificar o estado antes de adicionar ao DOM if (barHidden) { toolbar.style.display = 'none'; } document.body.appendChild(toolbar); // Garante que a barra esteja no DOM // Configurar atalho de teclado setupKeyboardShortcut(); // Aguarda o DOM carregar completamente antes de aplicar o layout window.addEventListener('load', () => { setTimeout(() => { if (barHidden) { console.log('Barra iniciada como oculta devido ao estado global salvo'); hideBar(); } else { console.log('Barra exibida por padrão'); showBar(); } }, 100); // Reduzido para 100ms para minimizar a aparição momentânea }); } catch (error) { console.error('Erro ao inicializar a barra:', error); toolbar.style.display = 'flex'; document.body.appendChild(toolbar); setupKeyboardShortcut(); // Também tenta configurar o atalho em caso de erro showOverlayConfirmationAutoClose("Erro", "Falha ao inicializar a barra: " + error.message); } // Observador para reaplicar o layout após mudanças no <ytd-app> if (location.hostname.includes('youtube.com')) { const observer = new MutationObserver(() => { if (!barHidden) { if (urlBarActive) { applyPushDownWithUrlBar(); } else { applyPushDown(); } } }); const appElement = document.querySelector('ytd-app') || document.body; observer.observe(appElement, { childList: true, subtree: true }); console.log('Observador de layout inicializado para ytd-app'); } if (location.hostname.includes('youtube.com')) { const observer = new MutationObserver((mutations) => { mutations.forEach(() => { const newVideoId = getVideoIdFromUrl(); if (newVideoId && newVideoId !== lastVideoId) { lastVideoId = newVideoId; } }); }); let lastVideoId = getVideoIdFromUrl(); observer.observe(document.querySelector('ytd-app') || document.body, { childList: true, subtree: true }); } // Inicializar o Turbo Loader se estiver ativo if (turboLoaderActive && !window.location.hostname.includes('youtube.com')) { initTurboLoader(); } console.log('Script finalizado'); })();