>>31},x=function(t){return t<<5|t>>>27},T=function(t){return t<<3|t>>>29},O=function(t){return t<<7|t>>>25},j=function(t,r,e,i,s,h,n){return t=r&e|~r&i,t+=s+t+h+n,r=E(r),t+=r,t};var R=function(t,r,e,i,s,h,n){return t=r&i|e&~i,t+=s+t+h+n,r=x(r),t+=r,t};var S=function(t,r,e,i,s,h,n){return t=r^e^i,t+=s+t+h+n,r=T(r),t+=r,t};var M=function(t,r,e,i,s,h,n){return t=e^(r|~i),t+=s+t+h+n,r=O(r),t+=r,t};t.prototype.finalize=function(){if(this.finalized)return this;this.finalized=!0;for(var t=this.bytes,e=this.blocks,i=[],s=0;s<64;++s)i[s]=e[s];i[t%64]=y[t%64];for(var h=Math.ceil((t+8)/64)-1;h<16;++h)i[h*4+3]=t<<3;var n=this.h0,a=this.h1,o=this.h2,f=this.h3,u=[1732584193,4023233417,2562383102,271733878];for(t=0;t<16;++t){var c=[],y=0;for(s=0;s<4;++s)c[s]=i[t*4+s]<>p[s]&255;u[t]+=u[t+4]}return this.h0=u[0],this.h1=u[1],this.h2=u[2],this.h3=u[3],this.hBytes=16,v};t.prototype.array=function(){return E.call(this).slice(0,16)};t.prototype.digest=function(){return E.call(this)};t.prototype.buffer=function(){if(!u)throw new Error("ArrayBuffer is not supported");return new Uint8Array(this.digest()).buffer};t.prototype.arrayBuffer=function(){return this.buffer()};t.prototype.hex=function(){var t=E.call(this),r=[];for(var e=0;e>>4]),r.push(c[15&t[e]]);return r.join("")};t.prototype.base64=function(){var t=E.call(this),r=t.length,e=(t[r-1]<<16|t[r-2]<<8|t[r-3])<<8,e=e.toString(64),i="",s=0;if(e.length<4)for(;s<4-e.length;)i+="=",s++;e=i+e;var h="",n=0;for(s=0;s *:nth-of-type(n + 8) {
margin-top: 0px !important;
}
body[biliplus-clean-mode] .recommended-container_floor-aside .container.is-version8 > *:nth-of-type(n + 13) {
margin-top: 0px !important;
}
.stepless-video-rate-btn {
fill: #fff;
color: hsla(0, 0%, 100%, 0.8);
height: 22px;
line-height: 22px;
outline: 0;
position: relative;
text-align: center;
z-index: 2;
font-size: 14px;
width: auto;
margin-right: 10px;
}
.stepless-video-rate-btn:hover {
color: #fff;
}
.stepless-video-rate-btn-result {
cursor: pointer;
font-weight: 600;
width: 100%;
user-select: none;
}
.stepless-video-rate-box {
background: hsla(0, 0%, 8%, 0.9);
border-radius: 2px;
bottom: 41px;
display: none;
height: 100px;
left: 50%;
margin-left: -16px;
position: absolute;
width: 32px;
}
.stepless-video-rate-box.display {
display: block;
}
.stepless-video-rate-number {
color: #e5e9ef;
font-size: 12px;
height: 28px;
line-height: 28px;
margin-bottom: 2px;
text-align: center;
width: 100%;
}
.stepless-video-rate-progress {
height: 60px !important;
margin: 0 auto;
}
.stepless-video-rate-progress .bui-area {
-webkit-box-pack: center !important;
-ms-flex-pack: center !important;
justify-content: center !important;
}
@media screen and (min-width: 750px) {
.bpx-player-container[data-screen='full'] .stepless-video-rate-btn,
.bpx-player-container[data-screen='web'] .stepless-video-rate-btn {
height: 43px;
line-height: 32px;
font-size: 16px;
}
.bpx-player-container[data-screen='full'] .stepless-video-rate-box,
.bpx-player-container[data-screen='web'] .stepless-video-rate-box {
bottom: 74px;
}
}
/* 设置面板样式 */
.biliplus-settings-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 99999;
display: flex;
justify-content: center;
align-items: center;
}
.biliplus-settings-panel {
background: #fff;
border-radius: 12px;
padding: 24px;
width: 400px;
max-width: 90%;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.biliplus-settings-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid #e3e5e7;
}
.biliplus-settings-title {
font-size: 18px;
font-weight: 600;
color: #18191c;
margin: 0;
}
.biliplus-settings-close {
background: none;
border: none;
font-size: 24px;
color: #9499a0;
cursor: pointer;
padding: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
transition: all 0.2s;
}
.biliplus-settings-close:hover {
background: #f1f2f3;
color: #18191c;
}
.biliplus-settings-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #f1f2f3;
}
.biliplus-settings-item:last-child {
border-bottom: none;
}
.biliplus-settings-item-label {
font-size: 14px;
color: #18191c;
font-weight: 500;
}
.biliplus-settings-item-desc {
font-size: 12px;
color: #9499a0;
margin-top: 4px;
}
.biliplus-settings-switch {
position: relative;
width: 44px;
height: 24px;
background: #c9ccd0;
border-radius: 12px;
cursor: pointer;
transition: background 0.3s;
flex-shrink: 0;
}
.biliplus-settings-switch.active {
background: #00aeec;
}
.biliplus-settings-switch::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
background: #fff;
border-radius: 50%;
transition: transform 0.3s;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.biliplus-settings-switch.active::after {
transform: translateX(20px);
}
.biliplus-settings-footer {
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid #e3e5e7;
text-align: center;
font-size: 12px;
color: #9499a0;
}
`);
// 全局补丁
window.addEventListener('keydown', e => {
if (e.key === 'Escape') {
const exitButton = document.querySelector('.reply-view-image .operation-btn-icon.close-container');
if (exitButton) exitButton.click();
}
});
// 首页"换一换"回溯功能
function initFeedRollHistoryBtn() {
if (!getSetting('feed-roll-history-btn')) return;
const feedHistory = [];
let feedHistoryIndex = 0;
const feedRollBackBtn = `
`;
const feedRollNextBtn = `
`;
const targetNode = document.querySelector('.recommended-container_floor-aside');
if (targetNode) {
const disconnect = _UTILS.observe(targetNode, () => {
let feedRollBtn = document.getElementsByClassName('roll-btn')[0];
if (feedRollBtn) {
// 处理返回上一页feed的历史内容
let backBtn = document.createElement('button');
feedRollBtn.parentNode.appendChild(backBtn);
backBtn.outerHTML = feedRollBackBtn;
document.getElementById('feed-roll-back-btn').addEventListener('click', () => {
let feedCards = document.getElementsByClassName('feed-card');
if (feedHistoryIndex == feedHistory.length) {
feedHistory.push(listInnerHTMLOfFeedCard(feedCards));
}
for (let fc_i = 0; fc_i < feedCards.length; fc_i++) {
feedCards[fc_i].innerHTML = feedHistory[feedHistoryIndex - 1][fc_i];
}
feedHistoryIndex = feedHistoryIndex - 1;
if (feedHistoryIndex == 0) {
disableElementById('feed-roll-back-btn', true);
}
disableElementById('feed-roll-next-btn', false);
});
// 处理返回下一页feed的历史内容
let nextBtn = document.createElement('div');
feedRollBtn.parentNode.appendChild(nextBtn);
nextBtn.outerHTML = feedRollNextBtn;
document.getElementById('feed-roll-next-btn').addEventListener('click', () => {
let feedCards = document.getElementsByClassName('feed-card');
for (let fc_i = 0; fc_i < feedCards.length; fc_i++) {
feedCards[fc_i].innerHTML = feedHistory[feedHistoryIndex + 1][fc_i];
}
feedHistoryIndex = feedHistoryIndex + 1;
if (feedHistoryIndex == feedHistory.length - 1) {
disableElementById('feed-roll-next-btn', true);
}
disableElementById('feed-roll-back-btn', false);
});
// 处理点击换一换事件
feedRollBtn.id = 'feed-roll-btn';
feedRollBtn.addEventListener('click', () => {
// 等待元素加载
setTimeout(() => {
if (feedHistoryIndex == feedHistory.length) {
let feedCards = listInnerHTMLOfFeedCard(document.getElementsByClassName('feed-card'));
feedHistory.push(feedCards);
}
feedHistoryIndex = feedHistory.length;
disableElementById('feed-roll-back-btn', false);
disableElementById('feed-roll-next-btn', true);
});
});
// disconnect observer
disconnect();
}
});
}
function disableElementById(id, bool) {
const element = document.getElementById(id);
if (element) {
if (bool) {
element.classList.add('biliplus-disabled');
} else {
element.classList.remove('biliplus-disabled');
}
}
}
function listInnerHTMLOfFeedCard(feedCardElements) {
let feedCardInnerHTMLs = [];
for (let fc of feedCardElements) {
feedCardInnerHTMLs.push(fc.innerHTML);
}
return feedCardInnerHTMLs;
}
}
// 首页干净模式
function initCleanHomePage() {
if (!getSetting('clean-home-page')) return;
let body = document.getElementsByTagName('body')[0];
body.setAttribute('biliplus-clean-mode', '');
const recommendedSwipe = document.getElementsByClassName('recommended-swipe')[0];
if (recommendedSwipe) recommendedSwipe.remove();
const loadMoreAnchor = document.querySelector('.load-more-anchor');
if (loadMoreAnchor) {
loadMoreAnchor.classList.add('biliplus-load-more-anchor');
const scroll = new Event('scroll');
dispatchEvent(scroll);
loadMoreAnchor.classList.remove('biliplus-load-more-anchor');
}
}
// 隐藏搜索栏热搜列表
function initHideHotSearchList() {
if (!getSetting('hide-hot-search-list')) return;
const body = document.querySelector('body');
body.classList.add('biliplus-hide-hot-search-list');
// 解决没有历史记录时显示空白的问题
const navSearchform = document.querySelector('#nav-searchform');
if (navSearchform) {
navSearchform.addEventListener('focusin', () => {
const history = document.querySelector('.search-panel .history');
const searchPanel = document.querySelector('.search-panel');
if (!history) {
if (searchPanel) {
searchPanel.style.display = 'none';
body.classList.add('biliplus-hide-hot-search-list-search-panel-raduis');
}
} else {
if (searchPanel) {
searchPanel.style.display = 'block';
body.classList.remove('biliplus-hide-hot-search-list-search-panel-raduis');
const clearButton = document.querySelector('.search-panel .history .clear');
if (clearButton) {
clearButton.addEventListener('click', () => {
searchPanel.style.display = 'none';
body.classList.add('biliplus-hide-hot-search-list-search-panel-raduis');
});
}
}
}
});
// 防止搜索框输入时候样式改变导致下边框圆角不一致
const navSearchInput = document.querySelector('.nav-search-input');
if (navSearchInput) {
navSearchInput.addEventListener('input', () => {
const suggestions = document.querySelector('.search-panel .suggestions');
const history = document.querySelector('.search-panel .history');
const searchPanel = document.querySelector('.search-panel');
if (!suggestions && !history) {
if (searchPanel) {
searchPanel.style.display = 'none';
body.classList.add('biliplus-hide-hot-search-list-search-panel-raduis');
}
} else {
if (searchPanel) {
searchPanel.style.display = 'block';
body.classList.remove('biliplus-hide-hot-search-list-search-panel-raduis');
}
}
});
}
}
}
// 无级视频倍速
function initSteplessVideoRate() {
if (!getSetting('stepless-video-rate')) return;
let videoRate = 1.0;
let hideBoxTimeout = null;
var mousePositionY = 0;
var initialPositionY = -10;
const rateButton = `
`;
document.body.classList.add('biliplus-stepless-video-rate');
// 用 MutationObserver 解决页面初始化时无法找到 bpx-player-ctrl-playbackrate 按钮
const disconnect = _UTILS.observe(document.body, () => {
if (document.querySelector('.bpx-player-ctrl-btn.bpx-player-ctrl-playbackrate') == null) {
return;
}
if (document.querySelector('.stepless-video-rate-btn') == null) {
const playerControl = document.querySelector('.bpx-player-control-bottom-right');
const oldRateButton = document.querySelector('.bpx-player-ctrl-btn.bpx-player-ctrl-playbackrate');
if (playerControl && oldRateButton) {
const newRateButton = document.createElement('div');
playerControl.insertBefore(newRateButton, oldRateButton);
newRateButton.outerHTML = rateButton;
const box = document.querySelector('.stepless-video-rate-box');
const dot = document.querySelector('.stepless-video-rate-box .bui-thumb');
const bar = document.querySelector('.stepless-video-rate-box .bui-bar');
const rate = document.querySelector('.stepless-video-rate-box .stepless-video-rate-number');
// 进入 btn 就显示 box
const bilibiliPlayer = document.querySelector('#bilibili-player');
if (bilibiliPlayer) {
bilibiliPlayer.addEventListener('mouseover', e => {
const target = e.target;
if (target.nodeName === 'DIV' && target.parentElement.classList.contains('stepless-video-rate-btn')) {
showBox();
if (hideBoxTimeout != null) {
clearTimeout(hideBoxTimeout);
}
}
});
}
// 离开 btn 就消失 box
const steplessVideoRateBtn = document.querySelector('.stepless-video-rate-btn');
if (steplessVideoRateBtn) {
steplessVideoRateBtn.addEventListener('mouseleave', e => {
// 防抖 400 ms
hideBoxTimeout = setTimeout(() => {
hideBox();
box.removeEventListener('mousemove', mouseMove);
}, 400);
});
}
// 进度条逻辑
let tempPositionY = 0;
function mouseDown(event) {
mousePositionY = event.clientY;
tempPositionY = initialPositionY;
box.addEventListener('mousemove', mouseMove);
}
function mouseMove(event) {
let deltaY = event.clientY - mousePositionY;
if (tempPositionY + deltaY < -48 || tempPositionY + deltaY > 0) {
return;
}
initialPositionY = tempPositionY + deltaY;
dot.style.transform = `translateY(${initialPositionY}px)`;
bar.style.transform = `scaleY(${Math.abs(initialPositionY) / 48})`;
videoRate = ((Math.abs(initialPositionY) / 48) * 5).toFixed(1);
rate.innerText = videoRate;
const video = document.querySelector('video');
if (video) video.playbackRate = videoRate;
}
function mouseUp() {
box.removeEventListener('mousemove', mouseMove);
}
if (dot) dot.addEventListener('mousedown', mouseDown);
if (box) box.addEventListener('mouseup', mouseUp);
const steplessBtn = document.querySelector('.stepless-video-rate-btn-result');
if (steplessBtn) {
//double click to reset rate
steplessBtn.addEventListener('dblclick', () => {
const video = document.querySelector('video');
if (video) video.playbackRate = 1.0;
videoRate = 1.0;
const rateNumber = document.querySelector('.stepless-video-rate-number');
if (rateNumber) rateNumber.innerText = "1.0";
const thumb = document.querySelector('.stepless-video-rate-box .bui-thumb');
if (thumb) thumb.style.transform = 'translateY(-10px)';
const barElement = document.querySelector('.stepless-video-rate-box .bui-bar');
if (barElement) barElement.style.transform = 'scaleY(0.2)';
mousePositionY = 0;
initialPositionY = -10;
});
}
}
} else {
disconnect();
}
});
function showBox() {
const rateBox = document.querySelector('.stepless-video-rate-box');
if (rateBox && !rateBox.classList.contains('display')) {
rateBox.classList.add('display');
}
}
function hideBox() {
const rateBox = document.querySelector('.stepless-video-rate-box');
if (rateBox && rateBox.classList.contains('display')) {
rateBox.classList.remove('display');
}
}
}
// 创建设置面板
function createSettingsPanel() {
// 检查是否已存在
if (document.getElementById('biliplus-settings-overlay')) {
return;
}
const overlay = document.createElement('div');
overlay.id = 'biliplus-settings-overlay';
overlay.className = 'biliplus-settings-overlay';
const panel = document.createElement('div');
panel.className = 'biliplus-settings-panel';
const header = document.createElement('div');
header.className = 'biliplus-settings-header';
const title = document.createElement('h2');
title.className = 'biliplus-settings-title';
title.textContent = 'BiliPlus 设置';
const closeBtn = document.createElement('button');
closeBtn.className = 'biliplus-settings-close';
closeBtn.innerHTML = '×';
closeBtn.addEventListener('click', closeSettingsPanel);
header.appendChild(title);
header.appendChild(closeBtn);
// 设置项
const settingsItems = [
{
key: 'feed-roll-history-btn',
label: '首页"换一换"回溯',
desc: '在首页添加前后翻页按钮,可查看之前的推荐内容'
},
{
key: 'clean-home-page',
label: '首页干净模式',
desc: '移除首页推荐区的滑动条,优化布局'
},
{
key: 'hide-hot-search-list',
label: '隐藏热搜列表',
desc: '隐藏搜索框下方的热搜内容'
},
{
key: 'stepless-video-rate',
label: '无级视频倍速',
desc: '支持0.5-5.0倍速的无级调节'
}
];
const footer = document.createElement('div');
footer.className = 'biliplus-settings-footer';
footer.textContent = '修改设置后请刷新页面生效';
panel.appendChild(header);
// 添加设置项
settingsItems.forEach(item => {
const settingItem = document.createElement('div');
settingItem.className = 'biliplus-settings-item';
const labelDiv = document.createElement('div');
const label = document.createElement('div');
label.className = 'biliplus-settings-item-label';
label.textContent = item.label;
const desc = document.createElement('div');
desc.className = 'biliplus-settings-item-desc';
desc.textContent = item.desc;
labelDiv.appendChild(label);
labelDiv.appendChild(desc);
const switchBtn = document.createElement('div');
switchBtn.className = 'biliplus-settings-switch' + (getSetting(item.key) ? ' active' : '');
switchBtn.dataset.key = item.key;
switchBtn.addEventListener('click', () => {
const isActive = switchBtn.classList.contains('active');
switchBtn.classList.toggle('active');
setSetting(item.key, !isActive);
});
settingItem.appendChild(labelDiv);
settingItem.appendChild(switchBtn);
panel.appendChild(settingItem);
});
panel.appendChild(footer);
overlay.appendChild(panel);
document.body.appendChild(overlay);
// 点击遮罩关闭
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
closeSettingsPanel();
}
});
// ESC关闭
const escHandler = (e) => {
if (e.key === 'Escape') {
closeSettingsPanel();
document.removeEventListener('keydown', escHandler);
}
};
document.addEventListener('keydown', escHandler);
}
// 关闭设置面板
function closeSettingsPanel() {
const overlay = document.getElementById('biliplus-settings-overlay');
if (overlay) {
overlay.remove();
}
}
// 注册油猴菜单命令
if (typeof GM_registerMenuCommand !== 'undefined') {
GM_registerMenuCommand('⚙️ BiliPlus 设置', createSettingsPanel);
}
// 初始化所有功能
function initBiliPlus() {
// 首页功能
if (window.location.hostname === 'www.bilibili.com' && window.location.pathname === '/') {
initFeedRollHistoryBtn();
initCleanHomePage();
}
// 全局功能
initHideHotSearchList();
// 视频页功能
if (window.location.pathname.includes('/video/') || window.location.pathname.includes('/list/') || window.location.pathname.includes('/bangumi/play/')) {
initSteplessVideoRate();
}
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initBiliPlus);
} else {
initBiliPlus();
}
// 监听页面变化,处理SPA路由
let lastUrl = location.href;
new MutationObserver(() => {
const currentUrl = location.href;
if (currentUrl !== lastUrl) {
lastUrl = currentUrl;
initBiliPlus();
}
}).observe(document, {
subtree: true,
childList: true
});
})();