dgj's bilibili ads guard

remove all ads from bilibili.com

  1. // ==UserScript==
  2. // @name dgj's bilibili ads guard
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-06-18
  5. // @description remove all ads from bilibili.com
  6. // @author noobdawn
  7. // @match https://www.bilibili.com/
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
  9. // @grant GM_xmlhttpRequest
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. var blockData = localStorage.getItem("dgjBlockData");
  17. //if (blockData === null)
  18. {
  19. blockData = {
  20. "up_name": "",
  21. // up主的名字
  22.  
  23. "up_uid": "",
  24. // up主的UID
  25.  
  26. "video_keyword":
  27. // sb游戏
  28. "剑网3,原神,崩坏,星穹铁道,星铁,绝区,未定事件簿,米哈游,仙家军,明日方舟,鹰角,海猫,鸣潮,战双帕弥什,库洛,少前,少女前线,面包房少女,散爆,羽中,无期迷途,叠纸,奇迹暖暖,无限暖暖,恋与深空,恋与制作人,百面千相,姚润昊,深空之眼,来古弥新,物华弥新,新月同行,归龙潮,破晓序列,卡拉彼丘,碧蓝档案,蔚蓝档案,雀魂,以闪亮之名,光与夜之恋,尘白禁区",
  29. // 视频的关键词
  30.  
  31. "video_label":
  32. // sb游戏
  33. "鸣潮,鸣潮公测二创,鸣潮公测创作者激励计划,新三国,火影忍者手游,地下城与勇士,dnf,dnfpk,300英雄,MOBA,三百英雄,剑网3,星穹铁道2.2波提欧,暴雪,原神,崩坏,崩坏学园2,崩坏3,崩坏星穹铁道,星穹铁道,绝区零,绝区0,未定事件簿,米哈游,仙家军,明日方舟,鹰角,鹰角网络,海猫,鸣潮,战双帕弥什,库洛,少前,少前2,少女前线,少女前线2,面包房少女,散爆,羽中,叠纸游戏,奇迹暖暖,无限暖暖,恋与深空,恋与制作人,百面千相,姚润昊,深空之眼,来古弥新,物华弥新,新月同行,归龙潮,望月,破晓序列,卡拉彼丘,碧蓝档案,蔚蓝档案,雀魂,以闪亮之名,光与夜之恋,尘白禁区" +
  34. // 没营养的东西
  35. "助眠,生活,情感,日常,校园,娱乐,记录,舞蹈,Vtuber,hololive,免单挑战,探店,美食,时尚,穿搭"
  36. // 视频的标签
  37. };
  38. blockData.up_name = blockData.up_name.split(",");
  39. blockData.video_keyword = blockData.video_keyword.split(",");
  40. blockData.video_label = blockData.video_label.split(",");
  41. blockData.up_uid = blockData.up_uid.split(",");
  42. localStorage.setItem("dgjBlockData", JSON.stringify(blockData));
  43. }
  44.  
  45. const DEBUG = true;
  46.  
  47. function removeAds(node, msg) {
  48. if (DEBUG)
  49. {
  50. // 删除所有子元素
  51. while (node.firstChild) {
  52. node.removeChild(node.firstChild);
  53. }
  54. // 添加一个文本节点
  55. node.appendChild(document.createTextNode(msg));
  56. }
  57. else
  58. node.remove();
  59. }
  60.  
  61. // 从URL中获取UID
  62. function GetUidFromUrl(url) {
  63. var index = url.indexOf("space.bilibili.com/");
  64. if (index === -1)
  65. return -1;
  66. // 将剩下的所有字符转换为字符串
  67. var uid = url.substring(index + 19);
  68. return parseInt(uid);
  69. }
  70.  
  71. // 根据UP主屏蔽视频
  72. function BlockVideoByUp(node) {
  73. // 向下查找,找到class为"bili-video-card__info--owner"的a,这就是UP主的链接
  74. var owner = node.getElementsByClassName("bili-video-card__info--owner");
  75. if (owner.length > 0) {
  76. var uid = GetUidFromUrl(owner[0].href);
  77. if (uid === -1)
  78. return true;
  79. // 如果uid在blockData的up_uid数组中,就删除这个视频
  80. if (blockData.up_uid.indexOf(uid) !== -1)
  81. {
  82. removeAds(node, "触发UP主:" + uid);
  83. return true;
  84. }
  85. owner = node.getElementsByClassName("bili-video-card__info--author");
  86. if (owner.length > 0) {
  87. var name = owner[0].innerText;
  88. // 如果name在blockData的up_name数组中,就删除这个视频
  89. if (blockData.up_name.indexOf(name) !== -1)
  90. {
  91. removeAds(node, "触发UP主:" + name);
  92. return true;
  93. }
  94. }
  95. return false;
  96. }
  97. return true;
  98. }
  99.  
  100. // 根据视频屏蔽视频
  101. function BlockVideoByVideo(node) {
  102. var link = node.getElementsByClassName("bili-video-card__info--tit");
  103. if (link.length > 0) {
  104. console.log(link[0].title);
  105. var title = link[0].title;
  106. for (var j = 0; j < blockData.video_keyword.length; j++){
  107. if (title.includes(blockData.video_keyword[j])) {
  108. removeAds(node, "触发关键字:" + blockData.video_keyword[j]);
  109. return;
  110. }
  111. }
  112. }
  113. // 向下查找,找到class为"bili-video-card__image--link"的a,这就是视频的链接
  114. link = node.getElementsByClassName("bili-video-card__image--link");
  115. if (link.length > 0) {
  116. var url = link[0].href;
  117. // 打开视频链接,获取视频的关键词和标签
  118. GM_xmlhttpRequest({
  119. method: "GET",
  120. url: url,
  121. onload: function(response) {
  122. var data = response.responseText;
  123. // 找到<meta data-vue-meta="true" itemprop="keywords" name="keywords" content="
  124. let start_string = "<meta data-vue-meta=\"true\" itemprop=\"keywords\" name=\"keywords\" content=\"";
  125. let end_string = "\"><meta";
  126. var index = data.indexOf(start_string);
  127. if (index !== -1)
  128. {
  129. var keywords = data.substring(index + start_string.length, data.indexOf(end_string, index));
  130. var keywordArray = keywords.split(",");
  131. // 抛弃第一个元素
  132. keywordArray.shift();
  133. console.log(keywordArray);
  134. // 如果labels数组中有一个元素在blockData的video_label数组中,就删除这个视频
  135. for (i = 0; i < keywordArray.length; i++)
  136. {
  137. if (blockData.video_label.indexOf(keywordArray[i]) !== -1)
  138. {
  139. removeAds(node, "触发标签:" + keywordArray[i]);
  140. return;
  141. }
  142. }
  143. }
  144. }
  145. });
  146. }
  147. }
  148.  
  149. // 每次页面元素变化时,都会触发此函数
  150. var observer = new MutationObserver(function(mutations) {
  151. // 遍历所有变化
  152. mutations.forEach(function(mutation) {
  153. mutation.addedNodes.forEach(function(node) {
  154. if (node instanceof HTMLDivElement) {
  155. // 为菜单新增一个按钮
  156. if (node.className === "bili-video-card__info--no-interest-panel")
  157. {
  158. // 创建一个新的选项,class为"bili-video-card__info--no-interest-panel--item",innerText为"屏蔽此UP主"
  159. var item = document.createElement("div");
  160. item.className = "bili-video-card__info--no-interest-panel--item";
  161. item.innerText = "屏蔽此UP主";
  162. // 将新的选项插入到菜单中
  163. node.appendChild(item);
  164. // 监听点击事件
  165. item.addEventListener("click", function() {
  166. // todo
  167. });
  168. }
  169.  
  170. // 如果新增的节点的class为"floor-single-card",这部分一般是直播间、番剧、国创、课堂、综艺推荐卡片,干掉
  171. if (node.className === "floor-single-card") {
  172. removeAds(node, "Ban原因:推送");
  173. return;
  174. }
  175. // 如果新增的节点的class为"bili-video-card is-rcmd",这部分一般是不能点不感兴趣的卡片,直接干掉
  176. if (node.className === "bili-video-card is-rcmd") {
  177. removeAds(node, "Ban原因:广告");
  178. return;
  179. }
  180. if (node.className === "bili-video-card is-rcmd enable-no-interest")
  181. {
  182. node.style.marginTop = '22px';
  183. }
  184. if (BlockVideoByUp(node) !== true)
  185. BlockVideoByVideo(node);
  186. }
  187. });
  188. });
  189. });
  190.  
  191. // 监听整个文档
  192. observer.observe(document, {
  193. childList: true,
  194. subtree: true
  195. });
  196.  
  197. // 移除滚动播放广告
  198. // 找到所有class为"recommended-swipe grid-anchor"的div,这就是左上角的广告滚动页
  199. var ads = document.getElementsByClassName("recommended-swipe grid-anchor");
  200. for (var i = 0; i < ads.length; i++) {
  201. removeAds(ads[i], "Ban原因:广告滚动页");
  202. }
  203.  
  204. // 找到所有首页的广告
  205. // 找到所有class为"bili-video-card is-rcmd"的div
  206. ads = document.getElementsByClassName("bili-video-card is-rcmd");
  207. for (i = 0; i < ads.length; i++) {
  208. if (ads[i].className === "bili-video-card is-rcmd")
  209. {
  210. if (ads[i].parentNode.className === "feed-card")
  211. removeAds(ads[i].parentNode, "Ban原因:广告");
  212. else
  213. removeAds(ads[i], "Ban原因:广告");
  214. }
  215. }
  216.  
  217. ads = document.getElementsByClassName("feed-card");
  218. for (i = 0; i < ads.length; i++) {
  219. // 将样式中的margin-top设为40像素
  220. ads[i].style.marginTop = '22px';
  221. if (BlockVideoByUp(ads[i]) !== true)
  222. BlockVideoByVideo(ads[i]);
  223. }
  224.  
  225.  
  226. })();