您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
打开视频时通过监听事件更精确地回到视频开头处
当前为
// ==UserScript== // @name BiliBackToBeginning (Improved) // @namespace https://github.com/ImQQiaoO/BiliBackToBeginning // @version v0.1.0 // @description 打开视频时通过监听事件更精确地回到视频开头处 // @author ImQQiaoO // @match *://*.bilibili.com/video/* // @match *://*.bilibili.com/list/* // @match *://*.bilibili.com/watchlater/* // @match *://*.bilibili.com/medialist/play/* // @match *://*.bilibili.com/bangumi/play/* // @exclude *://message.bilibili.com/* // @exclude *://data.bilibili.com/* // @exclude *://cm.bilibili.com/* // @exclude *://link.bilibili.com/* // @exclude *://passport.bilibili.com/* // @exclude *://api.bilibili.com/* // @exclude *://api.*.bilibili.com/* // @exclude *://*.chat.bilibili.com/* // @exclude *://member.bilibili.com/* // @exclude *://www.bilibili.com/tensou/* // @exclude *://www.bilibili.com/correspond/* // @exclude *://live.bilibili.com/* // 排除所有直播页面 // @exclude *://www.bilibili.com/blackboard/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // 设置和获取 localStorage const STORAGE_KEY = "reset_bili_video_enabled"; function setEnabled(flag) { localStorage.setItem(STORAGE_KEY, flag ? "1" : "0"); console.log(`[B站重置进度脚本] 设置已 ${flag ? '启用' : '禁用'}`); } function getEnabled() { // 默认启用,如果localStorage没有记录的话 const storedValue = localStorage.getItem(STORAGE_KEY); return storedValue === null || storedValue === "1"; } // 创建设置面板 function createSettingsPanel() { const style = ` #biliResetPanel { position: fixed; bottom: 30px; right: 30px; z-index: 99999; background: #fff; color: #333; border: 1px solid #bbb; border-radius: 8px; box-shadow: 0 6px 16px rgba(0,0,0,.1); padding: 18px 26px 18px 18px; font-size: 16px; display: none; } #biliResetPanel input[type=checkbox] { transform: scale(1.3); margin-right:8px; vertical-align: middle;} #biliResetPanelClose { cursor:pointer;color: #f66; float:right; font-size: 18px; line-height: 1;} #biliResetPanelBtn { position: fixed; bottom: 30px; right: 30px; z-index: 99998; background: #ffe2a0; color: #333; border: 1px solid #bbb; border-radius: 50%; width: 42px; height: 42px; text-align:center; line-height: 42px; font-size: 24px; cursor: pointer; box-shadow: 0 3px 12px rgba(0,0,0,.08); user-select: none; /* 防止意外选中文本 */ } `; // Ensure head exists before appending style let head = document.head; if (!head) { head = document.createElement('head'); document.documentElement.insertBefore(head, document.body); // Basic fallback } const styleEl = document.createElement("style"); styleEl.textContent = style; head.appendChild(styleEl); // Ensure body exists before appending panel/button const addPanelElements = () => { if (!document.body) { setTimeout(addPanelElements, 50); // Wait if body not ready return; } // Panel content const panel = document.createElement("div"); panel.id = "biliResetPanel"; panel.innerHTML = ` <span id="biliResetPanelClose" title="关闭设置面板">×</span> <label> <input type="checkbox" id="biliResetSwitch"> 启用自动重置进度到0秒 </label> `; document.body.appendChild(panel); // Show/hide button const btn = document.createElement("div"); btn.id = "biliResetPanelBtn"; btn.title = "打开【重置到0秒】设置"; btn.textContent = "↩₀"; // Use a more symbolic icon document.body.appendChild(btn); const switchCheckbox = document.getElementById('biliResetSwitch'); const closeButton = document.getElementById("biliResetPanelClose"); btn.onclick = (e) => { e.stopPropagation(); // Prevent event bubbling panel.style.display = panel.style.display === "block" ? "none" : "block"; } closeButton.onclick = (e) => { e.stopPropagation(); panel.style.display = "none"; } // Close panel when clicking outside document.addEventListener('click', (e) => { // Check if panel and button exist and panel is visible if (panel && btn && panel.style.display === 'block' && !panel.contains(e.target) && !btn.contains(e.target)) { panel.style.display = 'none'; } }); if (switchCheckbox) { // Check if element exists before accessing properties switchCheckbox.checked = getEnabled(); switchCheckbox.onchange = (e) => { setEnabled(e.target.checked); }; } else { console.error("[B站重置进度脚本] Checkbox 'biliResetSwitch' not found."); } }; addPanelElements(); // Call function to add elements } // Core function: add reset logic to video elements function setupVideoReset(videoElement) { if (!videoElement) return; // Guard clause // Define event handler function const onLoadedMetadata = () => { if (getEnabled()) { // Check if current time is not 0 to avoid unnecessary operations if (videoElement.currentTime > 0) { console.log(`[B站重置进度脚本] Detected video metadata loaded (Current time: ${videoElement.currentTime}), preparing to reset to 0s.`); videoElement.currentTime = 0; console.log("[B站重置进度脚本] Video progress reset to 0s."); // Note: No need to manually play() or pause(), the Bilibili player will handle playback state } else { // console.log("[B站重置进度脚本] Video metadata loaded, current time is already 0, no reset needed."); } } }; // Add a one-time 'loadedmetadata' event listener videoElement.addEventListener('loadedmetadata', onLoadedMetadata, { once: true }); console.log("[B站重置进度脚本] Added 'loadedmetadata' listener to video element."); // (Optional) Add error handling or listener removal logic just in case videoElement.addEventListener('error', () => { console.warn("[B站重置进度脚本] Video loading error, removing listener."); // Attempt to remove the listener if it hasn't fired yet try { videoElement.removeEventListener('loadedmetadata', onLoadedMetadata); } catch(e) {/* ignore */} }, { once: true }); } // Use MutationObserver to listen for DOM changes and catch dynamically loaded <video> elements function observeDOMForVideo() { const observer = new MutationObserver((mutationsList) => { for (const mutation of mutationsList) { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { // Ensure it's an element node if (node.nodeType === Node.ELEMENT_NODE) { let videoElement = null; // Check if the added node itself is a video if (node.tagName === 'VIDEO') { console.log("[B站重置进度脚本] Detected <video> element added directly to DOM."); videoElement = node; } // Check if the added node contains a video element (more common) else if (typeof node.querySelector === 'function') { // Check if querySelector exists videoElement = node.querySelector('video'); if (videoElement) { console.log("[B站重置进度脚本] Detected <video> element added within a child node."); } } if (videoElement) { setupVideoReset(videoElement); // Consider disconnecting observer if only one main video expected, for performance. // observer.disconnect(); } } }); } } }); // Configuration for the observer: watch for child node additions/removals in the entire subtree const config = { childList: true, subtree: true }; // Start observing the target node (document.body is usually sufficient) // Ensure body exists before observing const startObserving = () => { if (document.body) { observer.observe(document.body, config); console.log("[B站重置进度脚本] Started listening for DOM changes to find <video> elements."); // Also, check if a video element already exists on page load (for non-dynamic loading) const existingVideo = document.querySelector("video"); if (existingVideo) { console.log("[B站重置进度脚本] Detected existing <video> element on page load."); setupVideoReset(existingVideo); } else { // console.log("[B站重置进度脚本] No <video> element found on page load, waiting for DOM changes..."); } } else { setTimeout(startObserving, 50); // Wait if body not ready } }; startObserving(); // Return the observer instance in case you need to disconnect it later return observer; } // Initialization try { createSettingsPanel(); // Create settings UI observeDOMForVideo(); // Start listening for video elements } catch (error) { console.error("[B站重置进度脚本] Error during initialization:", error); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址