Barra de Botões Ferramentas Youtube

Cria uma barra fixa no topo com botões para funções diversas, testado no Brave.

当前为 2025-02-27 提交的版本,查看 最新版本

// ==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或关注我们的公众号极客氢云获取最新地址