您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
自动获取播放列表并随机播放视频
// ==UserScript== // @name Bilibili 列表随机播放 // @namespace http://tampermonkey.net/ // @version 0.8 // @description 自动获取播放列表并随机播放视频 // @author 0808 // @match https://www.bilibili.com/list/* // @icon https://www.bilibili.com/favicon.ico // @grant none // @license MIT // ==/UserScript== (function () { "use strict"; /** 配置选项 **/ const CONFIG = { randomButtonClass: "randomBtn", activeClass: "startRandom", scrollDelay: 200, timeoutDelay: 5000, waitTime: 2000, excludeLastX: 0, // 忽略最后的 X 个视频 maxScrollAttempts: 1, // 滚动到顶部或底部后再滚动 X 次 buttonText: "随机", logPrefix: "[列表随机播放]", // 日志前缀 colorEnable: 'rgba(0,174,236, 1)', colorDisable: 'rgba(0,174,236, 0.4)', colorHover: 'rgba(0,174,236, 0.7)', localStorageKey: "randomVideoEnabled", videoSelectorList: [ "ul.list-box > li > a > div.clickitem", ".base-video-sections-v1 .video-section-list .video-episode-card", "#playlist-video-action-list .action-list-inner .action-list-item .title", ], scrollContainerSelector: "#playlist-video-action-list", targetElementSelector: 'div.list-playway-btn.list-tool-btn[title="列表循环"]', }; /** 初始化脚本 **/ const state = { isRandomEnabled: getLocalStorage() === 1, videoList: [], isScrolling: false, // 添加滚动标志位 }; /** 封装 console.log,自动添加前缀 **/ function log(message) { console.log(`${CONFIG.logPrefix} ${message}`); } setTimeout(() => { init(); }, CONFIG.timeoutDelay); /** 主初始化函数 **/ function init() { getVideoList(); createRandomButton(); if (state.isRandomEnabled) startRandomPlayback(); } /** 创建随机播放按钮 **/ function createRandomButton() { // 找到目标元素 const targetElement = document.querySelector(CONFIG.targetElementSelector); if (targetElement) { // 创建按钮 const button = document.createElement("button"); button.textContent = CONFIG.buttonText; button.className = `${CONFIG.randomButtonClass} ${state.isRandomEnabled ? CONFIG.activeClass : ""}`; // 设置按钮样式 button.style.backgroundColor = state.isRandomEnabled ? CONFIG.colorEnable : CONFIG.colorDisable; button.style.transition = 'background-color 0.3s'; button.style.color = '#ffffff'; button.style.fontSize = '15px'; button.style.cursor = 'pointer'; button.style.borderRadius = '10px'; button.style.border = '0px solid #ffffff'; button.style.paddingLeft = '10px'; button.style.paddingRight = '10px'; button.style.marginBottom = '2px'; button.style.marginLeft = '10px'; // 添加左边距,与目标元素保持一定距离 // 添加悬停效果 button.addEventListener("mouseover", function () { button.style.backgroundColor = CONFIG.colorHover; }); button.addEventListener("mouseout", function () { button.style.backgroundColor = state.isRandomEnabled ? CONFIG.colorEnable : CONFIG.colorDisable; }); // 添加点击事件 button.addEventListener("click", toggleRandomPlayback); // 将按钮插入到目标元素后面 targetElement.insertAdjacentElement('afterend', button); } else { log("未找到目标元素,按钮创建失败"); } } /** 切换随机播放状态 **/ function toggleRandomPlayback(event) { event.stopPropagation(); const button = event.target; state.isRandomEnabled = !state.isRandomEnabled; if (state.isRandomEnabled) { log("已开启随机播放"); button.classList.add(CONFIG.activeClass); button.style.backgroundColor = CONFIG.colorEnable; setLocalStorage(1); startRandomPlayback(); // 如果正在滚动,则忽略点击事件 if (!state.isScrolling) { getVideoList(); } } else { log("已关闭随机播放"); button.classList.remove(CONFIG.activeClass); button.style.backgroundColor = CONFIG.colorDisable; setLocalStorage(0); } } /** 开始随机播放功能 **/ function startRandomPlayback() { const videoEl = document.querySelector("div.bpx-player-video-wrap > video"); if (videoEl) { videoEl.addEventListener("ended", handleVideoEnd); } } /** 视频播放结束时的处理 **/ function handleVideoEnd(event) { if (state.isRandomEnabled) { event.stopImmediatePropagation(); playNextVideo(); } } /** 随机播放下一个视频 **/ function playNextVideo() { const availableVideos = state.videoList.slice(0, state.videoList.length - CONFIG.excludeLastX); // 排除最后X个视频 if (availableVideos.length > 0) { const randomIndex = Math.floor(Math.random() * availableVideos.length); const nextVideo = availableVideos[randomIndex]; if (nextVideo) { nextVideo.click(); log(`当前正在随机播放第 ${randomIndex + 1} / ${state.videoList.length - CONFIG.excludeLastX} 个视频: [${nextVideo.title}]`); } } else { log("没有可用的视频进行随机播放"); } } /** 获取视频列表 **/ function getVideoList() { const container = document.querySelector(CONFIG.scrollContainerSelector); if (container) { simulateScroll(container, () => { for (const selector of CONFIG.videoSelectorList) { const videos = Array.from(document.querySelectorAll(selector)); if (videos.length > 0) { state.videoList = videos; log(`视频列表更新成功,读取到 ${state.videoList.length - CONFIG.excludeLastX} 个视频`); return; } } log("获取视频列表失败,请刷新重试"); }); } } /** 模拟滚动 **/ function simulateScroll(container, callback) { // 设置滚动标志位 state.isScrolling = true; log("开始滚动"); let scrollAttemptsTop = 0; // 向上滚动次数计数器 let scrollAttemptsBottom = 0; // 向下滚动次数计数器 function scrollToTop(container, onComplete) { const tolerance = 2; // 容许的误差 const interval = setInterval(() => { const { scrollTop } = container; // 检查是否接近顶部 if (scrollTop <= tolerance) { log("已到达顶部"); clearInterval(interval); // 如果未达到最大滚动次数,继续滚动 if (scrollAttemptsTop < CONFIG.maxScrollAttempts) { scrollAttemptsTop++; log(`滚动到顶部,正在进行第 ${scrollAttemptsTop} 次滚动`); setTimeout(() => scrollToTop(container, onComplete), CONFIG.waitTime); } else { // 达到最大滚动次数,结束滚动 onComplete(); } } else { // 动态调整滚动步长,避免跳动 container.scrollTop -= container.clientHeight / 4 * 3; } }, CONFIG.scrollDelay); } function scrollToBottom(container, onComplete) { const tolerance = 2; // 容许的误差 const interval = setInterval(() => { const { scrollTop, scrollHeight, clientHeight } = container; const distanceToBottom = scrollHeight - (scrollTop + clientHeight); // 检查是否接近底部 if (distanceToBottom <= tolerance) { log("已到达底部"); clearInterval(interval); // 如果未达到最大滚动次数,继续滚动 if (scrollAttemptsBottom < CONFIG.maxScrollAttempts) { scrollAttemptsBottom++; log(`滚动到底部,正在进行第 ${scrollAttemptsBottom} 次滚动`); setTimeout(() => scrollToBottom(container, onComplete), CONFIG.waitTime); } else { // 达到最大滚动次数,结束滚动 onComplete(); } } else { // 动态调整滚动步长,避免跳动 container.scrollTop += clientHeight / 4 * 3; } }, CONFIG.scrollDelay); } // 先滚动到顶部 scrollToTop(container, () => { // 滚动到顶部完成后,再滚动到底部 setTimeout(() => { scrollToBottom(container, () => { // 滚动结束后清除标志位 state.isScrolling = false; log("滚动结束"); callback(); }); }, CONFIG.waitTime); }); } /** 本地存储操作 **/ function setLocalStorage(value) { localStorage.setItem(CONFIG.localStorageKey, value); } function getLocalStorage() { return parseInt(localStorage.getItem(CONFIG.localStorageKey), 10) || 0; } })();