您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Downloads and copies YouTube video transcripts.
// ==UserScript== // @name YouTube Transcript Downloader // @namespace https://github.com/blarer/youtube-transcript-downloader // @version 1.1 // @description Downloads and copies YouTube video transcripts. // @author Blareware aka blare // @match https://www.youtube.com/watch?v=* // @grant GM_addStyle // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; // Add styles using GM_addStyle GM_addStyle(` #download_btn, #copy_btn { color: var(--yt-spec-text-primary); background: var(--yt-spec-brand-button-background); border: none; border-radius: 18px; padding: 8px 16px; font-size: 14px; cursor: pointer; margin: 10px 15px; /* Added 15px margin-right */ transition: opacity 0.2s; } #download_btn:hover, #copy_btn:hover { opacity: 0.8; } `); function init() { // Updated selectors for 2023 YouTube structure const possibleSelectors = [ "ytd-watch-metadata", "#above-the-fold", "#title.ytd-watch-metadata", "#container.ytd-watch-metadata", "ytd-video-primary-info-renderer" ]; console.log('Searching for YouTube containers...'); let container = null; for (const selector of possibleSelectors) { container = document.querySelector(selector); if (container) { console.log('Found container using selector:', selector); break; } } if (!container) { console.log('Container not found, retrying...'); setTimeout(init, 1000); return; } // Add event listener to the transcript button const transcriptButton = document.querySelector('button[aria-label="Show transcript"]'); if (transcriptButton) { transcriptButton.addEventListener('click', handleTranscriptButtonClick); } else { console.error('Transcript button not found.'); } } async function handleTranscriptButtonClick() { // Check if button already exists if (document.getElementById('download_btn')) { console.log('Download button already exists'); return; } console.log('Creating download button...'); // Wait for the transcript container to load (up to 5 seconds) const maxWaitTime = 5000; const startTime = Date.now(); let transcriptContainer = null; while (Date.now() - startTime < maxWaitTime) { transcriptContainer = document.querySelector('div#segments-container'); if (transcriptContainer) { break; // Transcript container found, exit loop } // Wait 200ms before retrying await new Promise(resolve => setTimeout(resolve, 200)); } if (!transcriptContainer) { console.error('Transcript container not found.'); return; } // Create the download button const downloadBtn = document.createElement('button'); downloadBtn.id = 'download_btn'; downloadBtn.textContent = 'Download Transcript'; downloadBtn.addEventListener('click', handleDownload); // Create the copy button const copyBtn = document.createElement('button'); copyBtn.id = 'copy_btn'; copyBtn.textContent = 'Copy Transcript'; copyBtn.addEventListener('click', handleCopy); // Create a wrapper div for the buttons const wrapper = document.createElement('div'); wrapper.style.display = 'flex'; wrapper.style.justifyContent = 'flex-start'; wrapper.style.alignItems = 'center'; wrapper.style.marginTop = '10px'; wrapper.appendChild(downloadBtn); wrapper.appendChild(copyBtn); // Insert the buttons into the transcript container transcriptContainer.insertAdjacentElement('afterbegin', wrapper); console.log('Buttons successfully added to page'); } async function handleDownload() { try { // Wait for the transcript to load (up to 5 seconds) const maxWaitTime = 5000; const startTime = Date.now(); let transcriptElements = []; while (Date.now() - startTime < maxWaitTime) { transcriptElements = Array.from(document.querySelectorAll('div#segments-container ytd-transcript-segment-renderer div.segment')) .filter(el => el.textContent.trim() !== ''); if (transcriptElements.length > 0) { break; // Transcript found, exit loop } await new Promise(resolve => setTimeout(resolve, 200)); // Wait 200ms before retrying } if (!transcriptElements.length) { alert('No transcript found. Please open the transcript panel first.'); return; } const text = transcriptElements.map(el => el.textContent.trim()).join('\n'); const filename = `${document.title.replace(/[/\\?%*:|"<>]/g, '-')} - Transcript.txt`; // Save to file download(text, filename, "text/plain"); } catch (err) { console.error('Failed to process transcript:', err); } } async function handleCopy() { try { // Wait for the transcript to load (up to 5 seconds) const maxWaitTime = 5000; const startTime = Date.now(); let transcriptElements = []; while (Date.now() - startTime < maxWaitTime) { transcriptElements = Array.from(document.querySelectorAll('div#segments-container ytd-transcript-segment-renderer div.segment')) .filter(el => el.textContent.trim() !== ''); if (transcriptElements.length > 0) { break; // Transcript found, exit loop } await new Promise(resolve => setTimeout(resolve, 200)); // Wait 200ms before retrying } if (!transcriptElements.length) { alert('No transcript found. Please open the transcript panel first.'); return; } const text = transcriptElements.map(el => el.textContent.trim()).join('\n'); // Copy to clipboard await navigator.clipboard.writeText(text); console.log('Successfully copied transcript to clipboard'); } catch (err) { console.error('Failed to process transcript:', err); } } function download(data, filename, type) { try { const blob = new Blob([data], {type: type}); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); requestAnimationFrame(() => { URL.revokeObjectURL(url); document.body.removeChild(link); }); } catch (err) { console.error('Failed to download file:', err); } } // Start the initialization when page loads if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();