您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
稳定可靠的AB点循环工具,适配最新B站页面结构
当前为
// ==UserScript== // @name B站循环助手-精简版 // @namespace bilibili-replayer // @version 1.17 // @description 稳定可靠的AB点循环工具,适配最新B站页面结构 // @author dms // @match https://www.bilibili.com/video/BV* // @match https://www.bilibili.com/bangumi/play/ep* // @match https://www.bilibili.com/medialist/play/* // @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com // @grant GM_notification // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; // 存储管理 const Storage = { savePoint: (index, value) => { try { GM_setValue(`Point_${index}`, value); return true; } catch(e) { console.error('保存点位失败:', e); return false; } }, getPoint: (index) => { try { return GM_getValue(`Point_${index}`, null); } catch(e) { console.error('获取点位失败:', e); return null; } } }; // 工具函数 const Utils = { createButton(text, className, parent) { const button = document.createElement('div'); className.split(' ').forEach(c => button.classList.add(c)); button.innerText = text; parent.appendChild(button); return button; }, showNotification(text, title = '提示', timeout = 2000) { GM_notification({ text, title, timeout }); } }; class VideoController { constructor(video) { this.video = video; this.points = [0, video.duration-1]; this.pointButtons = []; this.animationFrameId = null; this.lastTime = 0; } setPoint(index, value) { if (this.pointButtons[index].classList.contains('active-button')) { this.points[index] = index ? this.video.duration-1 : 0; this.pointButtons[index].classList.remove('active-button'); Storage.savePoint(index, null); } else { this.points[index] = value; this.pointButtons[index].classList.add('active-button'); Storage.savePoint(index, this.points[index]); } } startLoop(button) { if(this.animationFrameId) { cancelAnimationFrame(this.animationFrameId); this.animationFrameId = null; button.classList.remove('active-button'); button.innerText = '▶循环'; return; } button.classList.add('active-button'); button.innerText = '■停止'; const checkLoop = (timestamp) => { if (timestamp - this.lastTime > 200) { const A = this.points[0] <= this.points[1] ? this.points[0] : this.points[1]; const B = this.points[0] > this.points[1] ? this.points[0] : this.points[1]; if(this.video.currentTime >= B) { this.video.currentTime = A; } this.lastTime = timestamp; } this.animationFrameId = requestAnimationFrame(checkLoop); }; this.animationFrameId = requestAnimationFrame(checkLoop); } } const createToolbar = async () => { const video = document.querySelector('#bilibili-player video'); if (!video) return; const controller = new VideoController(video); const waitForControl = setInterval(() => { const controlBar = document.querySelector('.bpx-player-control-bottom'); if (!controlBar) return; clearInterval(waitForControl); // 修改现有的播放控制栏布局 const progressBar = controlBar.querySelector('.bpx-player-control-wrap'); const rightControls = controlBar.querySelector('.bpx-player-control-right'); // 确保进度条在顶部 if (progressBar) { progressBar.style.position = 'absolute'; progressBar.style.top = '0'; progressBar.style.left = '0'; progressBar.style.right = '0'; } // 创建工具栏容器 const toolbarbox = document.createElement('div'); toolbarbox.style = ` display: flex; align-items: center; justify-content: center; height: 100%; padding: 0 10px; flex-grow: 1; `; // 插入工具栏到右侧控制按钮之前 if (rightControls) { controlBar.insertBefore(toolbarbox, rightControls); } else { controlBar.appendChild(toolbarbox); } const toolbar = document.createElement('div'); toolbar.id = 'replayer-toolbar'; toolbar.style = ` display: flex; align-items: center; height: 100%; font-size: 12px; color: #ffffff; white-space: nowrap; `; toolbarbox.appendChild(toolbar); // 创建按钮 const pointA = Utils.createButton('起点', 'tool-item tool-button', toolbar); const toA = Utils.createButton('跳A', 'tool-item tool-button', toolbar); Utils.createButton('|', 'tool-item tool-text', toolbar); const pointB = Utils.createButton('终点', 'tool-item tool-button', toolbar); const toB = Utils.createButton('跳B', 'tool-item tool-button', toolbar); Utils.createButton('|', 'tool-item tool-text', toolbar); const Start = Utils.createButton('▶循环', 'tool-item tool-button', toolbar); // 添加按钮样式 const buttons = toolbar.getElementsByClassName('tool-item'); Array.from(buttons).forEach(button => { button.style.cssText = ` padding: 0 6px; margin: 0 1px; border-radius: 2px; color: #ffffff; cursor: pointer; opacity: 0.85; transition: all 0.2s ease; `; if (button.classList.contains('tool-button')) { button.addEventListener('mouseover', () => { if (!button.classList.contains('active-button')) { button.style.opacity = '1'; button.style.backgroundColor = 'rgba(255, 255, 255, 0.1)'; } }); button.addEventListener('mouseout', () => { if (!button.classList.contains('active-button')) { button.style.opacity = '0.85'; button.style.backgroundColor = ''; } }); } }); controller.pointButtons = [pointA, pointB]; // 事件监听 pointA.addEventListener('click', () => { controller.setPoint(0, video.currentTime); }); pointB.addEventListener('click', () => { controller.setPoint(1, video.currentTime); }); Start.addEventListener('click', () => controller.startLoop(Start)); toA.addEventListener('click', () => { video.currentTime = controller.points[0]; }); toB.addEventListener('click', () => { video.currentTime = controller.points[1]; }); }, 1000); }; if (document.readyState === 'complete') { createToolbar(); } else { window.addEventListener('load', createToolbar); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址