// ==UserScript== // @name 通用视频助手 // @version 1.0 // @description 鼠标滚轮控制视频前进/后退,默认4秒,可自己设定。中键加速+0.1,记忆每个视频的播放时间点,再次播放时自动跳转。设置视频默认播放速度1.0,可以自己设定 // @author 编码助手 // @match *://*/* // @grant none // @run-at document-start // @namespace https://greasyfork.org/users/325179 // @downloadURL none // ==/UserScript== (function() { 'use strict'; const JUMP_STEP = 4; const DEFAULT_SPEED = 1.0; // 1. 样式:提示文字在父级容器内绝对居中 const style = document.createElement('style'); style.innerHTML = ` .v-helper-container { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; pointer-events: none; z-index: 2147483647; } .v-helper-tip { background: rgba(0, 0, 0, 0.85); color: #00ffcc; padding: 15px 32px; border-radius: 12px; font-size: 34px; font-weight: bold; opacity: 0; box-shadow: 0 0 25px rgba(0,0,0,0.7); } .v-animate { animation: v-popup 0.6s ease-out forwards; } @keyframes v-popup { 0% { opacity: 0; transform: scale(0.4); } 15% { opacity: 1; transform: scale(1.1); } 85% { opacity: 1; transform: scale(1.05); } 100% { opacity: 0; transform: scale(1); } } `; document.head.appendChild(style); const containerEl = document.createElement('div'); containerEl.className = 'v-helper-container'; const tipEl = document.createElement('div'); tipEl.className = 'v-helper-tip'; containerEl.appendChild(tipEl); // 获取当前视频的唯一特征 (URL + 时长 + 视频源) function getFingerprint(video) { if (!video || isNaN(video.duration)) return null; const duration = Math.floor(video.duration); // 使用 video.src 是最稳妥的,视频一换,src 必换 const raw = `${window.location.pathname}${duration}${video.src.substring(0, 50)}`; return 'v_id_' + btoa(encodeURIComponent(raw)).substring(0, 40); } // --- 核心逻辑:处理视频状态切换 --- function checkVideoState() { // 使用你提供的 YouTube 专属路径 const v = document.querySelector("#movie_player > div.html5-video-container > video") || document.querySelector('video'); if (!v || isNaN(v.duration)) return; const currentFp = getFingerprint(v); // 如果指纹变了,说明切换了视频(即使在同一个网页内) if (v.dataset.lastFp !== currentFp) { console.log("[编码助手] 检测到视频源切换,正在应用设置..."); v.dataset.lastFp = currentFp; // 1. 恢复记忆进度 const savedTime = localStorage.getItem(currentFp); if (savedTime) { const time = parseFloat(savedTime); if (time > 5 && time < v.duration - 10) { v.currentTime = time; } } // 2. 强制设置倍速与播放 v.playbackRate = DEFAULT_SPEED; v.play().catch(() => { v.muted = true; v.play(); }); // 3. 确保绑定了进度记录器 (仅绑定一次) if (!v.dataset.saveBound) { v.addEventListener('timeupdate', () => { const fp = getFingerprint(v); if (fp && v.currentTime > 5 && v.currentTime < v.duration - 5) { localStorage.setItem(fp, v.currentTime); } }); v.dataset.saveBound = "true"; } } } // --- 视觉提示显示 --- function showTip(v, text) { // 优先挂载到 YouTube 的主播放器容器 const mountPoint = document.querySelector('#movie_player') || v.parentElement; if (!mountPoint) return; if (containerEl.parentNode !== mountPoint) { mountPoint.style.position = mountPoint.style.position || 'relative'; mountPoint.appendChild(containerEl); } tipEl.innerText = text; tipEl.classList.remove('v-animate'); void tipEl.offsetWidth; tipEl.classList.add('v-animate'); } // --- 全局事件拦截 --- const eventHandler = (e) => { const v = document.querySelector("#movie_player > div.html5-video-container > video") || document.querySelector('video'); if (!v) return; // 只要鼠标在播放器容器内 const player = document.querySelector('#movie_player') || v.closest('.html5-video-player') || v; const r = player.getBoundingClientRect(); if (e.clientX >= r.left && e.clientX <= r.right && e.clientY >= r.top && e.clientY <= r.bottom) { if (e.type === 'wheel') { e.preventDefault(); e.stopImmediatePropagation(); const forward = e.deltaY > 0; // 下滚前进 v.currentTime += forward ? JUMP_STEP : -JUMP_STEP; showTip(v, (forward ? '>> ' : '<< ') + JUMP_STEP + 's'); } else if (e.type === 'mousedown' && e.button === 1) { e.preventDefault(); e.stopImmediatePropagation(); v.playbackRate = (parseFloat(v.playbackRate) + 0.1).toFixed(1); showTip(v, `${v.playbackRate}x`); } } }; window.addEventListener('wheel', eventHandler, { passive: false, capture: true }); window.addEventListener('mousedown', eventHandler, { capture: true }); window.addEventListener('auxclick', e => e.button === 1 && e.preventDefault(), { capture: true }); // 轮询检查(关键:每秒检查指纹是否变化) setInterval(checkVideoState, 1000); })();