// ==UserScript== // @name BiliCleaner // @namespace https://greasyfork.org/scripts/511437/ // @description 隐藏B站动态瀑布流中的广告、评论区广告、充电内容以及美化首页 // @version 1.32 // @author chemhunter // @match *://t.bilibili.com/* // @match *://space.bilibili.com/* // @match *://www.bilibili.com/* // @match *://live.bilibili.com/* // @match *://message.bilibili.com/* // @connect www.gitlabip.xyz // @connect hub.gitmirror.com // @connect raw.githubusercontent.com // @grant GM_registerMenuCommand // @icon https://i0.hdslb.com/bfs/static/jinkela/long/images/favicon.ico // @license GPL-3.0 License // @run-at document-end // @noframes // @downloadURL https://update.greasyfork.icu/scripts/511437/BiliCleaner.user.js // @updateURL https://update.greasyfork.icu/scripts/511437/BiliCleaner.meta.js // ==/UserScript== (function() { 'use strict'; // --- 新增:声明全局变量 --- let keywordRegex, keywordRegexGlobal, biliAdWordsConfig, whiteList, messageDiv; let commentAppObserver, dynamicPanelObserver, panelCardObserver; let lastPathname = ''; let hiddenAdCount = 0; let lastActiveUpName = null; let setMainWidth = false; const defaultConfig = { keywordStr: `淘宝|京东|天猫|补贴|折扣|福利|专属|下单|运(费?)险|[领惠叠]券|[低特好底保降差性]价`, biliAdLinks: ['taobao.com', 'tb.cn', 'jd.com', 'pinduodilo.com','zhuanzhuan.com', 'mall.bilibili.com', 'gaoneng.bilibili.com'], time: 0 }; async function fetchConfigFromGit() { let lastError = null; const gitSource = ['www.gitlabip.xyz', 'hub.gitmirror.com', 'raw.githubusercontent.com']; //github镜像加速及源地址 const jsonFile = '/chemhunter/biliadskip/refs/heads/main/biliadwordslinks.json'; for (const source of gitSource) { const url = `https://${source}${jsonFile}?t=${Date.now()}`; try { const response = await fetch(url); if (!response.ok) {throw new Error(`HTTP错误! 状态码: ${response.status}`)}; const text = await response.text(); try { const configData = JSON.parse(text); log(`✅ 从git镜像: ${source} 获取到广告基础配置`); return configData; } catch (parseError) { throw new Error(`JSON解析失败: ${parseError.message}`); } } catch (error) { lastError = error; continue;} } throw new Error(`所有镜像源均无法访问: ${lastError?.message || '未知错误'}`); } async function getConfigWithFallback(maxRetries = 2) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const res = await fetchConfigFromGit(); return res; } catch (error) { console.error(`尝试 ${attempt} 失败:`, error.message); if (attempt === maxRetries) { console.warn('⚠️ 所有尝试均失败,使用默认配置'); return; } await new Promise(resolve => setTimeout(resolve, 500 * attempt)); } } return; } async function getAdWordsConfig() { try { const localConfigStr = localStorage.getItem("localConfig"); const localConfig = localConfigStr ? JSON.parse(localConfigStr) : null; const lastUpdateTime = localConfig && localConfig.time || 0; if ( Date.now() - lastUpdateTime >= 3600 * 24 * 1000) { const res = await getConfigWithFallback(); if (res) { log(`⚙️ 配置信息:`, res); biliAdWordsConfig = { ...res, keywordStr: Object.values(res.keywordStr).join('|'), time: Date.now() }; localStorage.setItem("localConfig", JSON.stringify(biliAdWordsConfig)); } } else { log(`读取本地广告词缓存`); biliAdWordsConfig = {...localConfig}; if (!biliAdWordsConfig.time) { biliAdWordsConfig = defaultConfig; } } } catch (error) { console.error("获取广告词配置失败:", error); } keywordRegex = new RegExp(biliAdWordsConfig.keywordStr.replace(/\s+/g, ''), 'i'); keywordRegexGlobal = new RegExp(biliAdWordsConfig.keywordStr.replace(/\s+/g, ''), 'gi'); } function log(...args) { console.log('[BiliCleaner] ', ...args); } function hideItem(element) { if (element && element.style.display !== 'none') { element.style.display = 'none'; } } function showMessage(msg) { messageDiv.textContent = msg; messageDiv.style.display = 'block'; setTimeout(() => { messageDiv.style.display = 'none'; }, 3000); } function hideUnwantedElements() { // 分组规则:按页面类型 const rules = { common: [ 'li.v-popover-wrap.left-loc-entry', //上方导航条末尾广告 'ul.left-entry > li.v-popover-wrap:last-child', // 上方导航条最后的“下载客户端” 'ul.right-entry > .vip-wrap', //顶部右侧 大会员按钮 ], video: [ ".video-page-game-card-small", //右侧栏推荐视频列表上方广告 '.video-page-special-card-small', //右侧卡片栏混入的特殊卡片链接 '.video-share-wrap', // 视频页面分享按钮 '.video-card-ad-small', // 弹幕列表下方 视频卡片形式的小广告 '.slide-ad-exp', //右侧上方弹幕列表下方的广告块 '.ad-report.strip-ad', // 视频下方 广告上报/“不喜欢”按钮 '.activity-m-v1', //评论区上方活动推广条 '.bili-mini-mask', // 视频区域的登录提示遮罩 ], dynamic: [ 'bili-dyn-home--member .right', '.bili-dyn-banner', //动态右侧社区公告 //'.bili-dyn-search-trendings',//动态右侧热搜,毫无营养 '.reply-notice', //动态页面评论区上方提醒条 ".bili-dyn-version-control__reminding", //动态页面新版导航提醒 ], live: [ "gift-control-vm", //直播界面下方送礼栏 ".gift-control-section", //直播界面下方送礼栏 '.room-info-ctnr', //直播界面下面推荐直播4x2 "rank-list-vm", //直播界面上方榜单 ".rank-list-section", //直播界面上方榜单 ] }; const { hostname, pathname } = location; const isVideoPage = pathname.startsWith('/video/'); const isDynamicPage = hostname === 't.bilibili.com' || pathname.startsWith('/opus/'); const isLivePage = hostname === 'live.bilibili.com' || pathname.startsWith('/live/'); let selectorsToApply = [...rules.common]; if (isVideoPage) { selectorsToApply.push(...rules.video); } else if (isDynamicPage) { selectorsToApply.push(...rules.dynamic); } else if (isLivePage) { selectorsToApply.push(...rules.live); } for (const selector of selectorsToApply) { const element = document.querySelector(selector); if (element) { if (selector === '.bili-mini-mask') { if (window.getComputedStyle(element).display !== 'none') { hideItem(element); } } else { hideItem(element); } } } } // 检查评论区置顶广告 function checkCommentTopAdsOld() { const commentAds = document.querySelectorAll('.dynamic-card-comment .comment-list.has-limit .list-item.reply-wrap.is-top'); commentAds.forEach(comment => { const links = comment.querySelectorAll('a'); links.forEach(link => { const href = link.getAttribute('href'); if (href && biliAdWordsConfig.biliAdLinks.some(blocked => href.includes(blocked))) { hideItem(comment); log('评论区置顶广告+1(链接)') return true; } }); }); return false; } // 新版本动态 commentapp or bili-dyn-comment // 旧版本动态 dynamic-card-comment function checkCommentsForAds() { const dynCommentOldVersion = document.querySelector('.dynamic-card-comment'); if (dynCommentOldVersion) { const result = checkCommentTopAdsOld(); if (result) { hiddenAdCount++; } return result; } const commentsContainer = document.querySelector('#commentapp > bili-comments') || document.querySelector('.bili-dyn-comment > bili-comments'); if (commentsContainer && commentsContainer.shadowRoot) { const headerElement = commentsContainer.shadowRoot.querySelector("#header > bili-comments-header-renderer"); if (headerElement && headerElement.shadowRoot) { const noticeElement = headerElement.shadowRoot.querySelector("#notice > bili-comments-notice"); if (noticeElement && noticeElement.shadowRoot) { const closeElement = noticeElement.shadowRoot.querySelector("#close"); if (closeElement) { log("评论区横条,自动关闭"); closeElement.click(); } } } const thread = commentsContainer.shadowRoot.querySelector('bili-comment-thread-renderer'); if (thread && window.getComputedStyle(thread).display !== 'none' && thread.shadowRoot) { const commentRenderer = thread.shadowRoot.querySelector('#comment'); if (commentRenderer && commentRenderer.shadowRoot) { const richText = commentRenderer.shadowRoot.querySelector('#content > bili-rich-text'); if (richText && richText.shadowRoot) { const contentsElement = richText.shadowRoot.querySelector('#contents'); if (contentsElement) { let foundAd; const links = contentsElement.querySelectorAll('a'); links.forEach(link => { const href = link.getAttribute('href'); if (href && biliAdWordsConfig.biliAdLinks.some(blocked => href.includes(blocked))) { foundAd = true; } }); if (!foundAd) { const commentText = contentsElement.textContent.trim(); foundAd = findAdwords(commentText) } if (foundAd) { //log('发现广告:', contentText); hideItem(thread); hiddenAdCount++; log('评论区置顶广告 +1') window.MyObserver.disconnect(); let message = `隐藏广告 x ${hiddenAdCount}`; showMessage(message); return true; } } } } } } return false; } function findAdwords(text) { const notAd = ['评论','评论区','产品'] const matches = text.match(keywordRegexGlobal); if (!matches) return false; return matches.some(match => !notAd.includes(match)); } function processFeedCards() { const adSpans = document.querySelectorAll('span.bili-video-card__stats--text'); adSpans.forEach(span => { if (span.textContent.trim() === '广告') { const targetCard = span.closest('.bili-feed-card') || span.closest('.feed-card'); if (targetCard) { hideItem(targetCard); } } }); const allFeedCards = document.querySelectorAll('.feed-card'); allFeedCards.forEach(card => { const hasVideoWrap = card.querySelector('.bili-video-card__wrap'); if (!hasVideoWrap) { hideItem(card); return; } }); } function logCurrentActiveUp() { if (window.location.hostname === 't.bilibili.com') { const upListContainer = document.querySelector('.bili-dyn-up-list__window'); if (!upListContainer) { return; } const activeUpElement = document.querySelector('.bili-dyn-up-list__item.active .bili-dyn-up-list__item__name'); let currentActiveUpName = null; if (activeUpElement) { currentActiveUpName = activeUpElement.textContent.trim(); } else { // 检查是否是“全部动态”被激活 const allDynamicActive = document.querySelector('.bili-dyn-up-list__item.active .bili-dyn-up-list__item__face.all'); if (allDynamicActive) { currentActiveUpName = '全部动态'; } } // 只有当当前激活的UP主与上次不同时才输出日志 if (currentActiveUpName && currentActiveUpName !== lastActiveUpName) { const inWhiteList = whiteList.includes(currentActiveUpName)? " (白名单)" : ''; console.log( `[BiliCleaner] UP: %c${currentActiveUpName}${inWhiteList}`, 'background: #009688; color: #fff; padding: 2px 5px; border-radius: 2px; font-weight: bold;' ); lastActiveUpName = currentActiveUpName; } else if (!currentActiveUpName && lastActiveUpName !== null) { lastActiveUpName = null; } } else { if (lastActiveUpName !== null) { lastActiveUpName = null; } } } function checkForContentToHide() { let hiddenChargeCount = 0; let hiddenAdCount = 0; const hostname = window.location.hostname; const pathname = window.location.pathname; hideUnwantedElements() //B站首页 if (hostname === 'www.bilibili.com' && !pathname.startsWith('/video/')) { // 处理 feed 卡片 processFeedCards() // 隐藏推广的 feed 卡片 const floorCards = document.querySelectorAll('.floor-single-card'); floorCards.forEach(card => { hideItem(card); }); //隐藏首页大屏 const targetElement = document.querySelector('.recommended-swipe'); hideItem(targetElement); //动态和个人空间页面 } else if (hostname === 't.bilibili.com' || hostname === 'space.bilibili.com') { if (hostname === 't.bilibili.com') { logCurrentActiveUp(); if (!setMainWidth) { const dynMain = document.querySelector('.bili-dyn-home--member > main') if (dynMain) { const currentWidth = parseInt(getComputedStyle(dynMain).width, 10); dynMain.style.width = (currentWidth + 175) + 'px'; setMainWidth = true; } } } checkCommentsForAds(); const items = document.querySelectorAll('.bili-dyn-list__item'); items.forEach(item => { if (window.getComputedStyle(item).display !== 'none') { const titleElement = item.querySelector('.bili-dyn-title'); if (titleElement && whiteList.includes(titleElement.textContent.trim())) { return; } const goods = item.querySelector('bili-dyn-card-goods') || item.querySelector('dyn-goods'); if (goods) { hideItem(item); log('广告卡片 +1') hiddenAdCount++; return; } let charge = null; const blockedmask = item.querySelector('.dyn-blocked-mask'); if (blockedmask) { charge = true; } else { const badge = item.querySelector('.bili-dyn-card-video__badge'); if (badge && (badge.textContent.includes('专属') || badge.textContent.includes('抢先看'))) { charge = true; } else { const lotteryTitle = item.querySelector('.dyn-upower-lottery__title'); if (lotteryTitle && lotteryTitle.textContent.includes('专属')) { charge = true; } else { const upower = item.querySelector(".bili-dyn-upower-common"); const ask = item.querySelector('.dyn-icon-badge__renderimg.bili-dyn-item__iconbadge'); if (ask || upower) { charge = true; } } } } if (charge) { const titleElement = item.querySelector('.bili-dyn-card-video__title'); if (titleElement) { const videoTitle = titleElement.textContent.trim(); log(`充电专属 +1: \n ----->"${videoTitle}"`); } else { log('充电专属 +1'); } hideItem(item); hiddenChargeCount++; return; } // 辅助函数:在指定容器中检查广告并隐藏 function checkAndHideAd(container, type) { const richtext = container.querySelector('.bili-rich-text .bili-rich-text__content')?.textContent?.trim(); if ( richtext && richtext.length >= 40 ) { const foundAd = findAdwords(richtext); if (foundAd) { log(`广告关键词 +1(${type}) \n ----> ${richtext.slice(0,30)}`); hideItem(item); hiddenAdCount++; return true; } } return false; } const contentDiv = item.querySelector('.bili-dyn-content'); if (!contentDiv) return; // 尝试在转发内容中查找 const origContent = contentDiv.querySelector('.bili-dyn-content__orig.reference'); if (origContent) { if (checkAndHideAd(origContent, '转发')) return; } // 尝试在原创内容中查找 if (checkAndHideAd(contentDiv, '原创')) return; const spans = item.querySelectorAll('span'); spans.forEach(span => { const dataUrl = span.getAttribute('data-url'); if (dataUrl && biliAdWordsConfig.biliAdLinks.some(blocked => dataUrl.includes(blocked))) { hideItem(item); log('广告链接 +1') hiddenAdCount++; } else if (span.textContent.includes('专属')) { hideItem(item); log('充电专属 +1') hiddenChargeCount++; return; } }); } }); //视频页面 } else if (pathname.startsWith('/video/BV')) { if (!checkCommentsForAds()) { setTimeout(() => { checkCommentsForAds(); }, 2000); } } let message = ''; if (hiddenChargeCount > 0) { message += `隐藏充电 x ${hiddenChargeCount} `; } if (hiddenAdCount > 0) { message += `隐藏广告 x ${hiddenAdCount} `; } if (message) { showMessage(message.trim()); } else { logCurrentActiveUp(); } } // 元素是否可见 function isVisible(el) { return el && el.offsetParent !== null; } /** 过滤单个动态卡片链接 */ function filterSingleDynamicLink(linkElement) { if (!isVisible(linkElement)) return; const title = linkElement.getAttribute('title') || ''; const tagSpan = linkElement.querySelector('.all-in-one-article-title > .article-tag'); const isArticle = tagSpan && tagSpan.textContent.trim() === '专栏'; if (isArticle && keywordRegex.test(title)) { const authorElement = linkElement.querySelector('.user-name a[title]'); const author = authorElement ? authorElement.getAttribute('title') : '未知作者'; log(`🚫 [动态弹窗] 广告卡片: 「${author}」- ${title.slice(0, 20)}...`); linkElement.style.display = 'none'; } } function watchDynamicAllPanel() { const containerObserver = new MutationObserver(mutations => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType !== 1) continue; const panel = node.matches('.dynamic-all') ? node : node.querySelector('.dynamic-all'); if (panel) { log('✅ .dynamic-all 面板已插入,启动内部卡片观察器'); panel.querySelectorAll('a[data-mod="top_right_bar_window_dynamic"]').forEach(filterSingleDynamicLink); if (panelCardObserver) panelCardObserver.disconnect(); panelCardObserver = new MutationObserver(cardMutations => { for (const cardMutation of cardMutations) { for (const addedCard of cardMutation.addedNodes) { if (addedCard.nodeType === 1 && addedCard.matches('a[data-mod="top_right_bar_window_dynamic"]')) { filterSingleDynamicLink(addedCard); } } } }); panelCardObserver.observe(panel, { childList: true }); break; } } // 2. 处理节点移除的情况 for (const node of mutation.removedNodes) { if (node.nodeType !== 1) continue; if ((node.matches('.dynamic-all') || node.querySelector('.dynamic-all')) && panelCardObserver) { log('⚪️ .dynamic-all 面板已移除,断开内部卡片观察器'); panelCardObserver.disconnect(); panelCardObserver = null; break; } } } }); const rightEntry = document.querySelector('.right-entry'); if (rightEntry) { containerObserver.observe(rightEntry, { childList: true, subtree: true }); log('✅ 已启动动态弹窗的外部观察器'); } } function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } function initObserver() { const mainObserver = new MutationObserver(debounce(checkForContentToHide, 250)); mainObserver.observe(document.body,{ childList: true, subtree: true, });//attributes: true, attributeFilter: ['class'] return mainObserver; } function restartMainObserver() { log('页面内容更新,重启观察器'); if (window.MyObserver) { window.MyObserver.disconnect(); } const mainObserver = initObserver(); window.MyObserver = mainObserver; } // 监听 commentapp 元素的变化 function initCommentAppObserver() { const commentApp = document.querySelector('#commentapp'); if (commentApp) { commentAppObserver = new MutationObserver(restartMainObserver); commentAppObserver.observe(commentApp, { childList: true, subtree: true, attributes: true, attributeFilter: ['class']}); log('启动观察commentapp'); } } /** 显示白名单管理菜单*/ async function whiteListMenu() { // 添加到白名单 function addToWhiteList(upId) { if (!whiteList.includes(upId)) { whiteList.push(upId); localStorage.setItem('biliUpWhiteList', JSON.stringify(whiteList)); updateWhiteListDisplay(); } else { alert(`${upId} 已在白名单中`); } } // 从白名单中移除 function removeFromWhiteList(upId) { const index = whiteList.indexOf(upId); if (index !== -1) { whiteList.splice(index, 1); localStorage.setItem('biliUpWhiteList', JSON.stringify(whiteList)); updateWhiteListDisplay(); } else { alert(`${upId} 不在白名单中`); } } // 更新白名单显示 function updateWhiteListDisplay() { const listDisplay = document.getElementById('whiteListDisplay'); if (listDisplay) { listDisplay.textContent = whiteList.join(', ') || '白名单为空'; } const currentUserRow = document.getElementById('bili-current-up-display'); const upInfo = getUpInfo(); if (currentUserRow) { if (upInfo && upInfo.name) { currentUserRow.innerHTML = `当前页面UP主: ${upInfo.name}`; } else { currentUserRow.innerHTML = ''; } } // 2. 【核心新增】更新“添加/移除当前页UP”按钮的状态 const currentUpBtn = document.getElementById('bili-add-current-up-btn'); if (currentUpBtn) { const upInfo = getUpInfo(); if (upInfo && upInfo.name) { currentUpBtn.style.display = ''; if (whiteList.includes(upInfo.name)) { currentUpBtn.textContent = `移除当前UP`; currentUpBtn.style.backgroundColor = '#e74c3c'; // 红色 } else { currentUpBtn.textContent = `添加当前UP`; currentUpBtn.style.backgroundColor = '#2eac31'; // 绿色 } } else { currentUpBtn.style.display = 'none'; } } } function getUpInfo() { const isSpacePage = window.location.href.match(/space.bilibili.com\/(\d+)/); const isVideoPage = window.location.href.includes('/video/BV'); const container = isSpacePage ? document.querySelector('.upinfo__main') : document.querySelector('.up-panel-container'); if (isVideoPage) { // 单个UP主的情况 const singleUp = container.querySelector('.up-detail .up-detail-top .up-name'); if (singleUp) { const clone = singleUp.cloneNode(true); clone.querySelectorAll('span').forEach(span => span.remove()); const name = clone.textContent.trim(); const href = singleUp.getAttribute('href'); const idMatch = href.match(/space\.bilibili\.com\/(\d+)/); const id = idMatch ? idMatch[1] : null; return { name, id}; } else { //多个UP的情况 const allMemberCards = container.querySelectorAll('.membersinfo-upcard'); const firstUpCard = allMemberCards[0]; if (firstUpCard) { const nameElement = firstUpCard.querySelector('.staff-name'); const name = nameElement ? nameElement.textContent.trim() : null; const idElement = firstUpCard.querySelector('a[href*="space.bilibili.com"]'); const href = idElement ? idElement.getAttribute('href') : null; const id = href ? href.match(/\/\/space\.bilibili\.com\/(\d+)/)?.[1] : null; return { name, id}; } } } return null; } const UpWhiteListContainer = document.createElement('div'); UpWhiteListContainer.id = 'UpWhiteListContainer'; UpWhiteListContainer.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 500px; padding: 20px; background: #fff; border: 1px solid #ccc; border-radius: 10px; z-index: 10000; font-size: 16px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); `; const Title = document.createElement('h3'); Title.textContent = `手动管理白名单(跳过检测)`; Title.style.cssText = 'text-align: center; margin-bottom: 20px; font-weight: bold; cursor: move; user-select: none;'; UpWhiteListContainer.appendChild(Title); const toggleUpRow = document.createElement('div'); toggleUpRow.style.cssText = `display: flex; align-items: center; margin-bottom: 10px; gap: 10px;`; const toggleUpLabel = document.createElement('label'); toggleUpLabel.textContent = '添加/移除UP主:'; toggleUpLabel.style.cssText = `flex-shrink: 0;`; // 为“执行”按钮绑定智能的切换逻辑 const handleToggle = () => { const upName = toggleUpInput.value.trim(); if (!upName) return; if (whiteList.includes(upName)) { removeFromWhiteList(upName); } else { addToWhiteList(upName); } toggleUpInput.value = ''; }; const toggleUpInput = document.createElement('input'); toggleUpInput.type = 'text'; toggleUpInput.id = 'toggleUpInput'; toggleUpInput.placeholder = '输入UP主昵称'; toggleUpInput.style.cssText = 'flex-grow: 1; min-width: 200; max-width: 240px; border: 1px solid #ccc;'; toggleUpInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') handleToggle(); }); const toggleButton = createButton('执行', handleToggle); toggleButton.style.minWidth = '80px'; toggleUpRow.appendChild(toggleUpLabel); toggleUpRow.appendChild(toggleUpInput); toggleUpRow.appendChild(toggleButton); UpWhiteListContainer.appendChild(toggleUpRow); // 白名单列表显示区域 const listDiv = document.createElement('div'); listDiv.id = 'whiteListDisplay'; listDiv.style.cssText = ` text-align: left; color: #30b688; margin: 20px 0; padding: 5px; border: 1px dashed #ccc; border-radius: 5px; font-size: 14px; word-break: break-word; max-height: 150px; overflow-y: auto;`; listDiv.textContent = whiteList.join(', ') || '白名单为空'; UpWhiteListContainer.appendChild(listDiv); const currentUpInfo = getUpInfo(); if (currentUpInfo && currentUpInfo.name) { const currentUserRow = document.createElement('div'); currentUserRow.id = 'bili-current-up-display'; currentUserRow.style.cssText = `text-align: center; font-size: 16px; color: #555; margin: 5px 0; padding: 5px;`; UpWhiteListContainer.appendChild(currentUserRow); } const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = 'display: flex; justify-content: center; margin: 10px 0; gap: 10px'; function createButton(text, onClick) { const button = document.createElement('button'); button.textContent = text; button.style.cssText = `padding: 3px 3px; border: 1px solid #ccc; background: #f0f0f0; border-radius: 4px; cursor: pointer; font-size: 14px;`; if (onClick) button.onclick = onClick; return button; } const finishButton = createButton('关闭界面', () => { document.body.removeChild(UpWhiteListContainer); }) if (currentUpInfo && currentUpInfo.name) { const upName = currentUpInfo.name; const toggleCurrentUpButton = document.createElement('button'); toggleCurrentUpButton.id = 'bili-add-current-up-btn'; toggleCurrentUpButton.style.cssText = `color: white; padding: 4px 5px; margin: 0 5px; border: none; border-radius: 4px;`; toggleCurrentUpButton.addEventListener('click', () => { if (whiteList.includes(upName)) { removeFromWhiteList(upName); } else { addToWhiteList(upName); } }); buttonContainer.appendChild(toggleCurrentUpButton); } buttonContainer.appendChild(finishButton); UpWhiteListContainer.appendChild(buttonContainer); document.body.appendChild(UpWhiteListContainer); updateWhiteListDisplay(); } function prepareForWork() { // 初始化白名单 const oldList = localStorage.getItem('whiteList'); if (oldList) {localStorage.setItem('biliUpWhiteList', oldList);localStorage.removeItem('whiteList');} whiteList = JSON.parse(localStorage.getItem('biliUpWhiteList')) || []; // 注册菜单命令 GM_registerMenuCommand("UP白名单", whiteListMenu); //创建提醒元素 messageDiv = document.createElement('div'); Object.assign(messageDiv.style, { position: 'fixed', top: '10px', right: '10px', padding: '10px', backgroundColor: 'rgba(0, 0, 0, 0.7)', color: 'white', borderRadius: '5px', zIndex: '9999', display: 'none' }); document.body.appendChild(messageDiv); lastPathname = window.location.pathname; } function reinitializeAllObservers() { log('执行观察器初始化...'); // 1. 断开所有可能存在的旧观察器 if (window.MyObserver) window.MyObserver.disconnect(); if (commentAppObserver) commentAppObserver.disconnect(); if (dynamicPanelObserver) dynamicPanelObserver.disconnect(); // 2. 重新运行所有初始化逻辑 window.MyObserver = initObserver(); initCommentAppObserver(); watchDynamicAllPanel(); checkForContentToHide(); } // 导航观察器 --- function setupNavigationObserver() { const observer = new MutationObserver(() => { const currentPathname = window.location.pathname; if (!lastPathname || currentPathname !== lastPathname) { if (lastPathname) log(`检测到页面导航: ${lastPathname} -> ${currentPathname}`); lastPathname = currentPathname; debounce(reinitializeAllObservers, 1500)(); } }); observer.observe(document.body, { childList: true, subtree: true }); log('✅ 主导航观察器已启动'); } async function initApp() { log('脚本加载,初始化'); await getAdWordsConfig(); prepareForWork(); reinitializeAllObservers(); setupNavigationObserver(); } initApp().catch(console.error); })();