您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a note-taking feature on YouTube videos, storing notes per video using localStorage and Tampermonkey storage.
- // ==UserScript==
- // @name YouTube Video Notes
- // @namespace http://tampermonkey.net/
- // @version 1.9
- // @description Adds a note-taking feature on YouTube videos, storing notes per video using localStorage and Tampermonkey storage.
- // @author You
- // @match https://www.youtube.com/*
- // @grant GM_setValue
- // @grant GM_deleteValue
- // @run-at document-idle
- // ==/UserScript==
- (function () {
- 'use strict';
- let lastVideoId = null;
- function saveNote(id, note) {
- localStorage.setItem(id, note);
- GM_setValue(id, note);
- }
- function deleteNote(id) {
- localStorage.removeItem(id);
- GM_deleteValue(id);
- }
- 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;
- 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) {
- saveNote(`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', () => {
- deleteNote(`yt-note-${videoId}`);
- 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;');
- const fetchAllNotesButton = createButton('yt-fetch-notes', 'Show All Notes', 'block', fetchAllNotes, 'margin-top: 10px; width: 100%;');
- container.appendChild(noteArea);
- container.appendChild(button);
- container.appendChild(saveButton);
- container.appendChild(deleteButton);
- container.appendChild(fetchAllNotesButton);
- return container;
- }
- function insertNoteUI() {
- const videoId = new URL(window.location.href).searchParams.get('v');
- if (!videoId || videoId === lastVideoId) return;
- lastVideoId = videoId;
- const existingNote = localStorage.getItem(`yt-note-${videoId}`) || '';
- const commentSection = document.getElementById('comments');
- if (!commentSection) {
- setTimeout(insertNoteUI, 2000);
- return;
- }
- const oldNote = document.querySelector('.yt-note-container');
- if (oldNote) oldNote.remove();
- const noteUI = createNoteUI(videoId, existingNote);
- commentSection.parentNode.insertBefore(noteUI, commentSection);
- }
- function fetchAllNotes() {
- const allNotes = {};
- for (let i = 0; i < localStorage.length; i++) {
- const key = localStorage.key(i);
- if (key.startsWith('yt-note-')) {
- allNotes[key.replace('yt-note-', '')] = localStorage.getItem(key);
- }
- }
- console.log(JSON.stringify(allNotes, null, 2));
- }
- function observeNavigationChanges() {
- document.addEventListener('yt-navigate-finish', () => {
- setTimeout(insertNoteUI, 2000);
- });
- let lastUrl = location.href;
- setInterval(() => {
- if (location.href !== lastUrl) {
- lastUrl = location.href;
- insertNoteUI();
- }
- }, 1000);
- }
- window.addEventListener('load', () => {
- setTimeout(() => {
- insertNoteUI();
- observeNavigationChanges();
- }, 2000);
- });
- })();