Ferramenta do YouTube

Copiar transcrição

Instale um favorito no Chrome para abrir a transcrição do vídeo, organizar os metadados e copiar todo o conteúdo.

Instalação rápida

Arraste para a barra de favoritos

Mostre a barra de favoritos com Ctrl + Shift + B e arraste o botão abaixo até ela.

📋 Copiar transcrição do YouTube

O botão deve ser salvo como favorito; ele não é executado nesta página.

Como usar

Três passos

  1. Abra um vídeo com transcrição no YouTube.
  2. Clique no favorito “Copiar transcrição do YouTube”.
  3. Cole o conteúdo no projeto de análise desejado.

Se a cópia automática for bloqueada, o próprio bookmarklet abrirá uma caixa com o botão “Copiar agora”.

Instalação manual

Código do favorito

Ver código-fonte legível
(async function () {
    const PREFIX = '[YT-Copy]';
    const SEGMENT_SELECTOR = 'transcript-segment-view-model, ytd-transcript-segment-renderer';
    const sleep = (milliseconds) => new Promise((resolve) => window.setTimeout(resolve, milliseconds));
    const segments = () => Array.from(document.querySelectorAll(SEGMENT_SELECTOR));

    function videoUrl() {
        try {
            const current = new URL(window.location.href);
            const videoId = current.searchParams.get('v');
            return videoId ? 'https://www.youtube.com/watch?v=' + encodeURIComponent(videoId) : current.href.split('&')[0];
        } catch (error) {
            return window.location.href.split('&')[0];
        }
    }

    function formatDate(value) {
        const match = String(value || '').match(/^(\d{4})-(\d{2})-(\d{2})/);
        return match ? match[3] + '/' + match[2] + '/' + match[1] : String(value || '');
    }

    function showStatus(message, success) {
        const title = Array.from(document.querySelectorAll('ytd-engagement-panel-title-header-renderer yt-formatted-string#title-text, #title .ytd-engagement-panel-title-header-renderer')).find((element) => element.offsetParent !== null);

        if (!title) {
            if (!success) window.alert(message);
            return;
        }

        const originalText = title.innerText;
        const originalColor = title.style.color;
        title.innerText = message;
        title.style.color = success ? '#4caf50' : '#d93025';
        window.setTimeout(() => {
            title.innerText = originalText;
            title.style.color = originalColor;
        }, 3500);
    }

    function manualCopy(text) {
        const overlay = document.createElement('div');
        const panel = document.createElement('div');
        const message = document.createElement('strong');
        const textarea = document.createElement('textarea');
        const actions = document.createElement('div');
        const copy = document.createElement('button');
        const close = document.createElement('button');

        overlay.style.cssText = 'position:fixed;inset:0;z-index:2147483647;display:grid;place-items:center;padding:20px;background:rgba(0,0,0,.65)';
        panel.style.cssText = 'width:min(760px,100%);display:grid;gap:12px;padding:18px;border-radius:10px;background:#fff;color:#172033;font:14px Arial,sans-serif';
        message.textContent = 'A cópia automática foi bloqueada. Clique em “Copiar agora”.';
        textarea.value = text;
        textarea.readOnly = true;
        textarea.style.cssText = 'width:100%;height:45vh;box-sizing:border-box;padding:10px;font:12px monospace';
        actions.style.cssText = 'display:flex;justify-content:flex-end;gap:8px';
        copy.textContent = 'Copiar agora';
        close.textContent = 'Fechar';
        [copy, close].forEach((button) => button.style.cssText = 'min-height:36px;padding:0 12px;border:1px solid #ccd3df;border-radius:7px;background:#f3f6fa;font-weight:700;cursor:pointer');
        copy.addEventListener('click', async () => {
            try {
                await navigator.clipboard.writeText(text);
            } catch (error) {
                textarea.focus();
                textarea.select();
                document.execCommand('copy');
            }
            overlay.remove();
            showStatus('✅ Copiado com sucesso!', true);
        });
        close.addEventListener('click', () => overlay.remove());
        actions.append(copy, close);
        panel.append(message, textarea, actions);
        overlay.append(panel);
        document.body.append(overlay);
        textarea.focus();
        textarea.select();
    }

    async function copyText(text) {
        try {
            await navigator.clipboard.writeText(text);
            showStatus('✅ Copiado com sucesso!', true);
        } catch (error) {
            manualCopy(text);
        }
    }

    async function extract() {
        const title = document.querySelector('meta[name="title"]')?.content || document.title.replace(/\s*-\s*YouTube\s*$/, '');
        const channel = document.querySelector('link[itemprop="name"]')?.getAttribute('content') || document.querySelector('ytd-channel-name .yt-formatted-string')?.innerText || 'Desconhecido';
        const published = document.querySelector('meta[itemprop="uploadDate"]')?.content || document.querySelector('meta[itemprop="datePublished"]')?.content || '';
        const header = 'Título: ' + title + '\nCanal: ' + channel + '\nData: ' + formatDate(published) + '\nURL: ' + videoUrl() + '\n\n---\n\n';
        const lines = segments().map((segment) => {
            const time = (segment.querySelector('.ytwTranscriptSegmentViewModelTimestamp, .segment-timestamp')?.innerText || '').trim();
            const text = (segment.querySelector('.ytAttributedStringHost, .segment-text')?.innerText || '').trim();
            return text ? (time ? time + ' - ' : '') + text : '';
        }).filter(Boolean);

        if (lines.length === 0) {
            throw new Error('Nenhum trecho de transcrição foi encontrado.');
        }

        console.log(PREFIX, 'Linhas processadas:', lines.length);
        await copyText(header + lines.join('\n'));
    }

    try {
        console.log(PREFIX, 'Script iniciado.');

        if (!/^(www\.)?youtube\.com$/i.test(window.location.hostname) || !window.location.pathname.includes('/watch')) {
            throw new Error('Abra uma página de vídeo do YouTube antes de usar o favorito.');
        }

        if (segments().length === 0) {
            const expand = document.querySelector('tp-yt-paper-button#expand');
            if (expand && expand.offsetParent !== null) expand.click();
            await sleep(500);

            const transcriptButton = document.querySelector('ytd-video-description-transcript-section-renderer button') || Array.from(document.querySelectorAll('button')).find((button) => /transcri/i.test(button.innerText || ''));

            if (!transcriptButton) {
                throw new Error('O botão de transcrição não foi encontrado. Verifique se o vídeo possui transcrição.');
            }

            transcriptButton.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));

            for (let attempt = 0; attempt < 120 && segments().length === 0; attempt += 1) {
                await sleep(250);
            }
        }

        if (segments().length === 0) {
            throw new Error('Tempo esgotado ao carregar a transcrição.');
        }

        await sleep(750);
        await extract();
    } catch (error) {
        console.error(PREFIX, error);
        showStatus('❌ ' + (error?.message || 'Falha ao copiar a transcrição.'), false);
    }
}());
Voltar às ferramentas do YouTube