您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Extract YouTube video chapters as JSON with manual trigger and download option, button placed under video, and dynamic file name with spaces replaced by underscores.
// ==UserScript== // @name YouTube Chapters to JSON // @version 1.4 // @description Extract YouTube video chapters as JSON with manual trigger and download option, button placed under video, and dynamic file name with spaces replaced by underscores. // @author Mazen Tamer // @match https://www.youtube.com/watch* // @grant none // @license MIT // @namespace https://greasyfork.org/users/1408089 // ==/UserScript== (function() { 'use strict'; // Function to parse timestamp into milliseconds function parseTimestamp(timeString) { const parts = timeString.split(':').reverse(); let milliseconds = 0; if (parts[0]) milliseconds += parseInt(parts[0], 10) * 1000; // Seconds if (parts[1]) milliseconds += parseInt(parts[1], 10) * 60 * 1000; // Minutes if (parts[2]) milliseconds += parseInt(parts[2], 10) * 60 * 60 * 1000; // Hours return milliseconds; } // Function to extract chapters and return as JSON function extractChapters() { const chapters = []; const chapterElements = document.querySelectorAll('ytd-macro-markers-list-item-renderer'); chapterElements.forEach(chapter => { const titleElement = chapter.querySelector('h4.macro-markers'); const timeElement = chapter.querySelector('div#time'); if (titleElement && timeElement) { const title = titleElement.title; const timestamp = parseTimestamp(timeElement.textContent.trim()); // Check if the chapter already exists const existingChapter = chapters.find(ch => ch.timestamp === timestamp && ch.title === title); if (!existingChapter) { chapters.push({ title, timestamp }); } } }); return chapters; } // Function to download the chapters JSON as a file function downloadJSON(chapters) { const json = JSON.stringify(chapters, null, 2); // Get the video title from the page and use it as the filename const videoTitle = document.title.replace(' - YouTube', '').replace(/[\/:*?"<>|]/g, '').replace(/\s+/g, '_'); // Replace spaces with underscores const filename = `${videoTitle}-chapters.json`; const blob = new Blob([json], { type: 'application/json' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; link.click(); } // Function to add the button directly under the video player function addButton() { const controlsContainer = document.querySelector('ytd-player'); if (controlsContainer) { // Create the button const button = document.createElement('button'); button.textContent = 'Extract Chapters & Download JSON'; button.style.position = 'absolute'; button.style.left = '50%'; button.style.transform = 'translateX(-50%)'; button.style.marginTop = '10px'; button.style.padding = '10px'; button.style.backgroundColor = '#FF0000'; button.style.color = '#FFF'; button.style.border = 'none'; button.style.cursor = 'pointer'; button.style.zIndex = 1000; // Add event listener to extract chapters and download JSON on click button.addEventListener('click', () => { const chapters = extractChapters(); if (chapters.length > 0) { downloadJSON(chapters); } else { alert('No chapters found!'); } }); // Append the button below the video controls controlsContainer.appendChild(button); } } // Wait for YouTube page to load and then add the button window.addEventListener('load', () => { setTimeout(addButton, 3000); // Wait a bit for the video page elements to load }); })();