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 YouTubeO botão deve ser salvo como favorito; ele não é executado nesta página.
Ferramenta do YouTube
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
Mostre a barra de favoritos com Ctrl + Shift + B e arraste o botão abaixo até ela.
📋 Copiar transcrição do YouTubeO botão deve ser salvo como favorito; ele não é executado nesta página.
Como usar
Se a cópia automática for bloqueada, o próprio bookmarklet abrirá uma caixa com o botão “Copiar agora”.
Instalação manual
(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);
}
}());