您需要先安装一个扩展,例如 篡改猴、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);
- });
- })();