您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a note-taking feature on YouTube videos, storing notes per video using localStorage.
当前为
// ==UserScript== // @name YouTube Video Notes // @namespace http://tampermonkey.net/ // @version 1.8 // @description Adds a note-taking feature on YouTube videos, storing notes per video using localStorage. // @author You // @match https://www.youtube.com/* // @grant none // @run-at document-idle // ==/UserScript== (function () { 'use strict'; let lastVideoId = null; // Track last video to detect changes function createButton(id, text, displayStyle, clickHandler, customStyles = '') { const button = document.createElement('button'); button.id = id; button.innerText = text; button.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--filled yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-s'; button.style.cssText = `margin-top: 10px; display: ${displayStyle}; ${customStyles}`; button.addEventListener('click', clickHandler); return button; } function createNoteUI(videoId, existingNote) { const container = document.createElement('div'); container.className = 'yt-note-container item style-scope ytd-watch-metadata'; container.style.cssText = ` background: var(--yt-spec-badge-chip-background); width: 100%; border-radius: 12px; font-size: 14px; padding: 8px; box-sizing: border-box; `; const noteArea = document.createElement('textarea'); noteArea.id = `yt-note-textarea-${videoId}`; noteArea.style.cssText = ` width: 100%; height: 80px; font-family: inherit; resize: vertical; display: ${existingNote ? 'block' : 'none'}; box-sizing: border-box; padding: 4px; color: #fff; background: transparent; border-radius: 4px; border: 1px solid white; `; noteArea.value = existingNote || ''; noteArea.disabled = !existingNote; // Disable if no note exists const button = createButton(`yt-note-button-${videoId}`, existingNote ? 'Edit Note' : 'Write Note', 'inline-block', () => { noteArea.style.display = 'block'; noteArea.disabled = false; saveButton.style.display = 'inline-block'; deleteButton.style.display = 'inline-block'; button.style.display = 'none'; }); const saveButton = createButton(`yt-note-save-${videoId}`, 'Save', existingNote ? 'none' : 'inline-block', () => { const noteText = noteArea.value.trim(); if (noteText) { localStorage.setItem(`yt-note-${videoId}`, noteText); button.innerText = 'Edit Note'; button.style.display = 'inline-block'; saveButton.style.display = 'none'; deleteButton.style.display = 'inline-block'; noteArea.disabled = true; } }, 'margin-left: 6px;'); const deleteButton = createButton(`yt-note-delete-${videoId}`, 'Delete Note', existingNote ? 'inline-block' : 'none', () => { localStorage.removeItem(`yt-note-${videoId}`); // Delete note noteArea.value = ''; button.innerText = 'Write Note'; button.style.display = 'inline-block'; saveButton.style.display = 'none'; deleteButton.style.display = 'none'; noteArea.disabled = true; }, 'margin-left: 6px;'); container.appendChild(noteArea); container.appendChild(button); container.appendChild(saveButton); container.appendChild(deleteButton); return container; } function insertNoteUI() { const videoId = new URL(window.location.href).searchParams.get('v'); if (!videoId || videoId === lastVideoId) return; // Prevent duplicate insertion lastVideoId = videoId; // Update last video ID const existingNote = localStorage.getItem(`yt-note-${videoId}`) || ''; const commentSection = document.getElementById('comments'); if (!commentSection) { setTimeout(insertNoteUI, 2000); return; } // Remove old note UI before inserting a new one const oldNote = document.querySelector('.yt-note-container'); if (oldNote) oldNote.remove(); const noteUI = createNoteUI(videoId, existingNote); commentSection.parentNode.insertBefore(noteUI, commentSection); } function observeNavigationChanges() { // YouTube fires "yt-navigate-finish" when navigation happens within SPA document.addEventListener('yt-navigate-finish', () => { setTimeout(insertNoteUI, 2000); }); // Also check for URL changes manually (backup method) let lastUrl = location.href; setInterval(() => { if (location.href !== lastUrl) { lastUrl = location.href; insertNoteUI(); } }, 1000); } // Run script on initial load and handle SPA navigation window.addEventListener('load', () => { setTimeout(() => { insertNoteUI(); observeNavigationChanges(); }, 2000); }); })();