您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Cria uma barra fixa no topo com botões para funções diversas, testado no Brave.
当前为
// ==UserScript== // @name Barra de Botões Ferramentas Youtube // @namespace https://t.me/virumaniaa // @version 1.2.3 // @license MIT // @description Cria uma barra fixa no topo com botões para funções diversas, testado no Brave. // @author // @match *://*.youtube.com/* // @match *://*.scribd.com/* // @match *://*.zlibrary*/* // @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 * // ==/UserScript== (function() { 'use strict'; console.log('Script iniciado'); // Impede a execução em iframes if (window.top !== window.self) { console.log('Barra não exibida em iframe'); return; } // Configurações e funções avançadas Z-Library 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); } // Função para criar botões estilizados function createButton(text, onClick, color = '#007bff') { const btn = document.createElement('button'); btn.innerHTML = 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; } // Variável global para armazenar os nós de texto originais let originalTextNodes = []; // Função para alternar o modo de tradução via Google Tradutor 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 { 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."); return; } 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'); const translatedBatch = await translateText(batch); translatedTexts = translatedTexts.concat(translatedBatch.split('\n')); } originalTextNodes.forEach((node, index) => { if (translatedTexts[index]) { node.node.textContent = translatedTexts[index]; } }); document.body.setAttribute('data-translated', 'true'); showOverlayConfirmationAutoClose("Sucesso", "Página traduzida para português!"); updateGoogleTradButtonStyle(btnGoogleTrad); } catch (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)}`; GM_xmlhttpRequest({ method: 'GET', url: url, onload: function(response) { if (response.status === 200) { try { const data = JSON.parse(response.responseText); const translated = data[0].map(item => item[0]).join(''); resolve(translated); } catch (e) { reject(new Error("Erro ao processar a resposta da tradução.")); } } else { reject(new Error(`Status ${response.status}`)); } }, onerror: function() { reject(new Error("Falha na requisição de tradução.")); } }); }); } 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')) { youtubeApp = document.querySelector('ytd-app'); youtubeMasthead = document.querySelector('ytd-masthead#masthead'); } 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) { updateMargin([youtubeMasthead, youtubeApp], marginValue); } else if (protonHeader && protonMain) { updateMargin([protonHeader, protonMain], marginValue); } else { updateMargin([document.body], marginValue); // Apenas body para evitar rolagem } } function removePushDown() { console.log('Removendo push down'); updateMargin([youtubeMasthead, 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; } .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; }`; document.head.appendChild(customStyle); console.log('Estilos personalizados adicionados'); 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'; } }); } const toolbar = document.createElement('div'); toolbar.id = 'userToolbar'; 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' }); document.body.appendChild(toolbar); console.log('Toolbar criada e adicionada ao DOM'); 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.innerText = titulo; overlay.appendChild(tituloElement); const mensagemElement = document.createElement('p'); mensagemElement.style.margin = '0'; mensagemElement.innerText = mensagem; overlay.appendChild(mensagemElement); document.body.appendChild(overlay); setTimeout(() => overlay.remove(), timeout); } // Funções para persistência do estado da toolbar (usa GM_setValue/GM_getValue para manter o estado global) function getToolbarStateGlobal() { return GM_getValue("toolbar_hidden_global", false); } function setToolbarStateGlobal(hidden) { GM_setValue("toolbar_hidden_global", hidden); } let barHidden = getToolbarStateGlobal(); // Estado inicial baseado no estado global let floatButton = null; let urlBarActive = false; // Estado não persistente, começa sempre desativado let urlBarContainer = null; const DRAG_THRESHOLD = 5; function createToggleBarButton() { const btn = document.createElement('button'); btn.innerText = 'Ocultar'; btn.className = 'btn-toggle'; btn.title = 'Oculta a barra, clique em Mostrar para voltar ao normal ou use o atalho Control+Shift+m'; btn.addEventListener('click', () => { barHidden ? showBar() : hideBar(); }); return btn; } function createFloatButton() { const btn = document.createElement('div'); btn.innerText = 'Mostrar'; btn.className = 'btn-toggle float-button'; btn.title = 'Mostra a barra'; Object.assign(btn.style, { position: 'fixed', top: '20px', left: '20px', zIndex: '10000001', userSelect: 'none', boxShadow: '0 2px 5px rgba(0,0,0,0.5)', cursor: 'grab', display: 'block', backgroundColor: '#ffff00', padding: '5px 10px', borderRadius: '4px' }); let isDragging = false, startX = 0, startY = 0, offsetX = 0, offsetY = 0; btn.addEventListener('mousedown', (e) => { startX = e.clientX; startY = e.clientY; offsetX = e.clientX - btn.offsetLeft; offsetY = e.clientY - btn.offsetTop; isDragging = true; btn.style.cursor = 'grabbing'; console.log('Botão "Mostrar" arrastado'); }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const moveX = e.clientX - startX; const moveY = e.clientY - startY; if (Math.abs(moveX) > DRAG_THRESHOLD || Math.abs(moveY) > DRAG_THRESHOLD) { e.preventDefault(); btn.style.left = `${e.clientX - offsetX}px`; btn.style.top = `${e.clientY - offsetY}px`; } }); document.addEventListener('mouseup', (e) => { if (!isDragging) return; isDragging = false; btn.style.cursor = 'grab'; const moveX = e.clientX - startX; const moveY = e.clientY - startY; if (Math.abs(moveX) <= DRAG_THRESHOLD && Math.abs(moveY) <= DRAG_THRESHOLD) { showBar(); } }); return btn; } function hideBar() { barHidden = true; toolbar.style.display = 'none'; if (urlBarContainer) { urlBarContainer.style.display = 'none'; } removePushDown(); setToolbarStateGlobal(true); if (!floatButton) { floatButton = createFloatButton(); document.body.appendChild(floatButton); console.log('Botão "Mostrar" criado e adicionado ao DOM'); } } function showBar() { barHidden = false; toolbar.style.display = 'flex'; if (urlBarActive && urlBarContainer) { urlBarContainer.style.display = 'block'; urlBarContainer.querySelector('#fullUrlInput').value = window.location.href; applyPushDownWithUrlBar(); } else { applyPushDown(); } setToolbarStateGlobal(false); if (floatButton) { document.body.removeChild(floatButton); floatButton = null; console.log('Botão "Mostrar" removido do DOM'); } } const toggleButton = createToggleBarButton(); toolbar.appendChild(toggleButton); console.log('Botão "Ocultar" adicionado à toolbar'); 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'); document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.shiftKey && e.key === 'M') { if (barHidden) { showBar(); showOverlayConfirmationAutoClose("Barra Exibida", "A barra foi restaurada via atalho (Ctrl+Shift+M)."); console.log('Barra restaurada via atalho Ctrl+Shift+M'); } } }); // Detecta navegação com "voltar" ou "avançar" no navegador e mantém o estado global window.addEventListener('popstate', () => { console.log('Navegação detectada via histórico (voltar/avançar)'); if (getToolbarStateGlobal()) { hideBar(); } else { showBar(); } }); // Observa mudanças na URL apenas para atualizar a barra de URL, sem interferir na barra fixa 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.innerHTML = '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.innerHTML = '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.innerText = '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.innerText = 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.innerText = 'Baixar Original'; btnDownOrig.className = 'btn-default'; btnDownOrig.addEventListener('click', event => { event.stopPropagation(); downloadSubtitle(subtitle); }); div.appendChild(btnDownOrig); const btnDownTrad = document.createElement('button'); btnDownTrad.innerText = '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.innerText = '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) { const txt = document.createElement("textarea"); txt.innerHTML = text; return txt.value; } function convertXmlToSrt(xmlContent) { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlContent, "text/xml"); const textElements = Array.from(xmlDoc.getElementsByTagName("text")); return textElements.map((textElement, index) => { const start = parseFloat(textElement.getAttribute("start")); const duration = parseFloat(textElement.getAttribute("dur")); const end = start + duration; const startTime = formatTime(start); const endTime = formatTime(end); const text = decodeHTMLEntities(textElement.textContent.trim()); return `${index + 1}\n${startTime} --> ${endTime}\n${text}\n\n`; }).join(''); } function convertXmlToPlainText(xmlContent) { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlContent, "text/xml"); const textElements = Array.from(xmlDoc.getElementsByTagName("text")); return textElements .map(el => decodeHTMLEntities(el.textContent.replace(/\n/g, " ").trim())) .join(" ") .trim(); } 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); 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); 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 { const response = await fetch(subtitle.baseUrl); const xmlContent = await response.text(); const plainText = convertXmlToPlainText(xmlContent); 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 }); } catch (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.innerText = '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.innerText = `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.innerText = '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.innerText = '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.innerText = 'Scribd down'; btn.className = 'btn-green'; 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-green'; 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; } 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() { const urlBarHeight = urlBarContainer ? urlBarContainer.offsetHeight : 0; const totalHeight = barOriginalHeight + urlBarHeight; const marginValueWithUrlBar = `${totalHeight}px`; if (youtubeApp && youtubeMasthead) { updateMargin([youtubeMasthead, youtubeApp], marginValueWithUrlBar); } else if (protonHeader && protonMain) { updateMargin([protonHeader, protonMain], marginValueWithUrlBar); } else { updateMargin([document.body], marginValueWithUrlBar); // Apenas body para evitar rolagem } console.log('Push down ajustado com barra de URL'); } 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(); } } function createIAsButton() { const container = document.createElement('div'); container.style.position = 'relative'; container.style.display = 'inline-block'; const mainBtn = document.createElement('button'); mainBtn.textContent = 'IAs'; mainBtn.className = 'btn-default'; mainBtn.title = 'Menu de links'; 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 iaButtons = [ { label: 'ChatGPT', url: 'https://chatgpt.com' }, { label: 'Qwen', url: 'https://chat.qwenlm.ai/' }, { label: 'DeepSeek', url: 'https://chat.deepseek.com/' }, { label: 'Decopy', url: 'https://decopy.ai/pt/' }, { label: 'none', url: '' } ]; iaButtons.forEach(btnData => { const btn = document.createElement('button'); btn.textContent = btnData.label; btn.className = 'btn-default'; btn.style.display = 'block'; btn.style.width = '100%'; btn.style.padding = '5px 10px'; btn.style.margin = '4px 0'; btn.style.textAlign = 'center'; btn.style.cursor = 'pointer'; btn.style.border = '1px solid #444'; btn.style.borderRadius = '3px'; btn.style.backgroundColor = '#222'; btn.addEventListener('click', (e) => { e.stopPropagation(); if (btnData.url) { window.open(btnData.url, '_blank'); } submenu.style.visibility = 'hidden'; submenu.style.opacity = '0'; mainBtn.removeAttribute("data-active"); }); 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; } const btnCopyLink = createCopyLinkButton(); const btnUrlFull = createUrlFullButton(); const btnIsgd = createIsgdButton(); const btnScribdDown = createScribdDownButton(); const btnActivateClickCopy = createActivateClickCopyButton(); const btnPaywallOff = createPaywallOffButton(); const btnZLibrary = createZLibraryButton(); const btnIAs = createIAsButton(); 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 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 youtubeMenuButton = createYoutubeMenuButton(); centerContainer.appendChild(youtubeMenuButton); centerContainer.appendChild(btnUrlFull); centerContainer.appendChild(btnCopyLink); centerContainer.appendChild(btnIsgd); centerContainer.appendChild(btnScribdDown); centerContainer.appendChild(btnActivateClickCopy); centerContainer.appendChild(btnPaywallOff); centerContainer.appendChild(btnZLibrary); centerContainer.appendChild(btnIAs); centerContainer.appendChild(btnGoogleTrad); centerContainer.appendChild(btnTopBottom); centerContainer.appendChild(btnIMGs); 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"); } } } if (getToolbarStateGlobal()) { console.log('Barra iniciada como oculta devido ao estado global salvo'); hideBar(); } else { console.log('Barra exibida por padrão'); showBar(); } 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 }); } console.log('Script finalizado'); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址