B站首页和视频页一键拉黑UP主

在B站首页和视频页添加拉黑按钮,一键拉黑UP主。修复导航栏消失和首页按钮问题。

  1. // ==UserScript==
  2. // @name B站首页和视频页一键拉黑UP主
  3. // @description 在B站首页和视频页添加拉黑按钮,一键拉黑UP主。修复导航栏消失和首页按钮问题。
  4. // @match https://bilibili.com/
  5. // @match https://www.bilibili.com/*
  6. // @match https://www.bilibili.com/video/*
  7. // @icon https://www.bilibili.com/favicon.ico
  8. // @version 1.1.2
  9. // @grant GM_registerMenuCommand
  10. // @grant GM_unregisterMenuCommand
  11. // @grant GM_addStyle
  12. // @namespace https://github.com/codertesla/bilibili-1-click-blocker
  13. // @author codertesla
  14. // @supportURL https://github.com/codertesla/bilibili-1-click-blocker
  15. // @homepageURL https://github.com/codertesla/bilibili-1-click-blocker
  16. // @require https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js
  17. // @license MIT
  18. // ==/UserScript==
  19.  
  20. (function () {
  21. 'use strict';
  22.  
  23. // 调试功能
  24. const DEBUG = true;
  25. function log(...args) {
  26. if (DEBUG) {
  27. console.log('[拉黑脚本]', ...args);
  28. }
  29. }
  30.  
  31. // 等待jQuery加载
  32. function waitForJQuery(callback) {
  33. if (typeof jQuery !== 'undefined') {
  34. log('jQuery已加载');
  35. callback(jQuery);
  36. } else {
  37. log('等待jQuery加载...');
  38. setTimeout(function () { waitForJQuery(callback); }, 50);
  39. }
  40. }
  41.  
  42. // --- Debounce 函数 ---
  43. function debounce(func, wait) {
  44. let timeout;
  45. return function executedFunction(...args) {
  46. const later = () => {
  47. clearTimeout(timeout);
  48. func(...args);
  49. };
  50. clearTimeout(timeout);
  51. timeout = setTimeout(later, wait);
  52. };
  53. }
  54. // ---
  55.  
  56. // 主函数
  57. waitForJQuery(function ($) {
  58. log('脚本初始化开始 v1.0.8-fixed');
  59.  
  60. // 添加样式 (保持不变)
  61. if (typeof GM_addStyle !== 'undefined') {
  62. GM_addStyle(`
  63. /* 通用按钮样式 */
  64. .bilibili-blacklist-btn {
  65. color: #fb7299 !important;
  66. cursor: pointer !important;
  67. font-weight: normal !important;
  68. display: inline-flex !important;
  69. align-items: center !important;
  70. justify-content: center !important;
  71. padding: 1px 5px !important;
  72. border: 1px solid #fb7299 !important;
  73. border-radius: 4px !important;
  74. font-size: 11px !important; /* 统一基础字号 */
  75. transition: all 0.2s ease !important;
  76. background-color: white !important;
  77. box-shadow: 0 0 2px rgba(251, 114, 153, 0.2) !important;
  78. width: auto !important;
  79. min-width: unset !important;
  80. max-width: none !important;
  81. box-sizing: border-box !important;
  82. text-align: center !important;
  83. white-space: nowrap !important;
  84. gap: 1px !important;
  85. vertical-align: middle; /* 垂直对齐 */
  86. line-height: normal; /* 正常行高 */
  87. margin: 0 5px 0 0 !important; /* 默认右边距,给后面元素空间 */
  88. }
  89. .bilibili-blacklist-btn:hover {
  90. background-color: #fb7299 !important;
  91. color: white !important;
  92. box-shadow: 0 0 4px rgba(251, 114, 153, 0.4) !important;
  93. }
  94. .bilibili-blacklist-btn:active {
  95. transform: scale(0.95) !important;
  96. }
  97. .bilibili-blacklist-btn::before {
  98. content: "" !important; margin-right: 0 !important; width: 0 !important;
  99. }
  100. /* 按钮动画 */
  101. @keyframes fadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
  102. .bilibili-blacklist-btn { animation: fadeIn 0.3s ease-out !important; }
  103.  
  104. /* --- 首页特定调整 --- */
  105. /* 将按钮添加到 info--bottom 前面时 */
  106. .bili-video-card__info--bottom > .bilibili-blacklist-btn {
  107. /* 首页按钮放在前面,给右边距 */
  108. margin-right: 8px !important;
  109. margin-left: 0 !important;
  110. }
  111.  
  112. /* --- 视频页特定调整 --- */
  113. /* 将按钮添加到 upname > a > span.name 后面时 */
  114. .upname a > .bilibili-blacklist-btn {
  115. margin-left: 5px !important; /* 视频页按钮放名字后面,给左边距 */
  116. margin-right: 0 !important;
  117. font-size: 10px !important; /* 视频页按钮可以小一点 */
  118. padding: 0px 4px !important;
  119. }
  120.  
  121. /* --- 卡片悬浮按钮特定调整 --- */
  122. .blacklist-button-container {
  123. position: absolute !important; top: 5px !important; right: 5px !important; z-index: 100 !important;
  124. opacity: 0 !important; transition: opacity 0.2s ease !important;
  125. }
  126. .bili-video-card:hover .blacklist-button-container,
  127. .video-card:hover .blacklist-button-container,
  128. .feed-card:hover .blacklist-button-container {
  129. opacity: 1 !important;
  130. }
  131. .blacklist-button-container .bilibili-blacklist-btn {
  132. padding: 0px 4px !important; font-size: 10px !important;
  133. min-width: unset !important; max-width: none !important; white-space: nowrap !important;
  134. margin: 0 !important; /* 悬浮按钮不需要外边距 */
  135. }
  136.  
  137. /* --- Toast 提示样式 (保持不变) --- */
  138. .bili-blacklist-toast { /* ... 省略不变的样式 ... */ }
  139. @keyframes toastIn { /* ... 省略不变的样式 ... */ }
  140. .bili-blacklist-toast-icon { /* ... 省略不变的样式 ... */ }
  141. .bili-blacklist-toast-content { /* ... 省略不变的样式 ... */ }
  142. `);
  143. }
  144.  
  145.  
  146. // Cookie获取函数 (保持不变)
  147. function getCookie(name) {
  148. const value = `; ${document.cookie}`;
  149. const parts = value.split(`; ${name}=`);
  150. if (parts.length === 2) return parts.pop().split(';').shift();
  151. return '';
  152. }
  153.  
  154. // 检查是否登录(不可用) (保持不变)
  155. const csrf = getCookie('bili_jct');
  156. if (!csrf) {
  157. log('警告: 未获取到bili_jct Cookie,可能未登录(不可用)');
  158. showToast('B站拉黑脚本提示:请先登录(不可用)B站账号!', 5000);
  159. } else {
  160. log('成功获取CSRF token');
  161. }
  162.  
  163. // 菜单控制 (保持不变)
  164. const menuctl = ({ initValue = 0 }) => {
  165. let total = initValue;
  166. let menuId = null;
  167. const currentName = () => "去管理黑名单 --( " + (total < 1 ? "请留意黑名单数量 )" : `总共:${total} )`);
  168. const register = () => {
  169. try {
  170. menuId = GM_registerMenuCommand(currentName(), () => {
  171. window.open('https://account.bilibili.com/account/blacklist', '_blank');
  172. });
  173. log('注册(不可用)菜单成功: ', currentName());
  174. } catch (e) { log('注册(不可用)菜单失败: ', e); }
  175. };
  176. register();
  177. const ctl = {
  178. get total() { return total; },
  179. set total(newValue) {
  180. if (newValue == total) return;
  181. if (menuId !== null) { try { GM_unregisterMenuCommand(menuId); log('解除注册(不可用)旧菜单'); } catch (e) { log('解除注册(不可用)旧菜单失败: ', e); } }
  182. total = newValue;
  183. register();
  184. },
  185. };
  186. return ctl;
  187. };
  188. const menu = menuctl({ initValue: 0 });
  189.  
  190. // 显示自定义提示框 (保持不变)
  191. function showToast(message, duration = 3000) {
  192. $('.bili-blacklist-toast').remove();
  193. const toast = $(`<div class="bili-blacklist-toast"><span class="bili-blacklist-toast-icon">✓</span><div class="bili-blacklist-toast-content">${message}</div></div>`);
  194. $('body').append(toast);
  195. setTimeout(() => {
  196. toast.css({ 'opacity': '0', 'transform': 'translate(-50%, -20px)', 'transition': 'opacity 0.3s ease, transform 0.3s ease' });
  197. setTimeout(() => toast.remove(), 300);
  198. }, duration);
  199. }
  200.  
  201.  
  202. // 拉黑功能 (保持不变)
  203. // === 更新后的 window.tools_toblack 函数 ===
  204. window.tools_toblack = (uid, upName) => {
  205. log('执行拉黑操作,UID:', uid, '名称:', upName);
  206. const isVideoPage = window.location.href.includes('/video/');
  207.  
  208. // --- 辅助函数:更新按钮状态为“已拉黑” ---
  209. const setButtonToBlocked = (targetUid) => {
  210. log(`准备将 UID ${targetUid} 的按钮状态更新为 '已拉黑'`);
  211. let buttonUpdated = false;
  212. // 选择器:查找带有特定 data-uid 且尚未被禁用的按钮
  213. const selector = `.bilibili-blacklist-btn[data-uid="${targetUid}"]:not(:disabled)`;
  214.  
  215. // 优先尝试视频页结构
  216. if (isVideoPage) {
  217. $(`div.upname:not([data-block-updated="true"])`).each(function () {
  218. const $upnameDiv = $(this);
  219. const $link = $upnameDiv.find(`a[href*="/${targetUid}"]`);
  220. if ($link.length > 0) {
  221. const $button = $link.find(selector);
  222. if ($button.length > 0) {
  223. log('找到视频页按钮,更新为已拉黑:', $button[0]);
  224. $button.text('已拉黑').css({ 'opacity': '0.6', 'cursor': 'not-allowed', 'background-color': '#eee', 'border-color': '#ddd', 'color': '#aaa' }).prop('disabled', true).off('click');
  225. $upnameDiv.attr('data-block-updated', 'true');
  226. buttonUpdated = true;
  227. return false; // 停止搜索
  228. }
  229. }
  230. });
  231. }
  232.  
  233. // 如果视频页没找到,或者是在首页,进行通用查找
  234. if (!buttonUpdated) {
  235. $(selector).each(function () {
  236. const $button = $(this);
  237. // 检查按钮是否可见,避免操作隐藏的按钮(虽然可能性小)
  238. if ($button.is(':visible')) {
  239. log('找到通用按钮,更新为已拉黑:', $button[0]);
  240. $button.text('已拉黑').css({ 'opacity': '0.6', 'cursor': 'not-allowed', 'background-color': '#eee', 'border-color': '#ddd', 'color': '#aaa' }).prop('disabled', true).off('click');
  241. buttonUpdated = true;
  242. // 首页可能有多张卡片,不停止搜索
  243. }
  244. });
  245. }
  246.  
  247. if (!buttonUpdated) {
  248. log(`警告:未能找到 UID ${targetUid} 对应的按钮进行状态更新。`);
  249. }
  250. };
  251. // --- 辅助函数结束 ---
  252.  
  253.  
  254. // 执行 API 请求
  255. fetch("https://api.bilibili.com/x/relation/modify", {
  256. method: "POST", credentials: 'include', headers: { "Content-Type": "application/x-www-form-urlencoded", },
  257. body: new URLSearchParams({ 'fid': uid, 'act': 5, 're_src': 11, 'gaia_source': 'web_main', 'csrf': getCookie('bili_jct'), })
  258. }).then(res => {
  259. if (!res.ok) { throw new Error(`HTTP error! Status: ${res.status}`); } return res.json();
  260. }).then(data => {
  261. log('拉黑API响应:', data);
  262. if (data.code === 0) { // 拉黑成功
  263. log('拉黑成功:', uid);
  264. showToast(`已成功将 "${upName || 'UP主'}" 加入黑名单`);
  265. setButtonToBlocked(uid); // 调用辅助函数更新按钮状态
  266. if (!isVideoPage) { // 首页额外操作:移除卡片
  267. log('执行首页移除操作...');
  268. $(`.bili-video-card[data-up-id="${uid}"], .feed-card[data-up-id="${uid}"], .video-card[data-up-id="${uid}"]`).fadeOut(300, function () { $(this).remove(); });
  269. $('div.uid_' + uid).fadeOut(300, function () { $(this).remove(); });
  270. }
  271. } else { // 拉黑失败 (API返回错误码)
  272. log('拉黑失败:', data.message || `错误码 ${data.code}`);
  273.  
  274. // === 新增:检查是否是 "已拉黑" 错误码 ===
  275. if (data.code === 22120) {
  276. log('检测到错误码 22120 (用户已被拉黑)');
  277. showToast('该用户已被拉黑'); // 显示更具体的提示
  278. setButtonToBlocked(uid); // 同样调用辅助函数更新按钮状态
  279. } else {
  280. // 对于其他所有错误,只显示通用失败提示,不改变按钮状态
  281. showToast(`拉黑失败: ${data.message || `错误码 ${data.code}`}`);
  282. }
  283. // === 检查结束 ===
  284. }
  285. }).catch(err => { // 网络请求错误等
  286. log('拉黑请求错误:', err);
  287. showToast('拉黑请求失败,请检查网络或登录(不可用)状态');
  288. // 网络错误不改变按钮状态
  289. });
  290. updateBlacklistCount(); // 更新黑名单计数
  291. };
  292.  
  293.  
  294. // 更新黑名单计数 (保持不变)
  295. function updateBlacklistCount() {
  296. fetch("https://api.bilibili.com/x/relation/blacks?re_version=0&pn=1&ps=20&jsonp=jsonp&web_location=333.33", {
  297. method: "GET", credentials: 'include',
  298. }).then(res => {
  299. if (!res.ok) { throw new Error(`HTTP error! Status: ${res.status}`); } return res.json();
  300. }).then(data => {
  301. log('黑名单API响应:', data); if (data.code === 0) { menu.total = data.data.total; log('更新黑名单计数:', data.data.total); }
  302. else { log('获取黑名单失败:', data.message || '未知错误'); }
  303. }).catch(err => { log('获取黑名单请求错误:', err); });
  304. }
  305.  
  306.  
  307. // === 处理首页 (已修改) ===
  308. function processHomePage() {
  309. log('处理首页');
  310. const possibleContainers = ['.bili-video-card', '.video-card', '.bili-video-card__wrap', '.feed-card'];
  311. let foundCards = false;
  312.  
  313. possibleContainers.forEach(containerSelector => {
  314. const cards = $(`${containerSelector}:not([data-toblack-processed="true"])`);
  315.  
  316. if (cards.length > 0) {
  317. // log(`找到 ${cards.length} 个未处理的视频卡片 (${containerSelector})`); // 减少日志
  318. foundCards = true;
  319.  
  320. cards.each((index, card) => {
  321. const $card = $(card);
  322. // 标记立即处理,避免重复
  323. $card.attr('data-toblack-processed', 'true');
  324.  
  325. let ownerLinkElement = null;
  326. let upName = '';
  327. let ownerUrl = '';
  328. let uid = '';
  329.  
  330. // 优先使用新结构选择器
  331. ownerLinkElement = $card.find('a.bili-video-card__info--owner');
  332. if (ownerLinkElement.length > 0) {
  333. ownerUrl = ownerLinkElement.attr('href');
  334. const authorSpan = ownerLinkElement.find('span.bili-video-card__info--author');
  335. if (authorSpan.length > 0) {
  336. upName = authorSpan.attr('title') || authorSpan.text().trim(); // 优先用 title
  337. } else {
  338. // 备选:直接取链接文本,尝试去除日期
  339. upName = ownerLinkElement.text().trim().split('·')[0].trim();
  340. }
  341. log(`首页: 结构匹配成功 (a.bili-video-card__info--owner)`);
  342. } else {
  343. // Fallback 到旧的选择器逻辑
  344. const possibleOwnerSelectors = ['.up-name', '.author-text', '.up-name__text'];
  345. for (const selector of possibleOwnerSelectors) {
  346. const element = $card.find(selector);
  347. if (element.length > 0) {
  348. ownerLinkElement = element; // 记录找到的元素
  349. ownerUrl = element.attr('href');
  350. upName = element.text().trim();
  351. log(`首页: 备选结构匹配成功 (${selector})`);
  352. break;
  353. }
  354. }
  355. }
  356.  
  357. if (!ownerUrl || !ownerLinkElement) {
  358. // log('未能找到卡片上的UP主链接');
  359. return; // Skip card
  360. }
  361.  
  362. // 提取 UID
  363. if (ownerUrl.includes('/space.bilibili.com/')) {
  364. uid = ownerUrl.split('/space.bilibili.com/')[1].split('?')[0].split('/')[0];
  365. } else if (ownerUrl.includes('/space/')) {
  366. uid = ownerUrl.split('/space/')[1].split('?')[0].split('/')[0];
  367. } else {
  368. const match = ownerUrl.match(/\/(\d+)(\/|\?|$)/);
  369. if (match && match[1]) { uid = match[1]; }
  370. }
  371.  
  372. if (!uid || !/^\d+$/.test(uid)) {
  373. // log('无法从URL提取有效的首页UID:', ownerUrl);
  374. return; // Skip card
  375. }
  376.  
  377. // 给卡片添加 data-up-id 属性,方便拉黑后移除
  378. $card.attr('data-up-id', uid);
  379.  
  380. if (!upName) { upName = `UID: ${uid}`; } // Default name if extraction failed
  381. log('提取首页UID:', uid, '名称:', upName);
  382.  
  383. // --- 按钮放置 ---
  384. let buttonAdded = false;
  385. // 尝试添加到 info--bottom 的前面 (首选)
  386. const bottomInfoDiv = $card.find('.bili-video-card__info--bottom');
  387. if (bottomInfoDiv.length > 0) {
  388. if (bottomInfoDiv.find('.bilibili-blacklist-btn').length === 0) { // 避免重复添加
  389. const blackButton = $(`<a class="bilibili-blacklist-btn" data-uid="${uid}">拉黑</a>`);
  390. blackButton.on('click', function (e) { e.preventDefault(); e.stopPropagation(); window.tools_toblack(uid, upName); });
  391. bottomInfoDiv.prepend(blackButton);
  392. buttonAdded = true;
  393. log('按钮添加到 info--bottom 前面');
  394. } else { buttonAdded = true; /* Already exists */ }
  395. }
  396.  
  397. // 备选:添加到卡片右上角悬浮 (如果上面没成功)
  398. if (!buttonAdded && $card.find('.blacklist-button-container').length === 0) {
  399. if (!$card.css('position') || $card.css('position') === 'static') {
  400. $card.css('position', 'relative'); //确保卡片有定位上下文
  401. }
  402. const container = $('<div class="blacklist-button-container"></div>');
  403. const blackButton = $(`<a class="bilibili-blacklist-btn" data-uid="${uid}">拉黑</a>`);
  404. blackButton.on('click', function (e) { e.preventDefault(); e.stopPropagation(); window.tools_toblack(uid, upName); });
  405. container.append(blackButton);
  406. $card.append(container);
  407. buttonAdded = true;
  408. log('按钮添加到悬浮容器');
  409. }
  410.  
  411. // 旧的 UID class 逻辑,保留
  412. $card.addClass('uid_' + uid);
  413. });
  414. }
  415. });
  416. // if (!foundCards) { log('本次未找到任何需要处理的视频卡片'); } // 减少日志
  417. }
  418.  
  419.  
  420. // === 处理视频页面 (保持不变, 使用 v1.0.7 的逻辑) ===
  421. function processVideoPage() {
  422. log('处理视频页面');
  423.  
  424. function findAndProcessUpInfo() {
  425. // Target the container div first, ensure it's not already processed
  426. const upnameDivs = $('div.upname:not([data-toblack-processed="true"])');
  427.  
  428. if (upnameDivs.length > 0) {
  429. log(`找到 ${upnameDivs.length} 个未处理的视频页UP主容器 (div.upname)`);
  430.  
  431. upnameDivs.each(function () {
  432. const upnameDiv = $(this);
  433. // Find the main link (<a>) inside the div, which contains the space URL
  434. const linkElement = upnameDiv.find('a[href*="/space.bilibili.com/"], a[href*="/space/"]');
  435. // Find the name span (span.name) inside the link
  436. const nameElement = linkElement.find('span.name');
  437.  
  438. if (linkElement.length > 0 && nameElement.length > 0) {
  439. if (linkElement.find('.bilibili-blacklist-btn').length > 0) {
  440. upnameDiv.attr('data-toblack-processed', 'true');
  441. // log('按钮已存在于 upname link 中,跳过'); //减少日志
  442. return;
  443. }
  444. const upUrl = linkElement.attr('href');
  445. const upName = nameElement.text().trim();
  446. if (!upUrl || !upName) { upnameDiv.attr('data-toblack-processed', 'true'); return; }
  447. let uid = '';
  448. if (upUrl.includes('/space.bilibili.com/')) { uid = upUrl.split('/space.bilibili.com/')[1].split('?')[0].split('/')[0]; }
  449. else if (upUrl.includes('/space/')) { uid = upUrl.split('/space/')[1].split('?')[0].split('/')[0]; }
  450. else { const match = upUrl.match(/\/(\d+)(\/|\?|$)/); if (match && match[1]) { uid = match[1]; } }
  451. if (!uid || !/^\d+$/.test(uid)) { upnameDiv.attr('data-toblack-processed', 'true'); return; }
  452. // ... (code to extract uid and upName) ...
  453.  
  454. log('提取视频页UID:', uid, '名称:', upName);
  455. const blackButton = $(`<a class="bilibili-blacklist-btn" data-uid="${uid}">拉黑</a>`);
  456.  
  457. // === MODIFIED CLICK HANDLER for Video Page ===
  458. blackButton.on('click', function (e) {
  459. e.preventDefault();
  460. e.stopPropagation();
  461. console.log('[拉黑脚本] --- 视频页按钮点击事件触发 ---'); // Log: Handler Fired
  462.  
  463. const buttonElement = $(this);
  464. // Re-read UID from the button's data attribute at the time of click
  465. const clickedUid = buttonElement.data('uid');
  466. // Use the name captured when the button was created (closure)
  467. // Alternatively, could try finding the name span again relative to buttonElement if needed
  468. const clickedName = upName;
  469.  
  470. console.log('[拉黑脚本] 点击时获取的 UID:', clickedUid, typeof clickedUid);
  471. console.log('[拉黑脚本] 点击时获取的 Name:', clickedName);
  472.  
  473. // Validate the UID before calling the API function
  474. if (!clickedUid || typeof clickedUid === 'undefined' || String(clickedUid).trim() === '' || !/^\d+$/.test(String(clickedUid))) {
  475. console.error('[拉黑脚本] 错误:点击时 UID 无效!', clickedUid);
  476. showToast('拉黑失败:无法获取有效的 UP 主 ID');
  477. // Optionally add more specific error messages based on the condition
  478. // if (!clickedUid) { showToast('拉黑失败:UID 未定义'); }
  479. // else if (!/^\d+$/.test(String(clickedUid))) { showToast('拉黑失败:UID 非数字'); }
  480. return; // Stop if UID is invalid
  481. }
  482.  
  483. // If UID is valid, proceed to call the block function
  484. console.log('[拉黑脚本] UID 有效,准备调用 tools_toblack...');
  485. try {
  486. window.tools_toblack(String(clickedUid), clickedName);
  487. } catch (apiError) {
  488. console.error('[拉黑脚本] 调用 tools_toblack 时出错:', apiError);
  489. showToast('拉黑操作内部出错,请检查控制台');
  490. }
  491. });
  492. // === END MODIFIED CLICK HANDLER ===
  493.  
  494. nameElement.after(blackButton); // Insert button after the name span
  495. upnameDiv.attr('data-toblack-processed', 'true');
  496. log('已添加拉黑按钮到视频页UP主:', upName);
  497. } else { upnameDiv.attr('data-toblack-processed', 'true'); }
  498. });
  499. return true; // Found and processed elements
  500. }
  501. return false; // Did not find any new div.upname to process this time
  502. }
  503.  
  504. // Retry logic (保持不变)
  505. if (!findAndProcessUpInfo()) {
  506. // log('首次未找到UP主信息 (div.upname),将在稍后重试'); // Reduce log noise
  507. let retryCount = 0; const maxRetries = 5; const retryInterval = 800;
  508. if (window.videoPageRetryTimer) { clearInterval(window.videoPageRetryTimer); }
  509. window.videoPageRetryTimer = setInterval(() => {
  510. retryCount++;
  511. if (findAndProcessUpInfo() || retryCount >= maxRetries) {
  512. clearInterval(window.videoPageRetryTimer); window.videoPageRetryTimer = null;
  513. if (retryCount >= maxRetries && !$('div.upname .bilibili-blacklist-btn').length) { log('在多次尝试后仍未找到或添加按钮到 UP主信息 (div.upname)'); }
  514. else if ($('div.upname .bilibili-blacklist-btn').length > 0) { log(`通过重试找到并处理了 UP 主信息`); }
  515. }
  516. }, retryInterval);
  517. }
  518. }
  519.  
  520.  
  521. // 统一处理入口 (保持不变)
  522. function processPage() {
  523. if (window.processingPage) { return; } // 简化跳过逻辑
  524. window.processingPage = true;
  525. try {
  526. const isVideoPage = window.location.href.includes('/video/');
  527. if (isVideoPage) { processVideoPage(); } else { processHomePage(); }
  528. } catch (error) {
  529. log("处理页面时出错:", error);
  530. } finally {
  531. // 使用 setTimeout 确保 processingPage 标志在稍后重置
  532. setTimeout(() => { window.processingPage = false; }, 300); // 减少延迟
  533. }
  534. }
  535.  
  536.  
  537. // === 设置DOM观察器 (已修改首页目标) ===
  538. function setupObserver() {
  539. if (window.blacklistObserverSet) { log('观察器已存在,跳过设置'); return; }
  540. log('设置DOM观察器');
  541.  
  542. // --- Debounce 处理函数 ---
  543. const debouncedProcessPage = debounce(processPage, 300);
  544. // ---
  545.  
  546. const observer = new MutationObserver(function (mutations) {
  547. let needsUpdate = false;
  548. mutations.forEach(mutation => {
  549. if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  550. for (let node of mutation.addedNodes) {
  551. if (node.nodeType === 1) { needsUpdate = true; break; }
  552. }
  553. }
  554. if (needsUpdate) return;
  555. });
  556.  
  557. if (needsUpdate) {
  558. // log('检测到DOM变更,计划更新拉黑按钮'); // 减少日志
  559. // --- 使用 Debounce ---
  560. debouncedProcessPage();
  561. // --- 不使用 Debounce (注释掉) ---
  562. // processPage();
  563. // ---
  564. }
  565. });
  566.  
  567. const isVideoPage = window.location.href.includes('/video/');
  568. let targetNode = null;
  569.  
  570. if (isVideoPage) {
  571. // 视频页目标 (保持 v1.0.7 的选择)
  572. targetNode = document.querySelector('#viewbox_report') ||
  573. document.querySelector('.video-info-detail') ||
  574. document.querySelector('#app .left-container');
  575. log('尝试为视频页选择观察节点:', targetNode ? (targetNode.id || targetNode.className) : '未找到特定节点');
  576. } else {
  577. // 首页目标: 添加更多备选项
  578. targetNode = document.querySelector('#app .feed-list') || // 推荐流
  579. document.querySelector('#i_cecream') || // 首页外层容器 ID 之一
  580. document.querySelector('.bili-grid') || // 通用网格布局
  581. document.querySelector('#app .bili-layout') || // 更通用的布局容器
  582. document.querySelector('#app'); // 万不得已才用 #app
  583. log('尝试为首页选择观察节点:', targetNode ? (targetNode.id || targetNode.className) : '未找到特定节点');
  584. }
  585.  
  586. if (targetNode) {
  587. log('最终选择观察节点:', targetNode);
  588. observer.observe(targetNode, { childList: true, subtree: true });
  589. window.blacklistObserverSet = true;
  590. log('DOM观察器已启动');
  591. } else {
  592. log('警告:未能找到合适的DOM节点进行观察,MutationObserver 未启动。按钮可能只在初始加载时添加。');
  593. }
  594.  
  595. // 初始加载时检查 (总会执行一次)
  596. setTimeout(processPage, 1500);
  597.  
  598. // 定期检查作为后备 (可选,可以注释掉)
  599. // if (window.blacklistInterval) clearInterval(window.blacklistInterval);
  600. // window.blacklistInterval = setInterval(processPage, 10000);
  601. }
  602.  
  603.  
  604. // 暴露给全局作用域 (保持不变)
  605. window.blacklistScript = { processPage, processHomePage, processVideoPage };
  606.  
  607. // 启动逻辑 (保持不变)
  608. $(document).ready(function () {
  609. log('页面就绪,初始化脚本');
  610. setupObserver();
  611. updateBlacklistCount();
  612. });
  613.  
  614. // 后备启动逻辑 (保持不变)
  615. setTimeout(function () {
  616. if (!window.blacklistObserverSet && !$('body').data('blacklist-init-fallback')) {
  617. log('延时后备初始化');
  618. $('body').data('blacklist-init-fallback', true);
  619. setupObserver();
  620. updateBlacklistCount();
  621. processPage();
  622. }
  623. }, 3000);
  624.  
  625. log('脚本初始化完成');
  626. });
  627. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址