您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tracks YouTube session time, video watch time, daily/monthly stats, with export/import support and toggle UI.
当前为
// ==UserScript== // @name YouTube Watch Tracker v1.3 // @namespace http://tampermonkey.net/ // @version 1.3 // @description Tracks YouTube session time, video watch time, daily/monthly stats, with export/import support and toggle UI. // @author Void // @match *://*.youtube.com/* // @grant none // @license CC-BY-ND-4.0 // ==/UserScript== (function () { 'use strict'; const STORAGE_KEY = 'yt_watch_tracker_data'; let data = {}; let sessionTime = 0; let videoTime = 0; let videoTimer = null; let sessionTimer = null; let currentDate = new Date().toISOString().slice(0, 10); let overlay, toggleBtn, hidden = false; function load() { try { const stored = localStorage.getItem(STORAGE_KEY); if (stored) data = JSON.parse(stored); } catch { data = {}; } } function save() { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } function ensureDateStats() { if (!data[currentDate]) { data[currentDate] = { watched: 0, wasted: 0, session: 0 }; } } function fmtTime(t) { const h = Math.floor(t / 3600); const m = Math.floor((t % 3600) / 60); const s = t % 60; return `${h}h ${String(m).padStart(2, '0')}m ${String(s).padStart(2, '0')}s`; } function updateOverlay() { ensureDateStats(); const daily = data[currentDate]; const month = currentDate.slice(0, 7); let monthly = { watched: 0, wasted: 0, session: 0 }; for (const [date, stats] of Object.entries(data)) { if (date.startsWith(month)) { monthly.watched += stats.watched; monthly.wasted += stats.wasted; monthly.session += stats.session; } } overlay.querySelector('.ytwt-text').textContent = `📅 Today: ${daily.watched} videos | 🕒 Wasted: ${fmtTime(daily.wasted)} | ⌛ Session: ${fmtTime(sessionTime)}\n` + `📆 This Month: ${monthly.watched} vids | 🕒 Wasted: ${fmtTime(monthly.wasted)}`; } function createOverlay() { overlay = document.createElement('div'); overlay.style = ` position: fixed; top: 100px; right: 0; background: rgba(0,0,0,0.85); color: #fff; padding: 10px; border-radius: 8px 0 0 8px; font-size: 13px; font-family: 'Segoe UI'; font-weight: 600; z-index: 99999; white-space: pre; user-select: none; box-shadow: 0 0 8px rgba(0,0,0,0.7); width: 280px; transition: right 0.3s ease; `; const content = document.createElement('div'); content.className = 'ytwt-text'; overlay.appendChild(content); const btnContainer = document.createElement('div'); btnContainer.style = 'margin-top: 6px; display: flex; gap: 6px;'; const exportBtn = document.createElement('button'); exportBtn.textContent = 'Export'; exportBtn.style = btnStyle(); exportBtn.onclick = () => { navigator.clipboard.writeText(JSON.stringify(data, null, 2)); alert('Data copied to clipboard.'); }; const importBtn = document.createElement('button'); importBtn.textContent = 'Import'; importBtn.style = btnStyle(); importBtn.onclick = () => { const json = prompt('Paste data to import:'); try { const parsed = JSON.parse(json); if (typeof parsed === 'object') { data = parsed; save(); updateOverlay(); alert('Import successful.'); } else throw 0; } catch { alert('Invalid JSON.'); } }; btnContainer.appendChild(exportBtn); btnContainer.appendChild(importBtn); overlay.appendChild(btnContainer); document.body.appendChild(overlay); // Create toggle arrow toggleBtn = document.createElement('div'); toggleBtn.textContent = '←'; toggleBtn.style = ` position: fixed; top: 120px; right: 280px; width: 20px; height: 40px; background: #111; color: #fff; display: flex; align-items: center; justify-content: center; border-radius: 6px 0 0 6px; cursor: pointer; font-size: 18px; z-index: 100000; transition: right 0.3s ease, transform 0.3s ease; `; toggleBtn.onclick = toggleOverlay; document.body.appendChild(toggleBtn); updateOverlay(); } function btnStyle() { return ` background: #333; color: #eee; border: 1px solid #555; border-radius: 4px; padding: 2px 8px; font-size: 12px; cursor: pointer; `; } function toggleOverlay() { hidden = !hidden; if (hidden) { overlay.style.right = '-300px'; toggleBtn.style.right = '0'; toggleBtn.textContent = '→'; } else { overlay.style.right = '0'; toggleBtn.style.right = '280px'; toggleBtn.textContent = '←'; } } function observeVideo() { new MutationObserver(() => { const video = document.querySelector('video'); if (!video || video === window.__lastYTVideo) return; window.__lastYTVideo = video; ensureDateStats(); data[currentDate].watched++; save(); updateOverlay(); clearInterval(videoTimer); videoTimer = setInterval(() => { if (video.readyState >= 2 && !video.paused && !video.ended) { data[currentDate].wasted++; videoTime++; if (videoTime % 5 === 0) save(); updateOverlay(); } }, 1000); }).observe(document.body, { childList: true, subtree: true }); } function startSessionTimer() { sessionTimer = setInterval(() => { sessionTime++; ensureDateStats(); data[currentDate].session++; if (sessionTime % 10 === 0) save(); updateOverlay(); }, 1000); } function init() { load(); ensureDateStats(); createOverlay(); observeVideo(); startSessionTimer(); } window.addEventListener('load', () => setTimeout(init, 500)); })();