BiliBackToBeginning

打开视频时默认回到视频开头处

目前为 2025-04-23 提交的版本。查看 最新版本

// ==UserScript==
// @name         BiliBackToBeginning
// @namespace    https://github.com/ImQQiaoO/BiliBackToBeginning
// @version      v0.1.1
// @description  打开视频时默认回到视频开头处
// @author       ImQQiaoO
// @match        *://*.bilibili.com/*
// @exclude      *://message.bilibili.com/pages/nav/header_sync
// @exclude      *://message.bilibili.com/pages/nav/index_new_pc_sync
// @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/p/html/*
// @exclude      *://live.bilibili.com/live-room-play-game-together
// @exclude      *://www.bilibili.com/blackboard/comment-detail.html*
// @exclude      *://www.bilibili.com/blackboard/newplayer.html*
// @license      MIT
// @grant        none // Explicitly state no special GM functions needed if true
// ==/UserScript==

(function() {
    'use strict';

    // 设置和获取 localStorage
    const STORAGE_KEY = "reset_bili_video_enabled";
    function setEnabled(flag) {
        localStorage.setItem(STORAGE_KEY, flag ? "1" : "0");
    }
    function getEnabled() {
        // Default to enabled if not set? Or default to disabled? Let's default to disabled.
        return localStorage.getItem(STORAGE_KEY) === "1";
    }

    // 创建设置面板 (代码不变,为简洁省略)
    function createSettingsPanel() {
        // ... (same code as before) ...
        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;}
            #biliResetPanel label { vertical-align: middle; cursor: pointer; }
            #biliResetPanelClose { cursor:pointer;color: #f66; float:right; font-size: 18px; margin-left: 10px; 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; /* Prevent text selection */
            }
        `;
        const styleEl = document.createElement("style");
        styleEl.textContent = style;
        document.head.appendChild(styleEl);

        // 面板内容
        const panel = document.createElement("div");
        panel.id = "biliResetPanel";
        panel.innerHTML = `
            <span id="biliResetPanelClose" title="关闭设置面板">&times;</span>
            <label>
                <input type="checkbox" id="biliResetSwitch">
                启用自动重置进度到0秒
            </label>
        `;
        document.body.appendChild(panel);

        // 显示/隐藏按钮
        const btn = document.createElement("div");
        btn.id = "biliResetPanelBtn";
        btn.title = "打开【重置到0秒】设置";
        btn.textContent = "⚙";
        document.body.appendChild(btn);

        const switchCheckbox = document.getElementById('biliResetSwitch');

        // Set initial state and event listener
        switchCheckbox.checked = getEnabled();
        switchCheckbox.onchange = (e) => {
            setEnabled(e.target.checked);
             // Optional: Immediately try to reset if enabled now and a video is present/ready
             if (e.target.checked) {
                 checkBilibiliVideoResume();
             }
        };

        btn.onclick = () => { panel.style.display = panel.style.display === 'block' ? 'none' : 'block'; } // Toggle display
        document.getElementById("biliResetPanelClose").onclick = () => { panel.style.display = "none"; }

        // Hide panel initially if setting is off, or show button if setting is on? Or always show button? Let's always show button.
        // Panel starts hidden via CSS/JS anyway.
    }

    // --- MODIFIED FUNCTION ---
    function checkBilibiliVideoResume() {
        if (!getEnabled()) {
            // console.log("[B站重置进度脚本] 功能未启用, 跳过.");
            return;
        }

        // Might need a more specific selector if multiple videos exist,
        // e.g., '#bofqi video', '.bpx-player-video-area video'
        const video = document.querySelector("video");

        if (video) {
            console.log("[B站重置进度脚本] 发现视频元素.");

            const resetAndPlay = () => {
                // Double check in case the user disabled it between finding the video and the event firing
                 if (!getEnabled()) {
                     console.log("[B站重置进度脚本] 功能在回调时被禁用, 跳过.");
                     return;
                 }
                // Only reset if not already at the beginning (or very close to it)
                if (video.currentTime > 0.1) { // Use a small threshold
                    console.log(`[B站重置进度脚本] 视频准备就绪 (readyState: ${video.readyState}). 重置 currentTime 从 ${video.currentTime} 到 0.`);
                    video.currentTime = 0;
                    // Attempt to play, ignore errors (e.g., browser blocked autoplay)
                    video.play().catch(error => {
                        console.warn("[B站重置进度脚本] 自动播放可能被阻止:", error.message);
                    });
                } else {
                    console.log(`[B站重置进度脚本] 视频已在开头 (currentTime: ${video.currentTime}), 无需重置.`);
                    // If it's paused at the beginning, maybe still try to play?
                    if (video.paused) {
                         video.play().catch(error => {
                            console.warn("[B站重置进度脚本] 尝试播放开头时, 自动播放可能被阻止:", error.message);
                        });
                    }
                }
            };

            // readyState >= 1 means HAVE_METADATA - duration and dimensions are known.
            // It's safe to set currentTime now.
            if (video.readyState >= 1) {
                console.log("[B站重置进度脚本] 视频元数据已加载 (readyState >= 1). 立即尝试重置.");
                resetAndPlay();
            } else {
                console.log("[B站重置进度脚本] 视频元数据尚未加载. 添加 'loadedmetadata' 事件监听器.");
                // Use { once: true } so the listener automatically removes itself after firing once.
                video.addEventListener('loadedmetadata', resetAndPlay, { once: true });
            }
        } else {
            // Video element might not be in the DOM yet, especially on fast navigation.
            // The observeVideoLoad should catch it when it appears.
            console.log("[B站重置进度脚本] 未找到视频元素.");
        }
    }

    // --- INITIALIZATION ---
    createSettingsPanel();

    // Observe URL changes for SPA navigation (B站)
    function observeVideoLoad() {
        let lastHref = location.href; // Initialize with current href
        // Use MutationObserver for better performance than setInterval if possible,
        // but setInterval is simpler and works reliably for URL checks.
        setInterval(() => {
            if (location.href !== lastHref) {
                console.log(`[B站重置进度脚本] URL 变化: ${lastHref} -> ${location.href}`);
                lastHref = location.href;
                // No need for setTimeout here anymore, checkBilibiliVideoResume handles waiting.
                // Give the DOM a *very* brief moment to update after URL change before checking
                queueMicrotask(() => {
                    checkBilibiliVideoResume();
                });
                 // Or just call directly: checkBilibiliVideoResume();
                 // queueMicrotask is slightly safer to ensure checks run after potential DOM updates triggered by the URL change.
            }
        }, 500); // Check URL every 500ms
    }

    observeVideoLoad();

    // Initial check when the script first runs on a page
    // Use requestAnimationFrame or a small timeout to ensure the initial DOM is somewhat ready
    // requestAnimationFrame waits for the next repaint, usually a good time.
    requestAnimationFrame(() => {
         console.log("[B站重置进度脚本] 初始页面加载, 检查视频.");
         checkBilibiliVideoResume();
    });
     // Alternatively, a slightly longer initial timeout might still be okay now:
     // setTimeout(checkBilibiliVideoResume, 200);

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址