// ==UserScript==
// @name BiliEx - B站直播间净化增强
// @namespace https://github.com/snorlaxy213/bilibiliEx
// @version 0.3.23
// @description 仿 DouyuEx 思路:净化 B 站直播间页面,只留播放器与右侧弹幕流;悬浮球设置面板;弹幕关键词过滤(标签式编辑)
// @author Jules.chen
// @license MIT
// @homepageURL https://github.com/snorlaxy213/bilibiliEx
// @supportURL https://github.com/snorlaxy213/bilibiliEx/issues
// @icon data:image/png;base64,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
// @match https://live.bilibili.com/*
// @run-at document-start
// @noframes
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// ==/UserScript==
/*
* BiliEx - B站直播间净化增强
* 项目结构参考 qianjiachun/douyuEx(MIT),净化选择器参考 festoney8/bilibili-cleaner(MIT)
*/
(() => {
// src/common/dom.js
function injectCss(css) {
const style = document.createElement("style");
style.textContent = css;
(document.head || document.documentElement).appendChild(style);
return style;
}
function setAttr(name, on) {
const root = document.documentElement;
if (on) root.setAttribute(name, "");
else root.removeAttribute(name);
}
// src/common/settings.js
var STORE_KEY = "bx_settings_v1";
var DEFAULTS = {
features: {},
// featureKey -> bool(未设置时用 feature 定义的 defaultOn)
filter: {
enabled: true,
keywords: ""
// 每行一个,支持 逗号/中文逗号 分隔;/xxx/ 形式按正则处理
}
};
var listeners = [];
function load() {
try {
const raw = GM_getValue(STORE_KEY, "{}");
const saved = JSON.parse(raw || "{}");
return {
features: { ...saved.features },
filter: { ...DEFAULTS.filter, ...saved.filter || {} }
};
} catch {
return { features: {}, filter: { ...DEFAULTS.filter } };
}
}
var cache = load();
function save() {
GM_setValue(STORE_KEY, JSON.stringify(cache));
}
var settingsBus = {
on(cb) {
listeners.push(cb);
},
// 携带变更 key 广播(旧回调忽略参数不受影响;'filter' 代表弹幕过滤配置变化)
emit(key) {
listeners.forEach((cb) => cb(key));
}
};
var settings = {
get(key, fallback = false) {
return cache.features[key] ?? fallback;
},
set(key, val) {
cache.features[key] = val;
save();
settingsBus.emit(key);
},
getFilter() {
return { ...cache.filter };
},
setFilter(patch) {
cache.filter = { ...cache.filter, ...patch };
save();
settingsBus.emit("filter");
}
};
// src/packages/purify/features.js
var FEATURES = [
// ================= 页面净化 =================
{
group: "快速使用",
key: "zenMode",
label: "极简模式",
desc: "一键隐藏页面周边元素,只留播放器与弹幕流",
defaultOn: true,
css: `
html[bx-zenMode] #sections-vm,
html[bx-zenMode] #sidebar-vm,
html[bx-zenMode] .flip-view,
html[bx-zenMode] #gift-control-vm,
html[bx-zenMode] #link-footer-vm,
html[bx-zenMode] footer.link-footer,
html[bx-zenMode] #head-info-vm #LiveRoomHotrankEntries,
html[bx-zenMode] #head-info-vm .activity-entry,
html[bx-zenMode] #head-info-vm .right-dynamic-modules { display: none !important; }
html[bx-zenMode] body:not(.pure_room_root, .player-full-win) #player-ctnr {
border-top-left-radius: 12px;
border-top-right-radius: 12px;
overflow: hidden;
}
`
},
{
group: "页面净化",
key: "hideHeadInfoTags",
label: "主播信息栏:热门榜/活动标签/更多设置",
desc: "隐藏主播信息栏右侧的热门榜、活动标签与更多设置入口",
defaultOn: true,
css: `
html[bx-hideHeadInfoTags] #head-info-vm #LiveRoomHotrankEntries,
html[bx-hideHeadInfoTags] #head-info-vm .activity-entry,
html[bx-hideHeadInfoTags] #head-info-vm .right-dynamic-modules { display: none !important; }
`
},
{
group: "页面净化",
key: "hideHeadInfo",
label: "主播信息栏(整个隐藏,含头像/关注)",
desc: "隐藏主播头像、昵称、关注按钮等整条信息栏",
defaultOn: false,
css: `
html[bx-hideHeadInfo] #head-info-vm { display: none !important; }
html[bx-hideHeadInfo] #player-ctnr {
border-top-left-radius: 12px;
border-top-right-radius: 12px;
overflow: hidden;
}
`
},
{
group: "页面净化",
key: "hideSidebar",
label: "右侧悬浮按钮(实验室/关注等)",
desc: "隐藏屏幕右侧的一列悬浮功能按钮",
defaultOn: true,
css: `
html[bx-hideSidebar] #sidebar-vm { display: none !important; }
`
},
{
group: "页面净化",
key: "hideSections",
label: "播放器下方全部内容(介绍/动态/推荐/公告)",
desc: "隐藏播放器下方的介绍、动态、推荐与公告区块",
defaultOn: true,
css: `
html[bx-hideSections] #sections-vm { display: none !important; }
html[bx-hideSections] .room-bg { min-height: 99vh !important; }
`
},
{
group: "页面净化",
key: "hideFlipView",
label: "活动海报",
desc: "隐藏直播间活动海报与翻转视图",
defaultOn: true,
css: `
html[bx-hideFlipView] .flip-view { display: none !important; }
`
},
{
group: "页面净化",
key: "hideFooter",
label: "页面页脚(关于我们/备案信息)",
desc: "隐藏页面底部关于我们与备案信息,并自动应用上下等边距贴合视口",
defaultOn: true,
css: `
html[bx-hideFooter] #link-footer-vm,
html[bx-hideFooter] footer.link-footer { display: none !important; }
`
},
{
group: "页面净化",
key: "removeWallpaper",
label: "直播间背景图(改纯色)",
desc: "移除直播间背景壁纸,改用纯色背景",
defaultOn: false,
css: `
html[bx-removeWallpaper] .room-bg { background-image: unset !important; }
html[bx-removeWallpaper] #player-ctnr {
box-shadow: 0 0 12px rgb(0 0 0 / 0.2);
border-radius: 12px;
}
html[bx-removeWallpaper] #aside-area-vm { box-shadow: 0 0 12px rgb(0 0 0 / 0.2); }
`
},
// ================= 播放器 =================
{
group: "播放器",
key: "hideGiftBar",
label: "礼物栏(宝箱/抽奖/贵物)",
desc: "隐藏播放器底部的礼物栏,含宝箱、抽奖、贵物入口",
defaultOn: true,
css: `
html[bx-hideGiftBar] #gift-control-vm { display: none !important; }
html[bx-hideGiftBar] body:not(.pure_room_root, .player-full-win) .fullscreen-container-paddingbox {
height: 0 !important;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
overflow: hidden;
}
html[bx-hideGiftBar] body:not(.pure_room_root, .player-full-win) #fullscreen-container {
grid-template-rows: minmax(0, 1fr) auto !important;
}
`
},
{
group: "播放器",
key: "hideAnnouncement",
label: "滚动礼物通告",
desc: "隐藏画面顶部的滚动礼物/打赏通告条",
defaultOn: true,
css: `
html[bx-hideAnnouncement] #live-player .announcement-wrapper { display: none !important; }
`
},
{
group: "播放器",
key: "hidePk",
label: "直播 PK 特效",
desc: "隐藏直播 PK 对战的特殊效果与弹窗",
defaultOn: true,
css: `
html[bx-hidePk] #pk-vm,
html[bx-hidePk] #awesome-pk-vm,
html[bx-hidePk] #universal-pk-vm { display: none !important; }
`
},
{
group: "播放器",
key: "hideLottery",
label: "天选时刻弹窗",
desc: "隐藏「天选时刻」抽奖弹窗提示",
defaultOn: true,
css: `
html[bx-hideLottery] #anchor-guest-box-id { display: none !important; }
`
},
{
group: "播放器",
key: "hideShop",
label: "购物小橙车",
desc: "隐藏画面中的购物车(小橙车)图标",
defaultOn: true,
css: `
html[bx-hideShop] #shop-popover-vm { display: none !important; }
`
},
{
group: "播放器",
key: "hideSticker",
label: "播放器内互动贴纸",
desc: "隐藏画面内的互动贴纸与浮层",
defaultOn: true,
css: `
html[bx-hideSticker] #interactive-sticker-vm { display: none !important; }
`
},
{
group: "播放器",
key: "hideFeedback",
label: "反馈按钮",
desc: "隐藏播放器控制条上的反馈按钮",
defaultOn: true,
css: `
html[bx-hideFeedback] .web-player-icon-feedback { display: none !important; }
`
},
{
group: "播放器",
key: "hideWatermark",
label: "直播水印 / 边缘模糊条纹",
desc: "隐藏直播水印与画面边缘的模糊条纹",
defaultOn: true,
css: `
html[bx-hideWatermark] .web-player-icon-roomStatus,
html[bx-hideWatermark] .blur-edges-ctnr { display: none !important; }
html[bx-hideWatermark] .web-player-module-area-mask { backdrop-filter: none !important; }
`
},
{
group: "播放器",
key: "hideResearch",
label: "卡顿打分弹窗",
desc: "隐藏偶尔弹出的卡顿体验打分框",
defaultOn: true,
css: `
html[bx-hideResearch] .research-container { display: none !important; }
`
},
{
group: "播放器",
key: "hideGameId",
label: "幻星互动游戏",
desc: "隐藏幻星互动游戏入口",
defaultOn: true,
css: `
html[bx-hideGameId] #game-id { display: none !important; }
`
},
{
group: "播放器",
key: "hideFullscreenDanmaku",
label: "全屏时的弹幕发送框",
desc: "隐藏网页全屏状态下浮出的弹幕发送框",
defaultOn: false,
css: `
html[bx-hideFullscreenDanmaku] #fullscreen-danmaku-vm { display: none !important; }
`
},
// ================= 聊天区 =================
{
group: "聊天区",
key: "hideRankList",
label: "排行榜 / 大航海列表",
desc: "隐藏右侧的排行榜与大航海(舰长)列表",
defaultOn: false,
css: `
html[bx-hideRankList] #rank-list-vm { display: none !important; }
html[bx-hideRankList] body:not(.hide-aside-area.player-full-win) #aside-area-vm {
display: flex;
flex-direction: column;
}
html[bx-hideRankList] .chat-history-panel { flex: 1; }
`
},
{
group: "聊天区",
key: "hideWelcome",
label: '"XXX 来了" 欢迎消息',
desc: "隐藏「XXX 来了」这类进场欢迎消息",
defaultOn: true,
css: `
html[bx-hideWelcome] .welcome-section-bottom { display: none; }
`
},
{
group: "聊天区",
key: "hideSystemMsg",
label: "系统提示公告",
desc: "隐藏系统提示与公告类消息",
defaultOn: true,
css: `
html[bx-hideSystemMsg] .convention-msg.border-box,
html[bx-hideSystemMsg] .new-video-pk-item-dm { display: none !important; }
`
},
{
group: "聊天区",
key: "hideGiftMsg",
label: "礼物弹幕(谁送了礼物)",
desc: "隐藏「谁送了什么礼物」类礼物弹幕",
defaultOn: true,
css: `
html[bx-hideGiftMsg] .chat-item.gift-item,
html[bx-hideGiftMsg] .chat-item.common-danmuku-msg { display: none !important; }
`
},
{
group: "聊天区",
key: "hideEmoticon",
label: "大表情弹幕",
desc: "隐藏大表情与表情弹幕",
defaultOn: false,
css: `
html[bx-hideEmoticon] .chat-item.bulge-emoticon,
html[bx-hideEmoticon] .chat-item.chat-emoticon { display: none !important; }
html[bx-hideEmoticon] .bili-dm-emoji,
html[bx-hideEmoticon] .bili-danmaku-x-dm-emoji { display: none !important; }
`
},
{
group: "聊天区",
key: "hideMedal",
label: "粉丝牌 / 等级 / 头衔 / 排名标识",
desc: "隐藏弹幕前的粉丝牌、等级、头衔与排名标识",
defaultOn: false,
css: `
html[bx-hideMedal] .chat-item .fans-medal-item-ctnr,
html[bx-hideMedal] .chat-item .wealth-medal-ctnr,
html[bx-hideMedal] .chat-item .group-medal-ctnr,
html[bx-hideMedal] .chat-item .title-label,
html[bx-hideMedal] .chat-item .rank-icon { display: none !important; }
`
},
{
group: "聊天区",
key: "hideHighlight",
label: "弹幕高亮底色(舰长/提督/总督)",
desc: "取消舰长、提督、总督弹幕的高亮底色",
defaultOn: false,
css: `
html[bx-hideHighlight] .chat-item {
background-color: unset !important;
border-image-source: unset !important;
}
`
},
{
group: "聊天区",
key: "hideBrushPrompt",
label: "底部滚动提示条",
desc: "隐藏弹幕区底部的滚动提示条",
defaultOn: true,
css: `
html[bx-hideBrushPrompt] #brush-prompt { display: none !important; }
html[bx-hideBrushPrompt] .chat-history-panel .chat-history-list.with-brush-prompt { height: 100% !important; }
`
},
{
group: "聊天区",
key: "hideComboCard",
label: "互动卡片弹窗",
desc: "隐藏连击、互动类卡片弹窗",
defaultOn: true,
css: `
html[bx-hideComboCard] #relocated-cards-area { display: none !important; }
`
},
{
group: "聊天区",
key: "hideControlPanel",
label: "弹幕发送框(整个底部面板,回车仍可发)",
desc: "隐藏整个弹幕发送面板,回车键仍可发送",
defaultOn: false,
css: `
html[bx-hideControlPanel] #chat-control-panel-vm {
display: none !important;
min-height: unset !important;
}
html[bx-hideControlPanel] body:not(.hide-aside-area.player-full-win) #aside-area-vm {
display: flex;
flex-direction: column;
}
html[bx-hideControlPanel] .chat-history-panel {
flex: 1;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
`
}
];
// src/packages/purify/index.js
function initPurify() {
injectCss(FEATURES.map((f) => f.css).join("\n"));
const apply = () => {
for (const f of FEATURES) {
setAttr("bx-" + f.key, settings.get(f.key, f.defaultOn));
}
};
apply();
settingsBus.on(apply);
}
// src/packages/enhance/features.js
var ENHANCE_FEATURES = [
{
group: "快速使用",
key: "autoWebFullscreen",
label: "自动网页全屏(进房即全屏)",
desc: "进入直播间后自动切换为网页全屏",
defaultOn: false
},
{
group: "快速使用",
key: "autoHighestQuality",
label: "自动最高画质(受登录/大会员限制)",
desc: "自动切换到当前可用的最高画质",
defaultOn: false
}
];
// src/packages/panel/panel.css.js
var PANEL_CSS = `
/* ===== B 图标 ===== */
#bx-ctrl-item {
position: fixed !important;
z-index: 2147483647 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
cursor: pointer !important;
pointer-events: auto !important;
}
#bx-ctrl-item svg { display: block; pointer-events: none; }
#bx-ctrl-item:hover svg circle:first-of-type { fill: #ff5c8a; }
/* ===== 遮罩层 ===== */
#bx-overlay {
position: fixed;
inset: 0;
z-index: 2147483646;
background: rgba(0, 0, 0, 0.4);
display: none;
}
#bx-overlay.bx-open { display: block; }
/* ===== 设置模态(居中大窗) ===== */
#bx-panel {
position: fixed;
z-index: 2147483647;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: min(860px, 92vw);
height: min(620px, 84vh);
display: none;
flex-direction: column;
background: #f6f7f8;
color: #18191c;
border-radius: 14px;
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.24);
font: 13px/1.5 -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
overflow: hidden;
}
#bx-panel.bx-open { display: flex; }
/* 头部 */
.bx-panel-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 20px;
font-weight: 600;
font-size: 15px;
}
.bx-panel-head .bx-close {
cursor: pointer;
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #61666d;
font-size: 16px;
line-height: 1;
background: #ececee;
}
.bx-panel-head .bx-close:hover { background: #e0e1e4; color: #18191c; }
/* 主体:左导航 + 右内容 */
.bx-panel-main {
display: flex;
flex: 1;
min-height: 0;
}
/* 左侧导航 */
.bx-nav {
width: 180px;
flex: none;
padding: 6px 10px;
overflow-y: auto;
}
.bx-nav-item {
display: flex;
align-items: center;
gap: 10px;
padding: 9px 12px;
margin-bottom: 2px;
border-radius: 8px;
cursor: pointer;
color: #18191c;
user-select: none;
}
.bx-nav-item:hover { background: #ececee; }
.bx-nav-item.bx-active { background: #e3e5e7; font-weight: 600; }
.bx-nav-item .bx-nav-ic {
flex: none;
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.bx-nav-item .bx-nav-ic svg { display: block; }
/* 右侧内容区 */
.bx-content {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: 6px 20px 20px;
}
.bx-content:not(.bx-active) { display: none; }
/* 分组标题(卡片外) */
.bx-group-title {
margin: 16px 0 8px;
font-size: 12px;
color: #9499a0;
font-weight: 600;
}
.bx-group-title:first-child { margin-top: 4px; }
/* 设置卡片 */
.bx-card {
background: #fff;
border: 1px solid #ececee;
border-radius: 12px;
overflow: hidden;
}
/* 设置项:两行(标题 + 描述)+ 右侧 toggle */
.bx-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px 16px;
cursor: pointer;
}
.bx-item + .bx-item { border-top: 1px solid #f0f1f3; }
.bx-item .bx-item-text { flex: 1; min-width: 0; }
.bx-item .bx-item-title { font-size: 13px; color: #18191c; }
.bx-item .bx-item-desc { font-size: 11.5px; color: #9499a0; margin-top: 2px; }
/* iOS 风格 toggle */
.bx-toggle {
position: relative;
flex: none;
width: 40px;
height: 23px;
border-radius: 23px;
background: #d0d3d7;
transition: background 0.18s ease;
}
.bx-toggle::after {
content: "";
position: absolute;
left: 2px;
top: 2px;
width: 19px;
height: 19px;
border-radius: 50%;
background: #fff;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
transition: transform 0.18s ease;
}
.bx-item input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; }
.bx-item input:checked + .bx-toggle { background: #2f81f7; }
.bx-item input:checked + .bx-toggle::after { transform: translateX(17px); }
/* 极简模式锁定态:整行置灰、光标提示、toggle 强制显示开启 */
.bx-item.bx-locked {
opacity: 0.55;
cursor: not-allowed;
}
.bx-item.bx-locked .bx-item-title { color: #61666d; }
.bx-item.bx-locked input:not(:checked) + .bx-toggle { background: #2f81f7; }
.bx-item.bx-locked input:not(:checked) + .bx-toggle::after { transform: translateX(17px); }
/* 「极简模式」徽标 */
.bx-lock-badge {
display: inline-block;
margin-left: 8px;
padding: 0 8px;
height: 18px;
line-height: 18px;
font-size: 11px;
font-weight: 400;
color: #185fa5;
background: #e6f1fb;
border: 1px solid #b5d4f4;
border-radius: 9px;
vertical-align: 1px;
}
/* 弹幕过滤编辑区 */
.bx-filter-row {
display: flex;
gap: 8px;
margin: 12px 16px 4px;
}
.bx-filter-input {
flex: 1;
min-width: 0;
box-sizing: border-box;
height: 34px;
padding: 0 12px;
border: 1px solid #e3e5e7;
border-radius: 8px;
font: 13px/1.5 -apple-system, "PingFang SC", sans-serif;
color: #18191c;
background: #fff;
}
.bx-filter-input:focus { outline: none; border-color: #2f81f7; }
.bx-filter-input.bx-dup { border-color: #e24b4a; }
.bx-filter-add {
flex: none;
height: 34px;
padding: 0 16px;
border: none;
border-radius: 8px;
background: #2f81f7;
color: #fff;
font: 13px/1 -apple-system, "PingFang SC", sans-serif;
cursor: pointer;
}
.bx-filter-add:hover { background: #1f6fe0; }
.bx-filter-add:active { background: #1a5fc4; }
.bx-filter-tags {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
gap: 6px;
min-height: 60px;
max-height: 160px;
overflow-y: auto;
margin: 8px 16px 0;
padding: 10px;
border: 1px solid #e3e5e7;
border-radius: 8px;
background: #fafbfc;
}
.bx-filter-tag {
display: flex;
align-items: center;
gap: 6px;
height: 24px;
padding: 0 7px 0 10px;
border-radius: 12px;
background: #e6f1fb;
border: 1px solid #b5d4f4;
font: 12px/1 -apple-system, "PingFang SC", sans-serif;
color: #185fa5;
}
.bx-filter-tag .bx-tag-close {
cursor: pointer;
font-size: 15px;
line-height: 1;
color: #85b7eb;
}
.bx-filter-tag .bx-tag-close:hover { color: #e24b4a; }
.bx-filter-empty { font: 12px/1.5 -apple-system, "PingFang SC", sans-serif; color: #9499a0; }
.bx-hint { font-size: 11px; color: #9499a0; margin: 10px 16px 0; }
`;
// src/packages/panel/index.js
var ICON_SVG = ``;
var NAV_ICONS = {
purify: '',
player: '',
chat: '',
quick: '',
filter: ''
};
var TABS = [
{ id: "quick", label: "快速使用", icon: "quick", groups: ["快速使用"], hint: "一键获得净化与自动化体验,细节可在各分类页调整。" },
{ id: "purify", label: "页面净化", icon: "purify", groups: ["页面净化"] },
{ id: "player", label: "播放器", icon: "player", groups: ["播放器"] },
{ id: "chat", label: "聊天区", icon: "chat", groups: ["聊天区"] },
{ id: "filter", label: "弹幕过滤", icon: "filter", groups: [] }
];
var ZEN_KEY = "zenMode";
var ZEN_COVERS = ["hideHeadInfoTags", "hideSidebar", "hideSections", "hideFlipView", "hideFooter", "hideGiftBar"];
function initPanel() {
injectCss(PANEL_CSS);
const overlay = document.createElement("div");
overlay.id = "bx-overlay";
overlay.addEventListener("click", () => closePanel());
const panel = document.createElement("div");
panel.id = "bx-panel";
const head = document.createElement("div");
head.className = "bx-panel-head";
const title = document.createElement("span");
title.textContent = "BiliEx 设置";
const close = document.createElement("span");
close.className = "bx-close";
close.textContent = "✕";
close.addEventListener("click", () => closePanel());
head.append(title, close);
const main = document.createElement("div");
main.className = "bx-panel-main";
const nav = document.createElement("div");
nav.className = "bx-nav";
const contentWrap = document.createElement("div");
contentWrap.style.cssText = "flex:1;min-width:0;display:flex;flex-direction:column;";
panel.append(head, main);
main.append(nav, contentWrap);
const contentEls = {};
for (const t of TABS) {
const el = document.createElement("div");
el.className = "bx-content";
el.dataset.key = t.id;
contentWrap.appendChild(el);
contentEls[t.id] = el;
}
const navEls = {};
const switchTab = (id) => {
for (const t of TABS) {
navEls[t.id].classList.toggle("bx-active", t.id === id);
contentEls[t.id].classList.toggle("bx-active", t.id === id);
}
};
for (const t of TABS) {
const item = document.createElement("div");
item.className = "bx-nav-item";
const ic = document.createElement("span");
ic.className = "bx-nav-ic";
ic.innerHTML = NAV_ICONS[t.icon];
const label = document.createElement("span");
label.textContent = t.label;
item.append(ic, label);
item.addEventListener("click", () => switchTab(t.id));
nav.appendChild(item);
navEls[t.id] = item;
}
const allFeatures = [...FEATURES, ...ENHANCE_FEATURES];
const renderFeatureGroup = (container, group) => {
const gt = document.createElement("div");
gt.className = "bx-group-title";
gt.textContent = group;
container.appendChild(gt);
const card = document.createElement("div");
card.className = "bx-card";
const zenOn = settings.get(ZEN_KEY, false);
for (const f of allFeatures.filter((x) => x.group === group)) {
const locked = zenOn && ZEN_COVERS.includes(f.key);
const on = locked ? true : settings.get(f.key, f.defaultOn);
const item = buildToggleItem(f.key, f.label, f.desc, on, (v) => settings.set(f.key, v));
if (locked) {
item.classList.add("bx-locked");
item.setAttribute("title", "由「极简模式」包含,当前已生效");
item.addEventListener("click", () => settings.set(ZEN_KEY, false));
}
card.appendChild(item);
}
container.appendChild(card);
const tab = TABS.find((t) => t.groups.includes(group));
if (tab && tab.hint) {
const h = document.createElement("div");
h.className = "bx-hint";
h.textContent = tab.hint;
container.appendChild(h);
}
};
const renderFilterTab = (container) => {
const gt = document.createElement("div");
gt.className = "bx-group-title";
gt.textContent = "弹幕过滤";
container.appendChild(gt);
const card = document.createElement("div");
card.className = "bx-card";
const filterConf = settings.getFilter();
card.appendChild(
buildToggleItem("filter-enabled", "启用弹幕关键词过滤", "匹配到关键词的弹幕将被隐藏", filterConf.enabled, (v) => settings.setFilter({ enabled: v }))
);
const row = document.createElement("div");
row.className = "bx-filter-row";
const input = document.createElement("input");
input.type = "text";
input.className = "bx-filter-input";
input.placeholder = "输入关键词,按回车添加";
const addBtn = document.createElement("button");
addBtn.type = "button";
addBtn.className = "bx-filter-add";
addBtn.textContent = "添加";
row.append(input, addBtn);
card.appendChild(row);
const tags = document.createElement("div");
tags.className = "bx-filter-tags";
card.appendChild(tags);
const hint = document.createElement("div");
hint.className = "bx-hint";
hint.textContent = "支持 /正则/ 形式,多个关键词可用逗号或换行分隔。修改会实时自动保存并生效。";
card.appendChild(hint);
container.appendChild(card);
let keywords = filterConf.keywords.split(/\n|[,,;;、]/).map((s) => s.trim()).filter(Boolean);
const persist = () => settings.setFilter({ keywords: keywords.join("\n") });
const renderTags = () => {
tags.innerHTML = "";
if (!keywords.length) {
const empty = document.createElement("div");
empty.className = "bx-filter-empty";
empty.textContent = "暂无过滤词";
tags.appendChild(empty);
return;
}
keywords.forEach((w) => {
const tag = document.createElement("span");
tag.className = "bx-filter-tag";
const label = document.createElement("span");
label.textContent = w;
const x = document.createElement("span");
x.className = "bx-tag-close";
x.textContent = "×";
x.addEventListener("click", () => {
keywords = keywords.filter((k) => k !== w);
persist();
renderTags();
});
tag.append(label, x);
tags.appendChild(tag);
});
};
const addKeyword = () => {
const val = input.value.trim();
if (!val) return;
if (keywords.includes(val)) {
input.classList.add("bx-dup");
return;
}
input.classList.remove("bx-dup");
keywords.push(val);
persist();
renderTags();
input.value = "";
input.focus();
};
addBtn.addEventListener("click", addKeyword);
input.addEventListener("keydown", (e) => {
if (e.key === "Enter") addKeyword();
});
input.addEventListener("input", () => input.classList.remove("bx-dup"));
renderTags();
};
const render = (tabId) => {
const targets = tabId ? TABS.filter((t) => t.id === tabId) : TABS;
for (const t of targets) {
const el = contentEls[t.id];
el.innerHTML = "";
if (t.id === "filter") renderFilterTab(el);
else for (const g of t.groups) renderFeatureGroup(el, g);
}
};
settingsBus.on((key) => {
if (!panel.classList.contains("bx-open")) return;
if (key === ZEN_KEY) {
render("quick");
render("purify");
render("player");
} else if (key === "filter") {
return;
} else {
render("quick");
}
});
const openPanel = () => {
render();
ensurePanelParent();
panel.classList.add("bx-open");
overlay.classList.add("bx-open");
switchTab(TABS[0].id);
};
const closePanel = () => {
panel.classList.remove("bx-open");
overlay.classList.remove("bx-open");
};
const togglePanel = () => {
if (panel.classList.contains("bx-open")) closePanel();
else openPanel();
};
function ensurePanelParent() {
const fsEl = document.fullscreenElement;
const root = fsEl || document.body;
if (overlay.parentElement !== root) root.appendChild(overlay);
if (panel.parentElement !== root) root.appendChild(panel);
}
const ICON = document.createElement("div");
ICON.id = "bx-ctrl-item";
ICON.title = "BiliEx 设置";
ICON.innerHTML = ICON_SVG;
ICON.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
togglePanel();
});
const mount = () => {
const root = document.body || document.documentElement;
root.appendChild(overlay);
root.appendChild(panel);
root.appendChild(ICON);
};
if (document.body) mount();
else document.addEventListener("DOMContentLoaded", mount, { once: true });
let lastKey = "";
const setSpot = (left, top, tag) => {
const key = tag + ":" + Math.round(left) + "," + Math.round(top);
if (key !== lastKey) {
lastKey = key;
console.log("[BiliEx] 图标定位(" + tag + ")=(", Math.round(left) + "," + Math.round(top) + ")");
}
ICON.style.left = left + "px";
ICON.style.top = top + "px";
};
const visibleRect = (el) => {
const r = el.getBoundingClientRect();
if (r.width > 0 && r.bottom > 0 && r.top < window.innerHeight && r.right > 0 && r.left < window.innerWidth) return r;
return null;
};
function placeIcon() {
const fsEl = document.fullscreenElement;
const root = fsEl || document.body || document.documentElement;
if (ICON.parentElement !== root) root.appendChild(ICON);
const cp = document.getElementById("chat-control-panel-vm");
const r1 = cp && visibleRect(cp);
if (r1) return setSpot(r1.right - 40, r1.top - 32, "发送面板上方");
const aside = document.getElementById("aside-area-vm");
const r2 = aside && visibleRect(aside);
if (r2) return setSpot(r2.right - 40, r2.bottom - 44, "聊天区右下角");
const fd = document.getElementById("fullscreen-danmaku-vm");
const r3 = fd && visibleRect(fd);
if (r3) return setSpot(r3.right - 40, r3.top - 36, "全屏弹幕框上方");
setSpot(window.innerWidth - 54, window.innerHeight - 130, "固定兜底");
}
document.addEventListener(
"pointerdown",
(e) => {
if (!panel.classList.contains("bx-open")) return;
if (panel.contains(e.target) || ICON.contains(e.target)) return;
closePanel();
},
true
);
const applyAll = () => placeIcon();
applyAll();
setInterval(applyAll, 1e3);
window.addEventListener("resize", applyAll);
document.addEventListener("fullscreenchange", () => setTimeout(applyAll, 100));
if (typeof GM_registerMenuCommand === "function") {
GM_registerMenuCommand("打开 BiliEx 设置", openPanel);
}
}
function buildToggleItem(key, label, desc, checked, onChange, locked) {
const item = document.createElement("label");
item.className = "bx-item";
const text = document.createElement("div");
text.className = "bx-item-text";
const t = document.createElement("div");
t.className = "bx-item-title";
const tl = document.createElement("span");
tl.textContent = label;
t.appendChild(tl);
if (locked) {
const badge = document.createElement("span");
badge.className = "bx-lock-badge";
badge.textContent = "极简模式";
t.appendChild(badge);
}
text.appendChild(t);
if (desc) {
const d = document.createElement("div");
d.className = "bx-item-desc";
d.textContent = desc;
text.appendChild(d);
}
const cb = document.createElement("input");
cb.type = "checkbox";
cb.checked = checked;
cb.addEventListener("change", () => onChange(cb.checked));
const sw = document.createElement("span");
sw.className = "bx-toggle";
item.append(text, cb, sw);
return item;
}
// src/packages/danmaku-filter/index.js
function initDanmakuFilter() {
let observer = null;
let container = null;
function getKeywords() {
const { enabled, keywords } = settings.getFilter();
if (!enabled) return null;
const list = keywords.split(/\n|[,,;;、]/).map((s) => s.trim()).filter(Boolean);
return list.length ? list : null;
}
function hit(node, words) {
const text = (node.textContent || "").toLowerCase();
return words.some((w) => text.includes(w.toLowerCase()));
}
function processNode(n, words) {
if (n.nodeType === 1 && n.classList && n.classList.contains("chat-item") && hit(n, words)) {
n.style.display = "none";
}
}
function process(nodes) {
const words = getKeywords();
if (!words) return;
for (const n of nodes) processNode(n, words);
}
function attach(el) {
if (observer) observer.disconnect();
container = el;
observer = new MutationObserver((muts) => {
const adds = [];
for (const m of muts) for (const n of m.addedNodes) adds.push(n);
process(adds);
});
observer.observe(el, { childList: true });
}
function ensure() {
const el = document.getElementById("chat-items") || document.querySelector(".chat-history-list");
if (el && el !== container) attach(el);
}
ensure();
setInterval(ensure, 2e3);
settingsBus.on(() => {
ensure();
const words = getKeywords();
const el = container;
if (!words || !el) return;
el.querySelectorAll(".chat-item").forEach((n) => {
if (n.style.display !== "none") processNode(n, words);
});
});
}
// src/packages/enhance/index.js
var log = (...a) => console.log("[BiliEx]", ...a);
var mutatingUrl = false;
function watchRoomChange(onChange) {
const getRoom = () => (location.pathname.match(/^\/(\d+)/) || [])[1] || null;
let cur = getRoom();
const wrap = (name) => {
const orig = history[name];
history[name] = function(...args) {
const r = orig.apply(this, args);
if (!mutatingUrl) setTimeout(onChange, 0);
return r;
};
};
wrap("pushState");
wrap("replaceState");
window.addEventListener("popstate", onChange);
return () => {
const m = getRoom();
const changed = m !== cur;
cur = m;
return changed;
};
}
function onPlayerReady(cb) {
if (window.__PlayerInitialized) return cb();
const timer = setInterval(() => {
if (window.__PlayerInitialized) {
clearInterval(timer);
cb();
}
}, 500);
setTimeout(() => clearInterval(timer), 6e4);
}
function findPlayerAPI() {
const cands = [window.Player, window.__INTERACTIVE_PLAYER__, window.EmbedPlayer];
for (const c of cands) {
if (c && typeof c.getPlayerInfo === "function") return c;
}
return null;
}
function retry(fn, intervalMs, timeoutMs, tag) {
const t0 = Date.now();
const timer = setInterval(() => {
let r;
try {
r = fn();
} catch (e) {
log(tag, "出错", e);
r = true;
}
if (r || Date.now() - t0 > timeoutMs) {
clearInterval(timer);
if (!r) log(tag, "超时放弃");
}
}, intervalMs);
}
function ensureUrlParam() {
if (!/^\/\d+/.test(location.pathname)) return;
const enabled = settings.get("autoWebFullscreen", false);
const params = new URLSearchParams(location.search);
if (!enabled) {
if (params.get("web_fullscreen") === "1") {
params.delete("web_fullscreen");
mutatingUrl = true;
history.replaceState(null, "", location.pathname + (params.toString() ? "?" + params.toString() : "") + location.hash);
mutatingUrl = false;
}
return;
}
if (params.get("web_fullscreen") === "1") return;
params.set("web_fullscreen", "1");
mutatingUrl = true;
history.replaceState(null, "", location.pathname + "?" + params.toString() + location.hash);
mutatingUrl = false;
}
function isWebFullscreen() {
return document.body.classList.contains("player-full-win");
}
function clickWebFsButton() {
const wrap = document.getElementById("web-player-controller-wrap-el");
if (!wrap) return false;
for (const el of wrap.querySelectorAll("*")) {
if (el.childElementCount === 0 && el.textContent.trim() === "网页模式") {
const holder = el.closest("div") || el.parentElement;
const btn = holder && holder.querySelector("span.icon");
if (btn) {
btn.click();
return true;
}
}
}
return false;
}
function tryWebFullscreen() {
if (isWebFullscreen()) return true;
const p = findPlayerAPI();
if (p && typeof p.setFullscreenStatus === "function") {
try {
p.setFullscreenStatus(1);
} catch {
}
if (isWebFullscreen()) return true;
}
return clickWebFsButton();
}
function runAutoWebFullscreen(tag) {
if (!settings.get("autoWebFullscreen", false)) return;
if (isWebFullscreen()) return;
retry(() => tryWebFullscreen(), 1e3, 2e4, `自动网页全屏(${tag})`);
}
function applyHighestQuality() {
const p = findPlayerAPI();
if (!p) {
log("自动最高画质: 未找到播放器实例(Player / __INTERACTIVE_PLAYER__ / EmbedPlayer 均无 getPlayerInfo)");
return false;
}
let info;
try {
info = p.getPlayerInfo();
} catch (e) {
log("自动最高画质: getPlayerInfo() 调用异常", e);
return false;
}
log("自动最高画质: getPlayerInfo() 原始返回", info);
const cands = info && (info.qualityCandidates || info.qualityList);
if (!Array.isArray(cands) || !cands.length) {
log("自动最高画质: 未拿到画质列表(无 qualityCandidates / qualityList 数组)");
return false;
}
const qnOf = (c) => Number(c.qn ?? c.value ?? c);
const cur = Number(info.quality ?? info.currentQuality ?? qnOf(cands[0]));
const max = Math.max(...cands.map(qnOf));
log("自动最高画质: 当前 qn=" + cur + " 最高 qn=" + max + " 列表=" + cands.map(qnOf).join(","));
if (!(cur < max)) {
log("自动最高画质: 已是最高画质");
return true;
}
const fn = p.switchQualitySeamless || p.switchQuality;
if (typeof fn === "function") {
try {
fn.call(p, max);
log("自动最高画质: 切档成功", cur, "->", max);
return true;
} catch (e) {
log("自动最高画质: 切画质失败", e);
return false;
}
}
log("自动最高画质: 无 switchQualitySeamless / switchQuality 方法");
return false;
}
function runAutoQuality(tag) {
if (!settings.get("autoHighestQuality", false)) return;
retry(() => applyHighestQuality(), 1e3, 2e4, `自动最高画质(${tag})`);
}
var FRAME_GAP_CSS = `
/* 背景图固定铺满整个视口:内容区下方留白露出背景图而非页面黑底 */
body:not(.pure_room_root, .player-full-win) .room-bg {
position: fixed !important;
inset: 0 !important;
min-height: 100vh !important;
background-size: cover !important;
background-position: center !important;
}
/* 宽度闭环校正:B 站原生公式按隐藏区域的预留高度收窄内容宽(底部余出 ~80px),
JS 量出底部视觉间距后写入 --bx-frame-w 撑回;未设置时回落 B 站原生公式 */
body:not(.pure_room_root):not(.player-full-win) .live-room-app .app-content .app-body {
width: var(--bx-frame-w, clamp(980px, min(calc((100vh - 136px - 78px - 64px) * 16 / 9 + 300px + 12px + 100px), calc(100vw - 100px)), 3420px)) !important;
}
`;
function measureFrameGap() {
const nav = document.getElementById("main-ctnr");
const appBody = document.querySelector(".live-room-app .app-body");
const playerArea = document.querySelector(".live-room-app .app-body .player-and-aside-area");
if (!nav || !appBody || !playerArea) return null;
const navBottom = nav.getBoundingClientRect().bottom;
const bodyTop = appBody.getBoundingClientRect().top;
const paBottom = playerArea.getBoundingClientRect().bottom;
const left = playerArea.querySelector(".left-container");
return {
T: Math.max(8, Math.round(bodyTop - navBottom)),
Gb: Math.round(window.innerHeight - paBottom),
appW: appBody.getBoundingClientRect().width,
leftW: left ? left.getBoundingClientRect().width : 0
};
}
function runFrameGap() {
injectCss(FRAME_GAP_CSS);
let running = false;
let timer = null;
const stop = () => {
running = false;
if (timer) {
clearInterval(timer);
timer = null;
}
document.documentElement.style.removeProperty("--bx-frame-w");
};
const start = () => {
if (running) return;
running = true;
let lastW = null;
timer = setInterval(() => {
const m = measureFrameGap();
if (!m) return;
const diff = m.Gb - m.T;
if (Math.abs(diff) <= 2) return;
const ratio = 0.5625;
const leftW = m.leftW > 0 ? m.leftW : m.appW * 0.8;
const dApp = Math.abs(diff) / ratio * (m.appW / leftW);
let W = diff > 0 ? m.appW + dApp : m.appW - dApp;
const cap = window.innerWidth - 100;
W = Math.round(Math.max(980, Math.min(W, cap)));
if (lastW !== null && Math.abs(W - lastW) <= 2) return;
lastW = W;
document.documentElement.style.setProperty("--bx-frame-w", W + "px");
}, 500);
};
const apply = () => {
const on = settings.get("hideFooter", true) || settings.get("zenMode", false);
if (on) start();
else stop();
};
apply();
settingsBus.on(apply);
}
function initEnhance() {
ensureUrlParam();
runFrameGap();
onPlayerReady(() => {
runAutoWebFullscreen("init");
runAutoQuality("init");
});
const checkRoom = watchRoomChange(() => {
ensureUrlParam();
onPlayerReady(() => {
runAutoWebFullscreen("room");
runAutoQuality("room");
});
});
setInterval(checkRoom, 1e3);
settingsBus.on(() => {
if (settings.get("autoWebFullscreen", false)) runAutoWebFullscreen("toggle");
if (settings.get("autoHighestQuality", false)) runAutoQuality("toggle");
});
window.__BiliExDebugQuality = () => applyHighestQuality();
}
// src/main.js
initPurify();
initDanmakuFilter();
initEnhance();
initPanel();
})();