Last active
December 22, 2024 07:12
-
-
Save zhuochun/f06e9fa18d9b7fab843e5dd8116312ea to your computer and use it in GitHub Desktop.
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 to Clipboard | |
| // @namespace http://tampermonkey.net/ | |
| // @version 1.0 | |
| // @description Adds a button to copy the transcript to the clipboard. | |
| // @author ChatGPT | |
| // @match https://www.youtube.com/* | |
| // @grant none | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| // A helper function to wait for an element to appear in the DOM. | |
| function waitForElement(selector, timeout = 10000) { | |
| return new Promise((resolve, reject) => { | |
| const start = Date.now(); | |
| (function check() { | |
| const element = document.querySelector(selector); | |
| if (element) { | |
| resolve(element); | |
| } else if (Date.now() - start >= timeout) { | |
| reject(new Error(`Element ${selector} not found within ${timeout}ms`)); | |
| } else { | |
| requestAnimationFrame(check); | |
| } | |
| })(); | |
| }); | |
| } | |
| // Create the clipboard button | |
| function createClipboardButton() { | |
| const btn = document.createElement('button'); | |
| btn.style.display = 'inline-flex'; | |
| btn.style.alignItems = 'center'; | |
| btn.style.justifyContent = 'center'; | |
| btn.style.marginLeft = '8px'; | |
| btn.style.padding = '4px'; | |
| btn.style.border = 'none'; | |
| btn.style.borderRadius = '2px'; | |
| btn.style.cursor = 'pointer'; | |
| btn.style.background = 'transparent'; | |
| btn.style.color = 'var(--yt-spec-text-primary)'; | |
| btn.title = 'Copy transcript'; | |
| // Adding a clipboard icon (using an emoji for simplicity) | |
| btn.textContent = 'π'; | |
| btn.addEventListener('click', async () => { | |
| try { | |
| const transcriptPanel = document.querySelector('ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-searchable-transcript"]'); | |
| if (!transcriptPanel) { | |
| alert("Transcript panel not found!"); | |
| return; | |
| } | |
| // Extract text from the transcript panel | |
| let text = transcriptPanel.textContent || ''; | |
| // Split by newline, trim lines, remove empty lines | |
| const lines = text | |
| .split('\n') | |
| .map(line => line.trim()) | |
| .filter(line => line !== ''); | |
| const finalText = lines.join('\n'); | |
| await navigator.clipboard.writeText(finalText); | |
| // Provide some feedback to the user | |
| btn.textContent = 'β '; | |
| setTimeout(() => { btn.textContent = 'π'; }, 2000); | |
| } catch (err) { | |
| console.error("Failed to copy text: ", err); | |
| } | |
| }); | |
| return btn; | |
| } | |
| // Wait for the owner container and add the clipboard button | |
| waitForElement('#owner').then(ownerContainer => { | |
| // Ensure we only add one button if the script re-runs | |
| if (!ownerContainer.querySelector('.clipboard-button')) { | |
| const clipboardButton = createClipboardButton(); | |
| clipboardButton.classList.add('clipboard-button'); | |
| ownerContainer.appendChild(clipboardButton); | |
| } | |
| }).catch((error) => { | |
| console.warn(error); | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment