您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
count how many times a video recommended to you in Youtube
// ==UserScript== // @name videoTracing // @namespace http://tampermonkey.net/ // @version 2025-04-10.2 // @description count how many times a video recommended to you in Youtube // @author gn_gf // @match https://www.youtube.com/ // @license MIT // ==/UserScript== (function () { 'use strict'; const DEBUG = true; const CONTENT_ID = 'contents'; const videoIdSet = new Set(); const videoStateObject = function (id, lastEditTime, times) { this.id = id; this.et = lastEditTime; this.c = times; }; const SCRIPT_NAME = GM_info.script.name; const debegLog = (content) => { if (DEBUG) { console.log(`${SCRIPT_NAME}:[DEBUG]:${content}`); } }; const getVideoId = (path) => path.split('?')[1].split('&')[0].split('=')[1]; const getAllVideoWhenInit = () => { let nodeListOf = document.querySelectorAll('#video-title-link'); if (nodeListOf.length === 0) { return []; } return Array.from(nodeListOf); }; const getVideoState = () => { let videoState = localStorage.getItem('videoState'); if (videoState === null) { return []; } return JSON.parse(videoState); }; const updateVideoState = (videoId, videoState) => { let videoStateItem = videoState.find((item) => item.id === videoId); if (videoStateItem === undefined) { videoStateItem = new videoStateObject(videoId, new Date().getTime(), 1); videoState.push(videoStateItem); } else { if (!videoIdSet.has(videoId)) { videoStateItem.c += 1; videoStateItem.et = new Date().getTime(); } } videoIdSet.add(videoId); return videoStateItem; }; const drawDot = (videoElement, videoState) => { let parent = videoElement.parentNode.parentNode.parentNode.parentNode; if (!parent.id || parent.id !== 'dismissible') { debegLog(`FROM DrawDow:${videoState.id}:parent id not match:${parent}`); return; } if (parent.firstChild.id && parent.firstChild.id === 'mydot') { parent.firstChild.innerText = videoState.c; return; } let element = document.createElement('span'); element.id = 'mydot'; element.style.position = 'absolute'; element.style.backgroundColor = '#f00a'; element.style.color = 'white'; element.style.borderRadius = '20px'; element.style.display = 'inline-block'; element.style.fontSize = '3em'; element.style.zIndex = '100'; element.style.margin = '10px'; element.style.padding = '10px'; element.style.textAlign = 'center'; element.innerText = videoState.c; parent.insertBefore(element, parent.firstChild); }; const saveVideoState = (videoState) => { localStorage.setItem('videoState', JSON.stringify(videoState)); }; const VIDEO_STATE = getVideoState(); //add a MutationObserver to get new videoIds when youtube page add new video when scroll const videoContentObserve = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes) { mutation.addedNodes.forEach((node) => { if (node.querySelectorAll) { const newVideos = node.querySelectorAll('#video-title-link'); newVideos.forEach(video => { const videoId = getVideoId(video.getAttribute('href')); let state = updateVideoState(videoId, VIDEO_STATE); drawDot(video, state); }); } }); } }); if (VIDEO_STATE.length >= 1000) { // remove old 100 items sort by lastEditTime VIDEO_STATE.sort((a, b) => a.lastEditTime - b.lastEditTime).splice(0, VIDEO_STATE.length - 1000 + 200); } saveVideoState(VIDEO_STATE); }); let bodyObserve = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList' && mutation.addedNodes) { mutation.addedNodes.forEach((node) => { node.id && debegLog(node.id); if (node.id && node.id === CONTENT_ID) { videoContentObserve.observe(node, {subtree: true, childList: true}); bodyObserve.disconnect(); return; } }); } }); }); //run script when all page loaded window.addEventListener('load', () => { //start the MutationObserver only for video container const videoContainer = document.querySelector('#contents'); if (!videoContainer) { bodyObserve.observe(document.body, {childList: true, subtree: true}); } else { videoContentObserve.observe(videoContainer, {childList: true, subtree: true}); bodyObserve = null; } let initVideos = getAllVideoWhenInit(); initVideos.forEach((videoElement) => { let id = getVideoId(videoElement.getAttribute('href')); let state = updateVideoState(id, VIDEO_STATE); drawDot(videoElement, state); }); saveVideoState(VIDEO_STATE); }); })();