B 站循环助手 - 增强版使用说明
B 站循环助手 - 增强版是一款能帮你在 B 站看视频时设置特定片段循环播放的工具,还能保存和使用之前设置好的循环片段,下面就来教你怎么用。
1. 工具界面介绍
🄰(A 点按钮):点击它,就能把当前视频播放到的位置设置为循环开始的起点,也就是 A 点。再次点击这个按钮,A 点会恢复到视频开头(0 秒位置)。
🄱(B 点按钮):和 A 点按钮类似,点击它可以把当前视频播放到的位置设置为循环结束的终点,也就是 B 点。再次点击这个按钮,B 点会恢复到视频的末尾(视频总时长 - 1 秒的位置)。
⯈循环:点击这个按钮,视频就会在你设置好的 A 点和 B 点之间开始循环播放。如果此时正在循环,再次点击这个按钮,就会停止循环,变回正常播放。
⯀停止:当视频处于循环播放状态时,这个按钮显示为 “⯀停止” ,点击它就能停止循环,回到正常播放状态。当视频没有在循环播放时,按钮显示为 “⯈循环” 。
存:点击 “存” 按钮,会弹出一个预设面板,它可以把你当前设置好的 A 点和 B 点组合保存起来,方便你以后快速使用。
2. 使用步骤
设置 AB 点:当你观看视频,看到想要循环播放的片段时,先在片段开始处点击 “🄰” 按钮,设置循环起点;再到片段结束处点击 “🄱” 按钮,设置循环终点。
开始循环:设置好 AB 点后,点击 “⯈循环” 按钮,视频就会在刚刚设置的 AB 点之间循环播放。
暂停与恢复循环:在循环过程中,如果你想暂停循环,点击 “⯀停止” 按钮即可;之后如果还想继续之前的循环,再次点击 “⯈循环” 按钮,只要 AB 点没有重新设置,就会继续之前的循环。
保存预设:点击 “存” 按钮,会生成一个新的预设,预设名称类似 AB1、AB2 这样依次递增。新预设保存成功后,会有提示告知你。
使用预设:点击 “存” 按钮,在弹出的预设面板中,点击已保存的预设(如 AB1),视频会自动应用这个预设的 AB 点,“🄰” 和 “🄱” 按钮会亮起,同时会有提示告知你加载的预设信息。如果再次点击已选中的预设,就会取消预设,AB 点恢复默认,即 A 点为 0,B 点为视频总时长。
删除预设:在预设面板中,点击预设项右侧的 “×”,可以删除对应的预设。如果删除的是当前选中的预设,会自动取消选中状态。
3. 注意事项
这个工具是在 B 站视频播放页面使用的,其他页面可能无法正常工作。
如果 B 站页面更新导致工具不能用,可能需要等待工具开发者更新后才能继续使用。
不带存储功能的简易代码(如下):
// ==UserScript==
// @name B站循环助手-增强版
// @namespace bilibili-replayer
// @version 1.52
// @description 稳定可靠的AB点循环工具,适配最新B站页面结构
// @author lily
// @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.intervalId = null;
}
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.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
button.innerText = '⯈循环';
return;
}
button.innerText = '⯀停止';
this.intervalId = setInterval(() => {
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;
}
}, 200);
}
}
const createToolbar = () => {
let retryCount = 0;
const maxRetries = 30;
const tryCreate = () => {
const video = document.querySelector('#bilibili-player video');
const controlBar = document.querySelector('.bpx-player-control-bottom');
if (!video || !controlBar) {
retryCount++;
if (retryCount < maxRetries) {
setTimeout(tryCreate, 500);
}
return;
}
const controller = new VideoController(video);
// 创建工具栏容器
const toolbarbox = document.createElement('div');
toolbarbox.className = 'ab-loop-toolbar';
// 设置基础样式
toolbarbox.style.cssText = `
display: flex;
align-items: center;
height: 12px;
background-color: rgba(0, 0, 0, 0.35);
border-radius: 4px;
padding: 0 5px;
box-sizing: border-box;
margin-top: 5px;
`;
// 创建自定义样式
const style = document.createElement('style');
style.textContent = `
.tool-item {
padding: 0 6px;
margin: 0 1px;
height: 12px;
line-height: 12px;
color: #ffffff;
cursor: pointer;
opacity: 0.85;
transition: all 0.2s ease;
border-radius: 2px;
user-select: none;
}
.tool-button:hover {
opacity: 1;
background-color: rgba(255, 255, 255, 0.1);
}
.active-button {
background-color: #00a1d6 !important;
color: white !important;
opacity: 1 !important;
}
`;
document.head.appendChild(style);
// 将工具栏添加到播放栏中
controlBar.appendChild(toolbarbox);
// 创建按钮
const pointA = Utils.createButton('🄰', 'tool-item tool-button', toolbarbox);
const toA = Utils.createButton('跳A', 'tool-item tool-button', toolbarbox);
Utils.createButton('|', 'tool-item tool-text', toolbarbox);
const pointB = Utils.createButton('🄱', 'tool-item tool-button', toolbarbox);
const toB = Utils.createButton('跳B', 'tool-item tool-button', toolbarbox);
Utils.createButton('|', 'tool-item tool-text', toolbarbox);
const Start = Utils.createButton('⯈循环', 'tool-item tool-button', toolbarbox);
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]; });
// 处理视频暂停和播放事件
controller.video.addEventListener('pause', () => {
if (controller.intervalId) {
clearInterval(controller.intervalId);
controller.intervalId = null;
Start.innerText = '⯈循环';
}
});
controller.video.addEventListener('play', () => {
if (!controller.intervalId && Start.innerText === '⯀停止') {
controller.startLoop(Start);
}
});
};
tryCreate();
};
// 检查页面加载状态
if (document.readyState === 'complete') {
createToolbar();
} else {
window.addEventListener('load', createToolbar);
}
})();