// ==UserScript== // @name 视频精确控制工具 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 为不方便进行控制的视频添加悬浮在屏幕下方的精确控制工具条 // @author wen2so // @match *://*/* // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/528200/%E8%A7%86%E9%A2%91%E7%B2%BE%E7%A1%AE%E6%8E%A7%E5%88%B6%E5%B7%A5%E5%85%B7.user.js // @updateURL https://update.greasyfork.icu/scripts/528200/%E8%A7%86%E9%A2%91%E7%B2%BE%E7%A1%AE%E6%8E%A7%E5%88%B6%E5%B7%A5%E5%85%B7.meta.js // ==/UserScript== (function () { "use strict"; // 创建悬浮控制面板 const controller = document.createElement("div"); controller.style.cssText = ` position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.2); padding: 10px; border-radius: 25px; display: flex; gap: 10px; z-index: 99999; box-shadow: 0 2px 8px rgba(0,0,0,0.3); backdrop-filter: blur(5px); align-items: center; `; // 创建控制按钮 const createButton = (text, seconds) => { const btn = document.createElement("button"); btn.textContent = text; btn.style.cssText = ` padding: 8px 16px; border: none; border-radius: 15px; background: rgba(100,255,255,0.2); color: white; font-size: 14px; cursor: pointer; transition: all 0.2s; `; btn.addEventListener("click", () => adjustVideoTime(seconds)); btn.addEventListener( "mousedown", () => (btn.style.transform = "scale(0.95)") ); btn.addEventListener("mouseup", () => (btn.style.transform = "scale(1)")); btn.addEventListener( "mouseleave", () => (btn.style.transform = "scale(1)") ); return btn; }; // 创建按钮组容器 const buttonGroupColumn = document.createElement("div"); buttonGroupColumn.style.cssText = ` display: flex; flex-direction: column; gap: 8px; `; // 创建后退按钮行 const backButtonRow = document.createElement("div"); backButtonRow.style.cssText = ` display: flex; gap: 10px; justify-content: flex-end; `; // 创建前进按钮行(放在上方) const forwardButtonRow = document.createElement("div"); forwardButtonRow.style.cssText = ` display: flex; gap: 10px; justify-content: flex-end; `; // 添加按钮到对应容器(注意顺序反转) forwardButtonRow.appendChild(createButton("5s»", 5)); forwardButtonRow.appendChild(createButton("30s»", 30)); forwardButtonRow.appendChild(createButton("1m»", 60)); backButtonRow.appendChild(createButton("«1m", -60)); backButtonRow.appendChild(createButton("«30s", -30)); backButtonRow.appendChild(createButton("«5s", -5)); // 将按钮行添加到列容器(前进在上方) buttonGroupColumn.appendChild(forwardButtonRow); buttonGroupColumn.appendChild(backButtonRow); // 将按钮组添加到控制面板 controller.appendChild(buttonGroupColumn); // 创建工具组容器 const toolGroupColumn = document.createElement("div"); buttonGroupColumn.style.cssText = ` display: flex; flex-direction: column; gap: 8px; `; // 创建倍率行 const speedRow = document.createElement("div"); speedRow.style.cssText = ` display: flex; gap: 10px; justify-content: flex-end; `; // 添加播放速率控制 const speedControl = document.createElement("select"); speedControl.innerHTML = ` `; speedControl.style.cssText = ` background: rgba(0,0,0,0.2); color: white; border: none; border-radius: 15px; padding: 8px 16px; font-size: 14px; cursor: pointer; `; speedControl.onchange = (e) => { document .querySelectorAll("video") .forEach((v) => (v.playbackRate = e.target.value)); }; // 创建精确控制行 const progressRow = document.createElement("div"); progressRow.style.cssText = ` display: flex; gap: 10px; justify-content: flex-end; `; // 添加精确进度控制 const progressContainer = document.createElement("div"); progressContainer.style.cssText = ` display: flex; align-items: center; gap: 10px; `; // 当前时间显示 const currentTimeDisplay = document.createElement("span"); currentTimeDisplay.textContent = "00:00"; currentTimeDisplay.style.cssText = ` color: white; font-size: 14px; `; // 精确时间输入框 const timeInput = document.createElement("input"); timeInput.type = "number"; timeInput.min = 0; timeInput.step = 1; timeInput.style.cssText = ` width: 80px; padding: 8px; border: none; border-radius: 15px; background: rgba(0,0,0,0.2); color: white; font-size: 14px; `; // 创建应用按钮 const applyButton = document.createElement("button"); applyButton.textContent = "应用"; applyButton.style.cssText = ` padding: 8px 16px; border: none; border-radius: 15px; background: rgba(100,255,255,0.2); color: white; font-size: 14px; cursor: pointer; transition: all 0.2s; `; applyButton.addEventListener("click", () => { const time = parseFloat(timeInput.value); if (!isNaN(time)) { document.querySelectorAll("video").forEach((v) => { const validTime = Math.max(0, Math.min(time, v.duration)); v.currentTime = validTime; timeInput.value = validTime; // 更新输入框为实际应用的时间 }); } }); applyButton.addEventListener( "mousedown", () => (applyButton.style.transform = "scale(0.95)") ); applyButton.addEventListener( "mouseup", () => (applyButton.style.transform = "scale(1)") ); applyButton.addEventListener( "mouseleave", () => (applyButton.style.transform = "scale(1)") ); speedRow.appendChild(speedControl); speedRow.appendChild(applyButton); toolGroupColumn.appendChild(speedRow); controller.appendChild(toolGroupColumn); progressRow.appendChild(currentTimeDisplay); progressRow.appendChild(timeInput); toolGroupColumn.appendChild(progressRow); controller.appendChild(toolGroupColumn); // 插入控制面板 document.body.appendChild(controller); // 调整视频时间 function adjustVideoTime(seconds) { const videos = document.querySelectorAll("video"); videos.forEach((video) => { try { const newTime = video.currentTime + seconds; video.currentTime = Math.max(0, Math.min(newTime, video.duration)); } catch (error) { console.log("视频控制错误:", error); } }); } // 更新当前时间和输入框 function updateTimeDisplay() { const video = document.querySelector("video"); if (video) { const currentTime = Math.floor(video.currentTime); const duration = Math.floor(video.duration); currentTimeDisplay.textContent = `${formatTime( currentTime )} / ${formatTime(duration)}`; // 仅在输入框未激活时更新其值 if (document.activeElement !== timeInput) { timeInput.value = currentTime; } } } // 格式化时间(秒 -> 分钟:秒) function formatTime(seconds) { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs < 10 ? "0" : ""}${secs}`; } // 监听视频时间更新 setInterval(updateTimeDisplay, 500); // 动态内容检测(针对SPA) const observer = new MutationObserver(() => { if (!document.body.contains(controller)) { document.body.appendChild(controller); } }); observer.observe(document, { childList: true, subtree: true, }); })();