您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Bilibili UP屏蔽插件 - 屏蔽UP主视频卡片,支持精确匹配和正则匹配,支持视频页面、分类页面、搜索页面等。
当前为
// ==UserScript== // @name Bilibili-BlackList // @namespace https://github.com/HeavenTTT/bilibili-blacklist // @version 1.1.8 // @author HeavenTTT // @description Bilibili UP屏蔽插件 - 屏蔽UP主视频卡片,支持精确匹配和正则匹配,支持视频页面、分类页面、搜索页面等。 // @match *://*.bilibili.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @icon https://www.bilibili.com/favicon.ico // @license MIT // ==/UserScript== (function () { "use strict"; /* * Bilibili-BlackList -- Bilibili UP屏蔽插件 * 脚本大部分代码由AI生成,作者一点都不懂JavaScript,出现bug请联系Gemini / ChatGPT / DeepSeek * this script is mainly generated by AI, the author doesn't know JavaScript at all, if there are bugs, please contact Gemini / ChatGPT / DeepSeek * 感谢你的使用 * Thank you for using this script * * 本段注释为VS code 自动生成 this is a comment generated by VS code */ // 从存储中获取黑名单 // 默认精确匹配黑名单(区分大小写) let exactBlacklist = GM_getValue("exactBlacklist", [ "绝区零", "崩坏星穹铁道", "崩坏3", "原神", "米哈游miHoYo", ]); // 默认正则匹配黑名单 let regexBlacklist = GM_getValue("regexBlacklist", [ "王者荣耀", "和平精英", "PUBG", "绝地求生", "吃鸡", ]); // 标签屏蔽黑名单 let tNameBlacklist = GM_getValue("tNameBlacklist", ["手机游戏"]); let globalConfig = GM_getValue("globalConfig", { flagInfo: true, flagAD: true, flagTName: true, flagCM: true, flagKirby: false, processQueueInterval: 500, // 单位 ms,处理单个卡片间的延迟 blockScanInterval: 200, // 单位 ms,BlockCard扫描新卡片的间隔 }); // 保存黑名单到存储 function saveBlacklists() { GM_setValue("exactBlacklist", exactBlacklist); GM_setValue("regexBlacklist", regexBlacklist); GM_setValue("tNameBlacklist", tNameBlacklist); } function saveGlobalConfig() { GM_setValue("globalConfig", globalConfig); } //开发用日志,bug:运行该函数后,脚本才能正常工作,原因未知,本插件依赖此bug运行,请勿删除任何引用 function Devlog(...args) { if (GM_info.script.version === "Dev") console.log("[Devlog]", ...args); } //#region 核心功能 - 屏蔽视频卡片 let isShowAll = false; // 是否显示全部视频卡片 let isBlocking = false; // 是否正在执行BlockCard扫描操作 let lastBlockExecutionTime = 0; // 上次执行BlockCard扫描的时间戳 let blockedCards = new Set(); // 存储已屏蔽的视频卡片元素 let processedCards = new WeakSet(); // 记录已处理过的卡片(避免重复处理,包括 UP主/标题检查和 tname 获取) let cardProcessQueue = new Set(); // 存储待处理的卡片,用于统一的队列处理 let isProcessingCardQueue = false; // 是否正在处理队列 let isPageActive = true; // 页面是否可见 //给卡片添加屏蔽按钮 function cardAddBlockcContainer(upName, card) { if (!card.querySelector("bilibili-blacklist-block-container")) { const container = document.createElement("div"); container.classList.add("bilibili-blacklist-block-container"); if (!card.querySelector(".bilibili-blacklist-block-btn")) { // 创建屏蔽按钮 if (isVideoPage()) { // 如果是视频页面 const blockButton = createBlockButton(upName, card); card.querySelector(".card-box").style.position = "relative"; card.querySelector(".card-box").appendChild(container); container.appendChild(blockButton); } else if (isCategoryPage()) { // 如果是分类页面 const blockButton = createBlockButton(upName, card); // 创建屏蔽按钮 card.querySelector(".bili-video-card").appendChild(container); // 将按钮添加到卡片中信息中 container.appendChild(blockButton); // 将按钮添加到容器中 } else { const blockButton = createBlockButton(upName, card); // 创建屏蔽按钮 card.appendChild(container); // 将按钮添加到卡片中 container.appendChild(blockButton); // 将按钮添加到容器中 } } } return card.querySelector(".bilibili-blacklist-block-container"); // 返回容器,方便后续添加 tname } //隐藏卡片 function hideCard(card) { card = getRealBlockCard(card); if (!blockedCards.has(card)) { blockedCards.add(card); // 将卡片添加到已屏蔽列表 } if (globalConfig.flagKirby) { addKirbyOverlay(card); // 添加遮罩层 } else { card.style.display = "none"; // 隐藏卡片 } } function getRealBlockCard(card) { if (isSearchPage()) { // 如果是搜索页面 -> 隐藏父元素 card = card.parentElement; // 获取父元素 } if (isMainPage()) { // 如果是主页 if (card.parentElement.classList.contains("bili-feed-card")) { // 如果父元素是feed-card card = card.parentElement; // 获取父元素 if (card.parentElement.classList.contains("feed-card")) { card = card.parentElement; // 获取父元素 } } } return card; } /// 查找所有视频卡片 function querySelectorAllVideoCard(selector) { return document.querySelectorAll(selector); } /// 屏蔽视频卡片 function BlockCard() { const now = Date.now(); // 降低 BlockCard 的调用频率,但确保它能及时捕捉新卡片 if ( isBlocking || now - lastBlockExecutionTime < globalConfig.blockScanInterval ) { return; } isBlocking = true; lastBlockExecutionTime = now; // 记录本次执行时间 try { let cards = null; if (isMainPage()) { cards = querySelectorAllVideoCard(".bili-video-card"); } else if (isVideoPage()) { cards = querySelectorAllVideoCard(".video-page-card-small"); } else if (isCategoryPage()) { cards = querySelectorAllVideoCard(".feed-card"); } else if (isSearchPage()) { cards = querySelectorAllVideoCard(".bili-video-card"); } else return; // 如果不是视频页面,则不执行屏蔽操作 cards.forEach((card) => { if (processedCards.has(card)) { return; // 如果卡片已经完全处理过,则跳过 } // --- 立即隐藏卡片的逻辑移动到这里 --- // 只有当 isShowAll 为 false 时才进行隐藏操作 const realCard = getRealBlockCard(card); if (!isShowAll) { if (globalConfig.flagKirby) { addKirbyOverlay(card); // 卡比模式下添加遮罩 realCard.style.display = "block"; // 确保卡片本身是显示的 } else { realCard.style.display = "none"; // 非卡比模式下直接隐藏 } } // --- 立即隐藏卡片的逻辑结束 --- // 获取UP主和标题信息,并添加屏蔽按钮容器 const { upName, title } = GetVideoInfo(card); if (upName && title) { cardAddBlockcContainer(upName, card); // 添加屏蔽按钮容器 } cardProcessQueue.add(card); // 将卡片添加到统一处理队列 }); // 启动队列处理 if (cardProcessQueue.size > 0 && !isProcessingCardQueue) { processCardQueue(); } refreshBlockCountDisplay(); FixedMainPage(); // 修正主页的错位问题 } finally { isBlocking = false; // 重置屏蔽状态 } } //修正主页的错位问题 function FixedMainPage() { if (!isMainPage()) return; // 仅在主页执行 const container = document.querySelector( ".recommended-container_floor-aside .container" // 推荐视频容器 ); // 检查父元素是否存在 if (container) { const children = container.children; // 这是一个 HTMLCollection let visableindex = 0; // 可见子元素的索引 for (let i = 0; i < children.length; i++) { const child = children[i]; if (child.style.display !== "none") { if (visableindex <= 6) { child.style.marginTop = "0px"; } else if (visableindex < 12) { child.style.marginTop = "24px"; } else { break; // 如果可见子元素超过10个,则停止处理 } visableindex++; // 统计可见子元素的数量 } } } } // 更新屏蔽计数显示 function refreshBlockCountDisplay() { if (blockCountDiv) { blockCountDiv.textContent = `${blockedCards.size}`; // 更新右侧导航栏的屏蔽计数 } // 更新面板标题(如果面板已打开) //const panel = document.getElementById("bilibili-blacklist-panel"); if (blockTitle) { blockTitle.textContent = `已屏蔽视频 (${blockedCards.size})`; // 更新面板标题 } } // 暂时取消屏蔽/恢复屏蔽功能 function toggleShowAll() { isShowAll = !isShowAll; blockedCards.forEach((card) => { // 这里的 card 已经是 getRealBlockCard 后的元素 if (globalConfig.flagKirby) { // 如果是卡比模式,切换遮罩的显示/隐藏 const kirbyOverlay = card.querySelector("#bilibili-blacklist-kirby"); if (kirbyOverlay) { kirbyOverlay.style.display = isShowAll ? "none" : "block"; } // 确保卡片本身是显示的 card.style.display = "block"; } else { card.style.display = isShowAll ? "block" : "none"; } }); btnTempUnblock.textContent = isShowAll ? "恢复屏蔽" : "取消屏蔽"; btnTempUnblock.style.background = isShowAll ? "#dddddd" : "#fb7299"; // 不需要更新blockCount,因为总数没有变化 } const selectorUpName = [ ".bili-video-card__info--author", // 主页 ".bili-video-card__author", // 分类页面--> span title ".name", // 播放页面 ]; const selectorTitle = [ ".bili-video-card__info--tit", // 主页 ".bili-video-card__title", // 分类页面--> span title ".title", // 播放页面 ]; //获取视频信息 -UP主名称 -视频标题 function GetVideoInfo(card) { let upName = ""; let title = ""; //if (card.style.display === "none") return { upName, title }; // 如果卡片已被隐藏,则直接返回空信息 const upNameElement = card.querySelectorAll(selectorUpName.join(", ")); // 使用逗号分隔的选择器 if (upNameElement.length > 0) { upName = upNameElement[0].textContent.trim(); // 获取第一个匹配到的元素的内容,并去除首尾空格 //处理分类页面的UP主名称 if (isCategoryPage()) { upName = upName.split(" · ")[0].trim(); } } const titleElement = card.querySelectorAll(selectorTitle.join(", ")); // 使用逗号分隔的选择器 if (titleElement.length > 0) { title = titleElement[0].textContent.trim(); // 获取第一个匹配到的元素的内容,并去除首尾空格 } return { upName, title }; } function isBlacklisted(upName, title) { // 精确匹配黑名单(无视大小写) // 将 upName 转换为小写,然后与黑名单中的每个项的小写进行比较 const lowerCaseUpName = upName.toLowerCase(); if (exactBlacklist.some((item) => item.toLowerCase() === lowerCaseUpName)) { return true; } // 正则匹配黑名单(无视大小写) // 为正则表达式添加 'i' 标志,表示忽略大小写 if (regexBlacklist.some((regex) => new RegExp(regex, "i").test(upName))) { return true; } if (regexBlacklist.some((regex) => new RegExp(regex, "i").test(title))) { return true; } return false; // 不在黑名单中 } /// 添加UP主到精确黑名单并刷新页面 function addToExactBlacklist(upName, cardElement = null) { try { if (!upName) return; if (!exactBlacklist.includes(upName)) { exactBlacklist.push(upName); saveBlacklists(); refreshAllTabs(); if (cardElement) { hideCard(cardElement); // 隐藏当前卡片 } } } catch (e) { console.error("[bilibili-blacklist] 添加黑名单出错:", e); } } function removeFromExactBlacklist(upName) { try { if (exactBlacklist.includes(upName)) { const index = exactBlacklist.indexOf(upName); exactBlacklist.splice(index, 1); saveBlacklists(); refreshExactList(); } } catch (e) { console.error("[bilibili-blacklist] 移除黑名单出错:", e); } finally { // 刷新后通常页面会重新渲染,所以这里不立即调用 BlockCard // 而是依赖 MutationObserver 再次触发 } } function addToTNameBlacklist(tname, cardElement = null) { try { if (!tname) { return; } if (!tNameBlacklist.includes(tname)) { tNameBlacklist.push(tname); saveBlacklists(); refreshAllTabs(); if (cardElement) { hideCard(cardElement); //隐藏卡片 } } } catch (e) { console.error("[bilibili-blacklist] 添加标签黑名单出错:", e); } } function removeFromTNameBlacklist(tname) { try { if (tNameBlacklist.includes(tname)) { const index = tNameBlacklist.indexOf(tname); tNameBlacklist.splice(index, 1); saveBlacklists(); refreshTagNameList(); // BlockCard(); // 重新执行 BlockCard 重新检查所有卡片 } } catch (e) { console.error("[bilibili-blacklist] 移除标签黑名单出错:", e); } } //#endregion //#region Bv号以及视频信息 function getCardBv(card) { const bvElement = card.querySelector("a"); if (!bvElement) { return null; } try { const link = bvElement.getAttribute("href"); if (!link) { return null; } else { //判断链接是不是cm.bilili if (link.match(/cm.bilibili.com/) && globalConfig.flagCM) { hideCard(card); // 这里直接隐藏广告卡片 return null; // 广告卡片不获取 BV 号 } const bv = link.match(/BV\w+/); return bv ? bv[0] : null; } } catch (e) { return null; } } async function getBilibiliVideoAPI(bvid) { if (!bvid || bvid.length >= 24) { // 增加对 bvid 的空值和长度检查 return null; } const url = `https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`; try { const response = await fetch(url); const json = await response.json(); if (json.code === 0) { return json.data; } else { return null; } } catch (error) { console.error("[bilibili-blacklist] API 请求失败:", error); } } function isCardBlacklistTName(card) { const tnameGroup = card.querySelector(".bilibili-blacklist-tname-group"); if (tnameGroup) { const tnameElements = tnameGroup.querySelectorAll( ".bilibili-blacklist-tname" ); for (const tnameElement of tnameElements) { const tname = tnameElement.textContent.trim(); if (tNameBlacklist.includes(tname)) { return true; } } } return false; } // 统一的卡片队列处理函数 async function processCardQueue() { if (isProcessingCardQueue) return; isProcessingCardQueue = true; while (cardProcessQueue.size > 0) { if (!isPageActive) { await sleep(1000); // 每秒检查一次页面活跃状态 continue; // 不处理当前卡片,重新判断 } const iterator = cardProcessQueue.values(); const card = iterator.next().value; cardProcessQueue.delete(card); // 从队列中移除当前处理的卡片 if (!card || processedCards.has(card)) { // 如果卡片不存在或已经完全处理过,跳过 continue; } let shouldHide = false; // 标记卡片是否应该被隐藏 // 阶段1: UP主和标题的初步处理 const { upName, title } = GetVideoInfo(card); if (upName && title) { // cardAddBlockcContainer(upName, card) 已在 BlockCard 中调用 if (isBlacklisted(upName, title) && globalConfig.flagInfo) { shouldHide = true; } } else { // 如果无法获取UP主或标题信息,对于一些特殊卡片(如广告等), // 仍然需要考虑隐藏,如果 getCardBv 返回 null 且 flagCM 为 true, // 那么 hideCard 会在 getCardBv 内部被调用,这里可以跳过 tname 检查 // 为了严谨性,这里可以再添加一个判断:如果 card 已经被 hideCard 隐藏了,那么 shouldHide 应该为 true if ( getRealBlockCard(card).style.display === "none" && !globalConfig.flagKirby ) { shouldHide = true; } else if ( getRealBlockCard(card).querySelector("#bilibili-blacklist-kirby") ) { shouldHide = true; // 如果已经有Kirby遮罩,说明可能被隐藏了 } } // 阶段2: TName 的异步获取和处理 (如果启用) // 如果卡片已经被UP主/标题屏蔽(或者因为是广告而隐藏),则无需再获取tname if (globalConfig.flagTName && !shouldHide) { const bv = getCardBv(card); // 只有当有 BV 号且尚未添加 tname group 时才请求 API if (bv && !card.querySelector(".bilibili-blacklist-tname-group")) { const data = await getBilibiliVideoAPI(bv); if (data) { const container = card.querySelector( ".bilibili-blacklist-block-container" ); // 只有当容器存在时才添加 tname group if (container) { const tnameGroup = document.createElement("div"); tnameGroup.className = "bilibili-blacklist-tname-group"; let hasTname = false; if (data.tname) { const btn = createTNameBlockButton(data.tname, card); tnameGroup.appendChild(btn); hasTname = true; } if (data.tname_v2) { const tnameElement = createTNameBlockButton( data.tname_v2, card ); tnameGroup.appendChild(tnameElement); hasTname = true; } if (hasTname) { container.appendChild(tnameGroup); } } if (isCardBlacklistTName(card)) { shouldHide = true; } } } } // 最终根据 shouldHide 决定卡片的可见性 if (shouldHide) { hideCard(card); // 隐藏卡片 (会添加到 blockedCards,并根据 kirby 配置添加遮罩或设置 display: none) } else { // 如果最终不屏蔽,则显示卡片 const realCardToDisplay = getRealBlockCard(card); if (blockedCards.has(realCardToDisplay)) { // 如果之前因为初步判断被隐藏,现在又被判断为不需要屏蔽,则从blockedCards移除 blockedCards.delete(realCardToDisplay); } if (globalConfig.flagKirby) { removeKirbyOverlay(card); // 移除Kirby遮罩 } // 确保卡片是可见的 realCardToDisplay.style.display = "block"; } // 标记为已完全处理 processedCards.add(card); await sleep(globalConfig.processQueueInterval || 100); } isProcessingCardQueue = false; refreshBlockCountDisplay(); // 处理结束后更新屏蔽计数 } function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } // 页面可见性监听 document.addEventListener("visibilitychange", () => { isPageActive = !document.hidden; //Devlog(`页面可见状态改变: ${isPageActive ? "活动" : "隐藏"}`); }); // 窗口焦点监听 window.addEventListener("focus", () => { isPageActive = true; //Devlog("窗口获得焦点"); }); window.addEventListener("blur", () => { isPageActive = false; //Devlog("窗口失去焦点"); }); //#endregion //#region 页面修改 //创建屏蔽按钮(悬停在视频卡片上时显示) function createBlockButton(upName, cardElement) { const btn = document.createElement("div"); btn.className = "bilibili-blacklist-block-btn"; btn.innerHTML = "屏蔽"; btn.title = `屏蔽: ${upName}`; // 屏蔽按钮样式 // 点击时添加到黑名单 btn.addEventListener("click", (e) => { e.stopPropagation(); // 防止事件冒泡 addToExactBlacklist(upName, cardElement); }); return btn; } function createTNameBlockButton(tName, cardElement) { const btn = document.createElement("span"); btn.className = "bilibili-blacklist-tname"; btn.innerHTML = `${tName}`; btn.title = `屏蔽: ${tName}`; // 屏蔽按钮样式 // 点击时添加到黑名单 btn.addEventListener("click", (e) => { e.stopPropagation(); // 防止事件冒泡 addToTNameBlacklist(tName, cardElement); }); return btn; } // 在右侧导航栏添加黑名单管理按钮 let blockCountDiv = null; function addBlacklistManagerButton() { const rightEntry = document.querySelector(".right-entry"); if (!rightEntry) { console.warn("[bilibili-blacklist] 未找到右侧导航栏"); return; } else if (!rightEntry.querySelector("#bilibili-blacklist-manager")) { //else if(rightEntry.getElementById('bilibili-blacklist-manager')){ const li = document.createElement("li"); li.id = "bilibili-blacklist-manager"; li.style.cursor = "pointer"; li.className = "v-popover-wrap"; const btn = document.createElement("div"); btn.className = "right-entry-item"; btn.style.display = "flex"; btn.style.flexDirection = "column"; btn.style.alignItems = "center"; btn.style.justifyContent = "center"; // 可爱的卡比图标SVG const icon = document.createElement("div"); icon.className = "right-entry__outside"; icon.innerHTML = getKirbySVG(); //icon.style.color = '#fb7299'; // B站粉色 icon.style.marginBottom = "-5px"; blockCountDiv = document.createElement("span"); //const text = document.createElement('div'); blockCountDiv.textContent = `0`; btn.appendChild(icon); btn.appendChild(blockCountDiv); li.appendChild(btn); // 在导航中插入按钮 if (rightEntry.children.length > 1) { rightEntry.insertBefore(li, rightEntry.children[1]); } else { rightEntry.appendChild(li); } // 点击按钮时显示面板 li.addEventListener("click", () => { if (managerPanel.style.display === "none") { managerPanel.style.display = "flex"; } else { managerPanel.style.display = "none"; } }); } } // 创建黑名单管理面板 let btnTempUnblock; let managerPanel; let exactList; //精确匹配列表 let regexList; //正则匹配列表 let tNameList; //tname匹配列表 let configList; //配置列表 let blockTitle; // 工具函数:创建按钮 function createButton(text, bgColor, onClick) { const button = document.createElement("button"); button.textContent = text; button.style.padding = "4px 8px"; button.style.background = bgColor; button.style.color = "#fff"; button.style.border = "none"; button.style.borderRadius = "4px"; button.style.cursor = "pointer"; button.addEventListener("click", onClick); return button; } // 工具函数:创建列表项 function createListItem(contentText, onRemoveClick, isRegex = false) { const item = document.createElement("li"); item.style.display = "flex"; item.style.justifyContent = "space-between"; item.style.alignItems = "center"; item.style.padding = "8px 0"; item.style.borderBottom = "1px solid #f1f2f3"; const content = document.createElement("span"); content.textContent = contentText; content.style.flex = "1"; const removeBtn = createButton("移除", "#f56c6c", onRemoveClick); item.appendChild(content); item.appendChild(removeBtn); return item; } // 更新精确匹配列表 function refreshExactList() { if (!exactList) { if (!isBlacklistPanelCreated()) { return; } exactList = document.querySelector("#bilibili-blacklist-exact-list"); if (!exactList) { console.warn("[Bilibili-Blacklist] exactList 未定义"); return; } } exactList.innerHTML = ""; exactBlacklist.forEach((upName) => { const item = createListItem(upName, () => { removeFromExactBlacklist(upName); //BlockCard(); //更新屏蔽卡片 }); exactList.appendChild(item); }); Array.from(exactList.children) .reverse() .forEach((item) => exactList.appendChild(item)); if (exactBlacklist.length === 0) { const empty = document.createElement("div"); empty.textContent = "暂无精确匹配屏蔽UP主"; empty.style.textAlign = "center"; empty.style.padding = "16px"; empty.style.color = "#999"; exactList.appendChild(empty); } } // 更新正则匹配列表 function refreshRegexList() { if (!regexList) return; regexList.innerHTML = ""; regexBlacklist.forEach((regex, index) => { const item = createListItem( regex, () => { regexBlacklist.splice(index, 1); saveBlacklists(); refreshRegexList(); //BlockCard(); }, true ); regexList.appendChild(item); }); Array.from(regexList.children) .reverse() .forEach((item) => regexList.appendChild(item)); if (regexBlacklist.length === 0) { const empty = document.createElement("div"); empty.textContent = "暂无正则匹配屏蔽规则"; empty.style.textAlign = "center"; empty.style.padding = "16px"; empty.style.color = "#999"; regexList.appendChild(empty); } } // 更新tname匹配列表 function refreshTagNameList() { if (!tNameList) { if (!isBlacklistPanelCreated()) { return; } tNameList = document.querySelector("#bilibili-blacklist-tname-list"); if (!tNameList) { console.warn("[Bilibili-Blacklist] tNameList 未定义"); return; } } tNameList.innerHTML = ""; tNameBlacklist.forEach((tName) => { const item = createListItem(tName, () => { removeFromTNameBlacklist(tName); }); tNameList.appendChild(item); }); Array.from(tNameList.children) .reverse() .forEach((item) => tNameList.appendChild(item)); if (tNameBlacklist.length === 0) { const empty = document.createElement("div"); empty.textContent = "暂无标签屏蔽规则"; empty.style.textAlign = "center"; empty.style.padding = "16px"; empty.style.color = "#999"; tNameList.appendChild(empty); } } // 工具函数:创建一个开关按钮 function createToggleButton(labelText, configKey, title = null) { const container = document.createElement("div"); container.style.display = "flex"; container.style.alignItems = "center"; container.style.marginBottom = "8px"; container.style.gap = "8px"; container.title = title; const label = document.createElement("span"); label.textContent = labelText; label.style.flex = "1"; const button = document.createElement("button"); button.style.padding = "6px 12px"; button.style.border = "none"; button.style.borderRadius = "4px"; button.style.cursor = "pointer"; button.style.color = "#fff"; function refreshButtonAppearance() { button.textContent = globalConfig[configKey] ? "开启" : "关闭"; button.style.backgroundColor = globalConfig[configKey] ? "#fb7299" : "#909399"; } button.addEventListener("click", () => { globalConfig[configKey] = !globalConfig[configKey]; refreshButtonAppearance(); saveGlobalConfig(); // 你可以实现此函数,将globalConfig存储到localStorage或其他 }); refreshButtonAppearance(); container.appendChild(label); container.appendChild(button); return container; } // 更新配置列表 function refreshConfigSettings() { if (!configList) return; // 清空 configContent configList.innerHTML = ""; //开关 const container = document.createElement("div"); container.style.display = "flex"; container.style.alignItems = "center"; container.style.marginBottom = "8px"; container.style.gap = "8px"; container.style.margin = "20px 0"; const label = document.createElement("span"); label.textContent = "临时开关"; label.style.flex = "1"; btnTempUnblock = document.createElement("button"); btnTempUnblock.textContent = isShowAll ? "恢复屏蔽" : "取消屏蔽"; btnTempUnblock.style.background = isShowAll ? "#dddddd" : "#fb7299"; btnTempUnblock.style.padding = "6px 12px"; btnTempUnblock.style.border = "none"; btnTempUnblock.style.cursor = "pointer"; btnTempUnblock.style.color = "#fff"; btnTempUnblock.addEventListener("click", toggleShowAll); container.appendChild(label); container.appendChild(btnTempUnblock); configList.appendChild(container); // 标题 const title = document.createElement("h4"); title.textContent = "全局配置开关(部分功能刷新后生效)"; title.style.fontWeight = "bold"; title.style.marginBottom = "12px"; configList.appendChild(title); // 创建开关 configList.appendChild( createToggleButton("屏蔽标题/Up主名", "flagInfo", "屏蔽标题/Up主名") ); configList.appendChild( createToggleButton("屏蔽分类标签", "flagTName", "通过请求API获取分类标签") ); configList.appendChild( createToggleButton("屏蔽主页推荐", "flagAD", "直播/广告/分区推送") ); configList.appendChild( createToggleButton("屏蔽主页视频软广", "flagCM", "cm.bilibili.com软广") ); configList.appendChild( createToggleButton("遮挡被屏蔽视频", "flagKirby", "更加温和的方式") ); // BlockCard 扫描间隔 const blockScanIntervalContainer = document.createElement("div"); blockScanIntervalContainer.style.display = "flex"; blockScanIntervalContainer.style.alignItems = "center"; blockScanIntervalContainer.style.marginTop = "16px"; blockScanIntervalContainer.style.gap = "8px"; blockScanIntervalContainer.title = "扫描新卡片的间隔时间,单位 ms。值越小,新卡片隐藏越快。"; const blockScanIntervalLabel = document.createElement("span"); blockScanIntervalLabel.textContent = "卡片扫描间隔 (ms):"; blockScanIntervalLabel.style.flex = "1"; const blockScanIntervalInput = document.createElement("input"); blockScanIntervalInput.type = "number"; blockScanIntervalInput.min = "0"; blockScanIntervalInput.value = globalConfig.blockScanInterval; blockScanIntervalInput.style.width = "100px"; blockScanIntervalInput.style.padding = "6px"; blockScanIntervalInput.style.border = "1px solid #ddd"; blockScanIntervalInput.style.borderRadius = "4px"; const saveBlockScanIntervalBtn = document.createElement("button"); saveBlockScanIntervalBtn.textContent = "保存"; saveBlockScanIntervalBtn.style.padding = "6px 12px"; saveBlockScanIntervalBtn.style.backgroundColor = "#fb7299"; saveBlockScanIntervalBtn.style.color = "#fff"; saveBlockScanIntervalBtn.style.border = "none"; saveBlockScanIntervalBtn.style.borderRadius = "4px"; saveBlockScanIntervalBtn.style.cursor = "pointer"; saveBlockScanIntervalBtn.addEventListener("click", () => { const val = parseInt(blockScanIntervalInput.value, 10); if (!isNaN(val) && val >= 0) { globalConfig.blockScanInterval = val; saveGlobalConfig(); } else { alert("请输入有效的非负数字!"); } }); blockScanIntervalContainer.appendChild(blockScanIntervalLabel); blockScanIntervalContainer.appendChild(blockScanIntervalInput); blockScanIntervalContainer.appendChild(saveBlockScanIntervalBtn); configList.appendChild(blockScanIntervalContainer); // 处理队列请求间隔 const intervalContainer = document.createElement("div"); intervalContainer.style.display = "flex"; intervalContainer.style.alignItems = "center"; intervalContainer.style.marginTop = "16px"; intervalContainer.style.gap = "8px"; intervalContainer.title = "处理队列中单个视频卡片时的API请求间隔时间,单位 ms。间隔时间越长,越不容易触发B站API限速。建议值 100ms。"; const intervalLabel = document.createElement("span"); intervalLabel.textContent = "卡片处理间隔 (ms):"; intervalLabel.style.flex = "1"; const intervalInput = document.createElement("input"); intervalInput.type = "number"; intervalInput.min = "0"; intervalInput.value = globalConfig.processQueueInterval; intervalInput.style.width = "100px"; intervalInput.style.padding = "6px"; intervalInput.style.border = "1px solid #ddd"; intervalInput.style.borderRadius = "4px"; const saveIntervalBtn = document.createElement("button"); saveIntervalBtn.textContent = "保存"; saveIntervalBtn.style.padding = "6px 12px"; saveIntervalBtn.style.backgroundColor = "#fb7299"; saveIntervalBtn.style.color = "#fff"; saveIntervalBtn.style.border = "none"; saveIntervalBtn.style.borderRadius = "4px"; saveIntervalBtn.style.cursor = "pointer"; saveIntervalBtn.addEventListener("click", () => { const val = parseInt(intervalInput.value, 10); if (!isNaN(val) && val >= 0) { globalConfig.processQueueInterval = val; saveGlobalConfig(); // 保存配置 //alert("处理队列请求间隔已保存!"); } else { alert("请输入有效的非负数字!"); } }); intervalContainer.appendChild(intervalLabel); intervalContainer.appendChild(intervalInput); intervalContainer.appendChild(saveIntervalBtn); configList.appendChild(intervalContainer); return configList; } function refreshAllTabs() { refreshExactList(); refreshRegexList(); refreshTagNameList(); refreshConfigSettings(); } function isBlacklistPanelCreated() { const panelInDom = document.querySelector( "#bilibili-blacklist-manager-panel" ); if (panelInDom) { if (!managerPanel) { managerPanel = panelInDom; } return true; } return false; } // 创建黑名单面板 function createBlacklistPanel() { if (isBlacklistPanelCreated()) { return; } managerPanel = document.createElement("div"); managerPanel.id = "bilibili-blacklist-panel"; managerPanel.style.position = "fixed"; managerPanel.style.top = "50%"; managerPanel.style.left = "50%"; managerPanel.style.transform = "translate(-50%, -50%)"; managerPanel.style.width = "500px"; managerPanel.style.maxHeight = "80vh"; managerPanel.style.backgroundColor = "#fff"; managerPanel.style.borderRadius = "8px"; managerPanel.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.15)"; managerPanel.style.zIndex = "99999"; managerPanel.style.overflow = "hidden"; managerPanel.style.display = "none"; managerPanel.style.flexDirection = "column"; managerPanel.style.backgroundColor = "#ffffffee"; const tabContainer = document.createElement("div"); tabContainer.style.display = "flex"; tabContainer.style.borderBottom = "1px solid #f1f2f3"; // 内容区 const exactContent = document.createElement("div"); exactContent.style.padding = "16px"; exactContent.style.overflowY = "auto"; exactContent.style.flex = "1"; exactContent.style.display = "block"; const regexContent = document.createElement("div"); regexContent.style.padding = "16px"; regexContent.style.overflowY = "auto"; regexContent.style.flex = "1"; regexContent.style.display = "none"; const tnameContent = document.createElement("div"); tnameContent.style.padding = "16px"; tnameContent.style.overflowY = "auto"; tnameContent.style.flex = "1"; tnameContent.style.display = "none"; const configContent = document.createElement("div"); configContent.style.padding = "16px"; configContent.style.overflowY = "auto"; configContent.style.flex = "1"; configContent.style.display = "none"; const tabs = [ { name: "精确匹配(Up名字)", content: exactContent }, { name: "正则匹配(Up/标题)", content: regexContent }, { name: "屏蔽分类", content: tnameContent }, { name: "插件配置", content: configContent }, ]; tabs.forEach((tabData) => { const tab = document.createElement("div"); tab.textContent = tabData.name; tab.style.padding = "12px 16px"; tab.style.cursor = "pointer"; tab.style.fontWeight = "500"; tab.style.borderBottom = tabData.content.style.display === "block" ? "2px solid #fb7299" : "none"; tab.addEventListener("click", () => { tabs.forEach(({ tab: t, content: c }) => { t.style.borderBottom = "none"; c.style.display = "none"; }); tab.style.borderBottom = "2px solid #fb7299"; tabData.content.style.display = "block"; }); tabData.tab = tab; tabContainer.appendChild(tab); }); // Header 区域 const header = document.createElement("div"); header.style.padding = "16px"; header.style.borderBottom = "1px solid #f1f2f3"; header.style.display = "flex"; header.style.justifyContent = "space-between"; header.style.alignItems = "center"; blockTitle = document.createElement("h3"); blockTitle.style.margin = "0"; blockTitle.style.fontWeight = "500"; const closeBtn = document.createElement("button"); closeBtn.textContent = "×"; closeBtn.style.background = "none"; closeBtn.style.border = "none"; closeBtn.style.cursor = "pointer"; closeBtn.style.padding = "0 8px"; closeBtn.addEventListener("click", () => { managerPanel.style.display = "none"; }); header.appendChild(blockTitle); header.appendChild(closeBtn); const contentContainer = document.createElement("div"); contentContainer.style.display = "flex"; contentContainer.style.flexDirection = "column"; contentContainer.style.flex = "1"; contentContainer.style.overflow = "hidden"; // 精确匹配输入框 const addExactContainer = document.createElement("div"); addExactContainer.style.display = "flex"; addExactContainer.style.marginBottom = "16px"; addExactContainer.style.gap = "8px"; const exactInput = document.createElement("input"); exactInput.type = "text"; exactInput.placeholder = "输入要屏蔽的UP主名称"; exactInput.style.flex = "1"; exactInput.style.padding = "8px"; exactInput.style.border = "1px solid #ddd"; exactInput.style.borderRadius = "4px"; const addExactBtn = document.createElement("button"); addExactBtn.textContent = "添加"; addExactBtn.style.padding = "8px 16px"; addExactBtn.style.background = "#fb7299"; addExactBtn.style.color = "#fff"; addExactBtn.style.border = "none"; addExactBtn.style.borderRadius = "4px"; addExactBtn.style.cursor = "pointer"; addExactBtn.addEventListener("click", () => { const upName = exactInput.value.trim(); if (upName) { addToExactBlacklist(upName); exactInput.value = ""; } }); addExactContainer.appendChild(exactInput); addExactContainer.appendChild(addExactBtn); exactContent.appendChild(addExactContainer); // 正则匹配输入框 const addRegexContainer = document.createElement("div"); addRegexContainer.style.display = "flex"; addRegexContainer.style.marginBottom = "16px"; addRegexContainer.style.gap = "8px"; const regexInput = document.createElement("input"); regexInput.type = "text"; regexInput.placeholder = "输入正则表达式 (如: 小小.*Official)"; regexInput.style.flex = "1"; regexInput.style.padding = "8px"; regexInput.style.border = "1px solid #ddd"; regexInput.style.borderRadius = "4px"; const addRegexBtn = document.createElement("button"); addRegexBtn.textContent = "添加"; addRegexBtn.style.padding = "8px 16px"; addRegexBtn.style.background = "#fb7299"; addRegexBtn.style.color = "#fff"; addRegexBtn.style.border = "none"; addRegexBtn.style.borderRadius = "4px"; addRegexBtn.style.cursor = "pointer"; addRegexBtn.addEventListener("click", () => { const regex = regexInput.value.trim(); if (regex && !regexBlacklist.includes(regex)) { try { new RegExp(regex); regexBlacklist.push(regex); saveBlacklists(); regexInput.value = ""; refreshRegexList(); //BlockCard(); } catch (e) { alert("无效的正则表达式: " + e.message); } } }); addRegexContainer.appendChild(regexInput); addRegexContainer.appendChild(addRegexBtn); regexContent.appendChild(addRegexContainer); // 精确匹配列表 exactList = document.createElement("ul"); exactList.id = "bilibili-blacklist-exact-list"; exactList.style.listStyle = "none"; exactList.style.padding = "0"; exactList.style.margin = "0"; // 正则匹配列表 regexList = document.createElement("ul"); regexList.id = "bilibili-blacklist-regex-list"; regexList.style.listStyle = "none"; regexList.style.padding = "0"; regexList.style.margin = "0"; // tname列表 tNameList = document.createElement("ul"); tNameList.id = "bilibili-blacklist-tname-list"; tNameList.style.listStyle = "none"; tNameList.style.padding = "0"; tNameList.style.margin = "0"; // 配置列表 configList = document.createElement("ul"); configList.id = "bilibili-blacklist-config-list"; configList.style.listStyle = "none"; configList.style.padding = "0"; configList.style.margin = "0"; // 更新列表 refreshAllTabs(); exactContent.appendChild(exactList); regexContent.appendChild(regexList); tnameContent.appendChild(tNameList); configContent.appendChild(configList); contentContainer.appendChild(exactContent); contentContainer.appendChild(regexContent); contentContainer.appendChild(tnameContent); contentContainer.appendChild(configContent); managerPanel.appendChild(tabContainer); managerPanel.appendChild(header); managerPanel.appendChild(contentContainer); document.body.appendChild(managerPanel); return managerPanel; } // 添加全局样式 GM_addStyle(` .bilibili-blacklist-block-container { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 20px; margin-top: 5px; padding: 0 5px; font-size: 12px; flex-direction: row; justify-content: space-between; align-items: center; gap: 3px; z-index: 9999; pointer-events: none; text-align:center; } .bili-video-card:hover .bilibili-blacklist-block-container, .card-box:hover .bilibili-blacklist-block-container { display: flex !important; pointer-events: none; } .card-box .bilibili-blacklist-block-container { flex-direction: column; align-items: flex-start; height: 100%; } .card-box .bilibili-blacklist-tname-group { flex-direction: column; align-items: flex-end; bottom: 0; } .card-box .bilibili-blacklist-tname-group .bilibili-blacklist-tname { background-color:rgba(255, 255, 255, 0.87); color: #9499A0; border: 1px solid #9499A0; } .bilibili-blacklist-block-btn { position: static; display: flex; width: 40px; height: 20px; justify-content: center; align-items: center; pointer-events: auto !important; background-color: #fb7299dd; color: white; border-radius: 10%; cursor: pointer; text-align: center; } .bilibili-blacklist-tname-group { display: flex; flex-direction: row; padding:0 5px; gap: 3px; align-items: center; margin-left: auto; max-width: 80%; pointer-events: none; } .bilibili-blacklist-tname { background-color: #fb7299dd; color: white; height: 20px; padding: 0 5px; border-radius: 10%; cursor: pointer; border-radius: 2px; pointer-events: auto; text-align: center; display: flex; justify-content: center; align-items: center; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } /* 修复视频卡片布局 */ .bili-video-card__cover { contain: layout !important; } /* 面板样式 */ #bilibili-blacklist-panel { font-size: 15px; } /* 按钮悬停效果 */ #bilibili-blacklist-panel button { transition: background-color 0.2s; } #bilibili-blacklist-panel button:hover { opacity: 0.9; } /* 管理按钮悬停效果 */ #bilibili-blacklist-manager:hover svg { transform: scale(1.1); } #bilibili-blacklist-manager svg { transition: transform 0.2s; } /* 输入框聚焦效果 */ #bilibili-blacklist-panel input:focus { outline: none; border-color: #fb7299 !important; } /*灰度效果*/ .bilibili-blacklist-grayscale { filter: grayscale(95%); } `); //可爱的卡比图标 function getKirbySVG() { return ` <svg width="35" height="35" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" > <ellipse cx="70" cy="160" rx="30" ry="15" fill="#cc3333" /> <ellipse cx="130" cy="160" rx="30" ry="15" fill="#cc3333" /> <ellipse cx="50" cy="120" rx="20" ry="20" fill="#ffb6c1" /> <ellipse cx="150" cy="120" rx="20" ry="20" fill="#ffb6c1" /> <circle cx="100" cy="110" r="60" fill="#ffb6c1" /> <ellipse cx="80" cy="90" rx="10" ry="22" fill="blue" /> <ellipse cx="80" cy="88" rx="10" ry="15" fill="black" /> <ellipse cx="80" cy="82" rx="8" ry="12" fill="#ffffff" /> <ellipse cx="80" cy="90" rx="10" ry="22" fill="#00000000" stroke="#000000" strokeWidth="4" /> <ellipse cx="120" cy="90" rx="10" ry="22" fill="blue" /> <ellipse cx="120" cy="88" rx="10" ry="15" fill="black" /> <ellipse cx="120" cy="82" rx="8" ry="12" fill="#ffffff" /> <ellipse cx="120" cy="90" rx="10" ry="22" fill="#00000000" stroke="#000000" strokeWidth="4" /> <ellipse cx="60" cy="110" rx="8" ry="5" fill="#ff4466" /> <ellipse cx="140" cy="110" rx="8" ry="5" fill="#ff4466" /> <path d="M 90 118 Q 100 125, 110 118" stroke="black" strokeWidth="3" fill="transparent" /> </svg> `; } // 添加卡比覆盖图层到指定卡片上 function addKirbyOverlay(card, invert = false) { const kirbyWrapper = document.createElement("div"); if (card.querySelector("#bilibili-blacklist-kirby") != null) return; kirbyWrapper.innerHTML = getKirbySVG(); kirbyWrapper.id = "bilibili-blacklist-kirby"; // 设置整体遮罩样式(背景 + 毛玻璃) const justifyContent = isVideoPage() ? "flex-start" : "center"; const alignItems = isVideoPage() ? "flex-start" : "center"; Object.assign(kirbyWrapper.style, { position: "absolute", top: "0", left: "0", width: "100%", height: "100%", pointerEvents: "none", display: "flex", justifyContent: `${justifyContent}`, alignItems: `${alignItems}`, zIndex: "10", backgroundColor: "rgba(255, 255, 255, 0.5)", // 背景 backdropFilter: "blur(2px)", WebkitBackdropFilter: "blur(2px)", borderRadius: "inherit", border: "1px solid rgba(255, 255, 255, 0.5)", }); const svg = kirbyWrapper.querySelector("svg"); if (svg) { const cardRect = card.getBoundingClientRect(); const size = Math.min(cardRect.width, cardRect.height) * 1.0; svg.setAttribute("width", `${size}px`); svg.setAttribute("height", `${size}px`); svg.setAttribute("bottom", `${cardRect.height - size}px`); // 设置卡比图标本身样式(可反色 + 半透明) svg.style.opacity = "0.15"; // 仅图标本体半透明 svg.style.filter = invert ? "invert(1)" : "none"; if (isVideoPage()) { svg.style.marginTop = "-10px"; } else { svg.style.marginTop = "-40px"; } } const cardStyle = getComputedStyle(card); if (cardStyle.position === "static" || !cardStyle.position) { card.style.position = "relative"; } card.appendChild(kirbyWrapper); } function removeKirbyOverlay(card) { const kirbyWrapper = card.querySelector("#bilibili-blacklist-kirby"); if (kirbyWrapper) { kirbyWrapper.remove(); } } //#endregion //########################## //#region 观察者 // MutationObserver 检测动态加载的新内容(仅当节点可见时才触发) const observer = new MutationObserver((mutations) => { let shouldCheck = false; if (isVideoPage()) { mutations.forEach((mutation) => { if (mutation.addedNodes.length > 0) { // 检查新增节点是否可见(有宽度或高度) shouldCheck = Array.from(mutation.addedNodes).some((node) => { // 仅检查元素节点(跳过文本节点、注释等) if (node.nodeType !== Node.ELEMENT_NODE) return false; // 检查元素或其子元素是否可见 const hasVisibleContent = node.offsetWidth > 0 || node.offsetHeight > 0 || node.querySelector("[offsetWidth], [offsetHeight]"); return hasVisibleContent; }); } }); } else { mutations.forEach((mutation) => { if (mutation.addedNodes.length > 0) { shouldCheck = true; } }); } // 如果有新内容,延迟后执行 BlockCard。 // 使用 setTimeout 确保 DOM 稳定后再扫描,并结合 `blockScanInterval` 控制频率 if (shouldCheck) { // 如果当前有大量卡片在队列中等待处理,或者 BlockCard 正在运行,可以考虑不立即触发新的扫描 // 这里的 `isBlocking` 已经通过 `lastBlockExecutionTime` 进行了控制 setTimeout(() => { BlockCard(); // 触发 BlockCard 将新卡片添加到队列并立即隐藏 if (isMainPage()) { BlockMainAD(); // 屏蔽页面广告 } if (isVideoPage()) { BlockVideoPageAd(); // 屏蔽视频页面广告 } // 确保黑名单管理按钮存在 if (!document.getElementById("bilibili-blacklist-manager")) { addBlacklistManagerButton(); } }, globalConfig.blockScanInterval); // 使用配置的扫描间隔 } }); // 初始化观察者(监视 DOM 变化) let observerError = 0; function initObserver(container) { const rootNode = document.getElementById(container) || document.querySelector(container) || document.documentElement; // 回退到整个文档 if (rootNode) { observer.observe(rootNode, { childList: true, // 监视添加/移除的节点 subtree: true, // 监视所有后代 }); return true; } else { // 如果没找到根节点则重试 setTimeout(() => initObserver(container), 500); console.warn("[bilibili-blacklist] 未找到根节点,正在重试..."); observerError++; if (observerError > 10) { console.error("[bilibili-blacklist] 重试次数过多,停止重试。"); return false; } } } //#endregion //#region 初始化函数 function init() { // 重置状态 //if (isInit) return; isBlocking = false; lastBlockExecutionTime = 0; blockedCards = new Set(); // 使用 Set 存储已屏蔽的卡片 cardProcessQueue = new Set(); // 初始化时清空队列 processedCards = new WeakSet(); // 初始化时清空已处理卡片 if (isMainPage()) { initMainPage(); // 初始化主页 BlockMainAD(); // 屏蔽主页广告 } else if (isSearchPage()) { initSearchPage(); // 初始化搜索页 } else if (isVideoPage()) { initVideoPage(); // 初始化播放页 //BlockVideoPageAd(); // 屏蔽视频页面广告 } else if (isCategoryPage()) { initCategoryPage(); // 初始化分类页 } else if (isUserSpace()) { initUserSpace(); // 初始化用户空间 //return; // 用户空间不需要屏蔽 } else { return; // 如果不是已知页面则不执行 } createBlacklistPanel(); console.log("[bilibili-blacklist] 脚本已加载🥔"); } // 监听页面加载完成事件 document.addEventListener("DOMContentLoaded", init); if ( document.readyState === "complete" || document.readyState === "interactive" ) { init(); } // 检查当前页面是否为B站主页 function isMainPage() { return location.pathname === "/"; } function initMainPage() { initObserver("feedchannel-main"); // 传入B站主页的主容器ID console.log("[bilibili-blacklist] 主页已加载🍓"); } /// -----搜索页---- function isSearchPage() { return location.hostname === "search.bilibili.com"; } function initSearchPage() { initObserver("i_cecream"); console.log("[bilibili-blacklist] 搜索页已加载🍉"); } /// --- 播放页 --- function isVideoPage() { return location.pathname.startsWith("/video/"); } function initVideoPage() { initObserver("right-container"); console.log("[bilibili-blacklist] 播放页已加载🍇"); } // ---- 分类页 ---- function isCategoryPage() { return location.pathname.startsWith("/c/"); } function initCategoryPage() { initObserver("app"); console.log("[bilibili-blacklist] 分类页已加载🍊"); } ///---用户空间--- function isUserSpace() { return location.hostname === "space.bilibili.com"; } function initUserSpace() { console.log("[bilibili-blacklist] 用户空间已加载🍎"); const upNameSelector = "#h-name, .nickname"; const observerForUpName = new MutationObserver((mutations, observer) => { const upNameElement = document.querySelector(upNameSelector); if (upNameElement) { observer.disconnect(); addBlockButtonToUserSpace(upNameElement); } }); observerForUpName.observe(document.body, { childList: true, subtree: true, }); const initialUpNameElement = document.querySelector(upNameSelector); if (initialUpNameElement) { observerForUpName.disconnect(); addBlockButtonToUserSpace(initialUpNameElement); } } function addBlockButtonToUserSpace(upNameElement) { const upName = upNameElement.textContent.trim(); //console.log(`当前用户昵称: ${upName}`); if (upNameElement.querySelector(".bilibili-blacklist-up-block-btn")) { return; } // 设置 inline-flex,让文字和按钮一行显示 upNameElement.style.display = "inline-flex"; upNameElement.style.alignItems = "center"; const button = document.createElement("button"); button.className = "bilibili-blacklist-up-block-btn"; button.textContent = "屏蔽"; button.style.color = "#fff"; button.style.width = "100px"; button.style.height = "30px"; button.style.marginLeft = "10px"; button.style.borderRadius = "5px"; button.style.border = "1px solid #fb7299"; const refreshButtonStatus = () => { const blocked = isBlacklisted(upName); if (blocked) { button.textContent = "已屏蔽"; button.style.backgroundColor = "#dddddd"; button.style.border = "1px solid #ccc"; upNameElement.style.textDecoration = "line-through"; // 添加删除线效果 document.body.classList.add("bilibili-blacklist-grayscale"); } else { button.textContent = "屏蔽"; button.style.backgroundColor = "#fb7299"; button.style.border = "1px solid #fb7299"; upNameElement.style.textDecoration = "none"; // 移除删除线效果 document.body.classList.remove("bilibili-blacklist-grayscale"); } }; button.addEventListener("click", (e) => { e.stopPropagation(); const blocked = isBlacklisted(upName); if (blocked) { removeFromExactBlacklist(upName); } else { addToExactBlacklist(upName); } refreshButtonStatus(); }); refreshButtonStatus(); // Set initial button state upNameElement.appendChild(button); } //#endregion //#region 额外功能-屏蔽广告 // 屏蔽广告 function BlockMainAD() { if (!globalConfig.flagAD) return; const adSelectors = [ ".floor-single-card", // 分区推荐 ".bili-live-card", // 直播推广 ".btn-ad", // 广告按钮 ]; adSelectors.forEach((selector) => { document.querySelectorAll(selector).forEach((adCard) => { //adCard.remove(); hideCard(adCard); }); }); } // 屏蔽视频页面广告(使用数组优化) function BlockVideoPageAd() { if (!globalConfig.flagAD) return; const adSelectors = [ ".video-card-ad-small", // 右上角推广 ".slide-ad-exp", // 大推广 ".video-page-game-card-small", // 游戏推广 ".activity-m-v1", // 活动推广 ".video-page-special-card-small", // 特殊卡片推广 ".ad-floor-exp", // 广告地板 ".btn-ad", // 广告按钮 ]; adSelectors.forEach((selector) => { document.querySelectorAll(selector).forEach((adCard) => { //adCard.remove(); hideCard(adCard); }); }); } //#endregion })();