Adds one icon to the YouTube transcript panel. Click it and everything in the transcript is selected so you can copy it instantly.
// ==UserScript==
// @name YouTube Transcript Select All
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Adds one icon to the YouTube transcript panel. Click it and everything in the transcript is selected so you can copy it instantly.
// @author echoZ
// @license MIT
// @match https://www.youtube.com/watch*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
const BTN_ID = 'yt-ts-selectall-btn';
const PANEL_IDS = [
'PAmodern_transcript_view',
'engagement-panel-searchable-transcript'
];
function findPanel() {
for (const id of PANEL_IDS) {
const p = document.querySelector(`ytd-engagement-panel-section-list-renderer[target-id="${id}"]`);
if (p && p.offsetParent !== null) return p;
}
return null;
}
function selectAllTranscript() {
const panel = findPanel();
if (!panel) { alert('Open the transcript panel first.'); return; }
const container =
panel.querySelector('#segments-container') ||
panel.querySelector('ytd-transcript-segment-list-renderer') ||
panel.querySelector('[class*="transcript-segment"]')?.parentElement ||
panel;
const range = document.createRange();
range.selectNodeContents(container);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
function injectButton() {
if (document.getElementById(BTN_ID)) return;
const panel = findPanel();
if (!panel) return;
const anchor =
panel.querySelector('#action-buttons') ||
panel.querySelector('#menu') ||
panel.querySelector('ytd-engagement-panel-title-header-renderer');
if (!anchor) return;
const btn = document.createElement('button');
btn.id = BTN_ID;
btn.title = 'Select all transcript text';
btn.style.cssText = `
background: transparent;
border: none;
cursor: pointer;
padding: 8px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--yt-spec-text-primary, #fff);
transition: background 0.2s;
margin-right: 4px;
`;
btn.onmouseenter = () => btn.style.background = 'rgba(255,255,255,0.1)';
btn.onmouseleave = () => btn.style.background = 'transparent';
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('width', '24');
svg.setAttribute('height', '24');
svg.style.cssText = 'fill:currentColor;pointer-events:none;';
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'M3 5h2V3a2 2 0 0 0-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2a2 2 0 0 0-2-2zM5 21v-2H3a2 2 0 0 0 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8a2 2 0 0 0 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2zM7 17h10V7H7v10z');
svg.appendChild(path);
btn.appendChild(svg);
btn.onclick = (e) => {
e.stopPropagation();
selectAllTranscript();
btn.style.background = 'rgba(0,200,80,0.25)';
setTimeout(() => btn.style.background = 'transparent', 800);
};
anchor.prepend(btn);
}
let lastUrl = location.href;
new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
document.getElementById(BTN_ID)?.remove();
}
injectButton();
}).observe(document.body, { childList: true, subtree: true });
})();