Greasy Fork

Greasy Fork is available in English.

B站视频合集随机播放

添加这个脚本后会在视频合集中添加一个随机播放按钮,通过点击按钮进行开关。(启用此脚本会使自动连播关闭)

// ==UserScript==
// @name         B站视频合集随机播放
// @namespace    http://greasyfork.icu
// @version      1.0
// @description  添加这个脚本后会在视频合集中添加一个随机播放按钮,通过点击按钮进行开关。(启用此脚本会使自动连播关闭)
// @author       NineSummer
// @match        https://www.bilibili.com/video/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {

    const STATE_KEY = 'bili_random_play_enabled';

    function log(...args) {
        console.log('[随机播放]', ...args);
    }

    function getState() {
        return localStorage.getItem(STATE_KEY) === 'true';
    }

    function setState(val) {
        localStorage.setItem(STATE_KEY, val ? 'true' : 'false');
    }

    let observerStarted = false;

    function insertToggleButton() {
        const existing = document.getElementById('randomPlayToggle');
        if (existing) return;

        const ref = document.querySelector('.header-bottom');
        if (!ref) return;

        const btn = document.createElement('button');
        btn.id = 'randomPlayToggle';
        btn.style.cssText = `
      margin-top: 10px;
      margin-left: 10px;
      padding: 4px 8px;
      font-size: 14px;
      background-color: #00a1d6;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    `;

      function updateBtnText() {
          const current = getState();
          btn.textContent = `随机播放:${current ? '开启' : '关闭'}`;
      }

      btn.onclick = () => {
          const newState = !getState();
          setState(newState);
          updateBtnText();
          if (newState) {
              observeVideoEnded();
          }
      };

      updateBtnText();
      ref.parentElement.appendChild(btn);
      log('按钮插入完成');

      if (getState()) {
          observeVideoEnded();
      }
  }

    function getAllBVLinks() {
        const items = document.querySelectorAll('.video-pod__list .pod-item');
        return Array.from(items)
            .map(el => el.getAttribute('data-key'))
            .filter(bv => bv && bv.startsWith('BV'));
    }

    function getCurrentBV() {
        const match = location.pathname.match(/\/video\/(BV\w+)/);
        return match ? match[1] : null;
    }

    function jumpToRandomBV() {
        const currentBV = getCurrentBV();
        const all = getAllBVLinks();
        const others = all.filter(bv => bv !== currentBV);
        if (others.length === 0) {
            log('没有其他分集可跳转');
            return;
        }
        const target = others[Math.floor(Math.random() * others.length)];
        const url = `https://www.bilibili.com/video/${target}`;
        log('跳转到:', url);
        location.href = url;
    }

    function observeVideoEnded() {
        if (observerStarted) return;
        observerStarted = true;

        const check = setInterval(() => {
            const video = document.querySelector('video');
            if (video) {
                clearInterval(check);
                video.addEventListener('ended', () => {
                    if (getState()) {
                        log('视频播放完,开始跳转');
                        jumpToRandomBV();
                    }
                });
                log('已监听 video 播放完毕');
            }
        }, 1000);
    }

    function init() {
        observerStarted = false;
        const interval = setInterval(() => {
            const ref = document.querySelector('.description.van-popover__reference');
            const video = document.querySelector('video');
            const pod = document.querySelector('.video-pod__list .pod-item');
            if (ref && video && pod) {
                insertToggleButton();
                clearInterval(interval);
            }
        }, 1000);
    }

    function hookPushState() {
        const originalPushState = history.pushState;
        history.pushState = function () {
            originalPushState.apply(this, arguments);
            setTimeout(init, 1000);
        };
        window.addEventListener('popstate', () => {
            setTimeout(init, 1000);
        });
    }

    hookPushState();
    window.addEventListener('load', init);
})();