Created
April 18, 2025 19:18
-
-
Save Programazing/d29915fb01335f599f22045809fe7906 to your computer and use it in GitHub Desktop.
Tampermonkey YouTube Transcript Extractor - Extracts YouTube transcripts to the clipboard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name YouTube Transcript Extractor | |
// @namespace http://tampermonkey.net/ | |
// @version 1.9 | |
// @description Extracts YouTube transcripts to the clipboard | |
// @author Programazing | |
// @match https://www.youtube.com/watch?v=* | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
function waitForElement(selector, timeout = 10000) { | |
return new Promise((resolve, reject) => { | |
const interval = 100; | |
let elapsed = 0; | |
const timer = setInterval(() => { | |
const el = document.querySelector(selector); | |
if (el) { | |
clearInterval(timer); | |
resolve(el); | |
} else if ((elapsed += interval) >= timeout) { | |
clearInterval(timer); | |
reject(new Error(`Element ${selector} not found within ${timeout}ms`)); | |
} | |
}, interval); | |
}); | |
} | |
async function addCopyButton() { | |
// Wait for the masthead's right section | |
let mastheadEnd; | |
try { | |
mastheadEnd = await waitForElement('ytd-masthead #end'); | |
} catch { | |
console.error("Could not find masthead end section"); | |
return; | |
} | |
if (document.getElementById('copy-transcript-btn')) return; | |
// Create button with beautiful styling | |
const btn = document.createElement('button'); | |
btn.id = 'copy-transcript-btn'; | |
btn.textContent = 'Copy Transcript'; | |
btn.style.marginLeft = '12px'; | |
btn.style.padding = '10px 16px'; | |
btn.style.background = '#1da1f2'; | |
btn.style.color = '#fff'; | |
btn.style.border = 'none'; | |
btn.style.borderRadius = '6px'; | |
btn.style.fontSize = '16px'; | |
btn.style.cursor = 'pointer'; | |
btn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)'; | |
btn.style.transition = 'background 0.2s'; | |
btn.style.height = '40px'; | |
btn.style.alignSelf = 'center'; | |
btn.style.display = 'inline-flex'; | |
btn.style.alignItems = 'center'; | |
btn.onmouseover = () => { btn.style.background = '#0d8ddb'; }; | |
btn.onmouseout = () => { btn.style.background = '#1da1f2'; }; | |
btn.onclick = async () => { | |
btn.textContent = 'Opening transcript...'; | |
btn.disabled = true; | |
try { | |
// Try to find and click the transcript button | |
let transcriptBtn = Array.from(document.querySelectorAll('button')) | |
.find(b => (b.textContent || "").toLowerCase().includes('transcript') && | |
(b.textContent || "").toLowerCase().includes('show')); | |
if (transcriptBtn) { | |
transcriptBtn.click(); | |
// Wait for transcript panel to appear and load | |
await new Promise(resolve => setTimeout(resolve, 2000)); | |
} | |
// Look for transcript panel directly | |
const transcriptPanel = document.querySelector('ytd-transcript-search-panel-renderer') || | |
document.querySelector('[role="dialog"] ytd-transcript-segment-list-renderer') || | |
document.querySelector('ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-transcript"]'); | |
if (!transcriptPanel) { | |
alert('Could not find transcript panel. This video may not have a transcript.'); | |
btn.textContent = 'Copy Transcript'; | |
btn.disabled = false; | |
return; | |
} | |
// Wait an extra moment for content to load | |
btn.textContent = 'Extracting transcript...'; | |
await new Promise(resolve => setTimeout(resolve, 1000)); | |
// Try multiple different selectors to find transcript segments | |
const segments = transcriptPanel.querySelectorAll('ytd-transcript-segment-renderer') || | |
transcriptPanel.querySelectorAll('.ytd-transcript-segment-renderer') || | |
transcriptPanel.querySelectorAll('[class*="segment"]'); | |
let lines = []; | |
if (segments && segments.length > 0) { | |
segments.forEach(seg => { | |
const time = seg.querySelector('#segment-timestamp') ? | |
seg.querySelector('#segment-timestamp').textContent.trim() : | |
seg.querySelector('[id*="timestamp"]')?.textContent.trim() || ''; | |
const text = seg.querySelector('#segment-text') ? | |
seg.querySelector('#segment-text').textContent.trim() : | |
seg.querySelector('[id*="text"]')?.textContent.trim() || ''; | |
if (time && text) lines.push(`${time} ${text}`); | |
}); | |
} | |
let clipboardText = ''; | |
if (lines.length === 0) { | |
// Fallback: split raw text at timestamps, robustly | |
const text = transcriptPanel.textContent.trim(); | |
const parts = text.split(/(\d{1,2}:\d{2})/g); | |
let formattedLines = []; | |
for (let i = 1; i < parts.length; i += 2) { | |
const timestamp = parts[i]; | |
const lineText = (parts[i + 1] || '').replace(/\s+/g, ' ').trim(); | |
if (timestamp && lineText) { | |
formattedLines.push(`${timestamp} ${lineText}`); | |
} | |
} | |
if (formattedLines.length > 0) { | |
clipboardText = formattedLines.join('\n'); | |
} else { | |
clipboardText = text; | |
} | |
} else { | |
clipboardText = lines.join('\n'); | |
} | |
try { | |
await navigator.clipboard.writeText(clipboardText); | |
btn.textContent = 'Copied!'; | |
} catch (err) { | |
alert("Failed to copy transcript to clipboard."); | |
btn.textContent = 'Copy Transcript'; | |
} | |
setTimeout(() => { | |
btn.textContent = 'Copy Transcript'; | |
btn.disabled = false; | |
}, 2000); | |
} catch (error) { | |
console.error('Error in transcript extraction:', error); | |
alert('An error occurred while extracting the transcript.'); | |
btn.textContent = 'Copy Transcript'; | |
btn.disabled = false; | |
} | |
}; | |
mastheadEnd.appendChild(btn); | |
} | |
// Observe URL changes (for SPA navigation) | |
let lastUrl = ''; | |
setInterval(() => { | |
if (location.href !== lastUrl) { | |
lastUrl = location.href; | |
setTimeout(addCopyButton, 1500); | |
} | |
}, 500); | |
// Initial run | |
setTimeout(addCopyButton, 2000); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment