YouTube去广告

这是一个去除YouTube广告的脚本,轻量且高效,它能丝滑的去除界面广告和视频广告,包括6s广告。

当前为 2024-06-12 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube去广告 YouTube AD Blocker
  3. // @name:zh-CN YouTube去广告
  4. // @name:zh-TW YouTube去廣告
  5. // @name:zh-HK YouTube去廣告
  6. // @name:zh-MO YouTube去廣告
  7. // @namespace https://github.com/iamfugui/YouTubeADB
  8. // @version 6.04
  9. // @description 这是一个去除YouTube广告的脚本,轻量且高效,它能丝滑的去除界面广告和视频广告,包括6s广告。This is a script that removes ads on YouTube, it's lightweight and efficient, capable of smoothly removing interface and video ads, including 6s ads.
  10. // @description:zh-CN 这是一个去除YouTube广告的脚本,轻量且高效,它能丝滑的去除界面广告和视频广告,包括6s广告。
  11. // @description:zh-TW 這是一個去除YouTube廣告的腳本,輕量且高效,它能絲滑地去除界面廣告和視頻廣告,包括6s廣告。
  12. // @description:zh-HK 這是一個去除YouTube廣告的腳本,輕量且高效,它能絲滑地去除界面廣告和視頻廣告,包括6s廣告。
  13. // @description:zh-MO 這是一個去除YouTube廣告的腳本,輕量且高效,它能絲滑地去除界面廣告和視頻廣告,包括6s廣告。
  14. // @author iamfugui
  15. // @match *://*.youtube.com/*
  16. // @exclude *://accounts.youtube.com/*
  17. // @exclude *://www.youtube.com/live_chat_replay*
  18. // @exclude *://www.youtube.com/persist_identity*
  19. // @icon https://www.google.com/s2/favicons?sz=64&domain=YouTube.com
  20. // @grant none
  21. // @license MIT
  22. // ==/UserScript==
  23. (function() {
  24. `use strict`;
  25. //界面广告选择器
  26. const cssSeletorArr = [
  27. `#masthead-ad`,//首页顶部横幅广告.
  28. `ytd-rich-item-renderer.style-scope.ytd-rich-grid-row #content:has(.ytd-display-ad-renderer)`,//首页视频排版广告.
  29. `.video-ads.ytp-ad-module`,//播放器底部广告.
  30. `tp-yt-paper-dialog:has(yt-mealbar-promo-renderer)`,//播放页会员促销广告.
  31. `ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]`,//播放页右上方推荐广告.
  32. `#related #player-ads`,//播放页评论区右侧推广广告.
  33. `#related ytd-ad-slot-renderer`,//播放页评论区右侧视频排版广告.
  34. `ytd-ad-slot-renderer`,//搜索页广告.
  35. `yt-mealbar-promo-renderer`,//播放页会员推荐广告.
  36. `ad-slot-renderer`,//M播放页第三方推荐广告
  37. `ytm-companion-ad-renderer`,//M可跳过的视频广告链接处
  38. ];
  39.  
  40. window.dev=false;//开发使用
  41.  
  42. /**
  43. * 将标准时间格式化
  44. * @param {Date} time 标准时间
  45. * @param {String} format 格式
  46. * @return {String}
  47. */
  48. function moment(time) {
  49. // 获取年⽉⽇时分秒
  50. let y = time.getFullYear()
  51. let m = (time.getMonth() + 1).toString().padStart(2, `0`)
  52. let d = time.getDate().toString().padStart(2, `0`)
  53. let h = time.getHours().toString().padStart(2, `0`)
  54. let min = time.getMinutes().toString().padStart(2, `0`)
  55. let s = time.getSeconds().toString().padStart(2, `0`)
  56. return `${y}-${m}-${d} ${h}:${min}:${s}`
  57. }
  58.  
  59. /**
  60. * 输出信息
  61. * @param {String} msg 信息
  62. * @return {undefined}
  63. */
  64. function log(msg) {
  65. if(!window.dev){
  66. return false;
  67. }
  68. console.log(window.location.href);
  69. console.log(`${moment(new Date())} ${msg}`);
  70. }
  71.  
  72. /**
  73. * 设置运行标志
  74. * @param {String} name
  75. * @return {undefined}
  76. */
  77. function setRunFlag(name){
  78. let style = document.createElement(`style`);
  79. style.id = name;
  80. (document.querySelector(`head`) || document.querySelector(`body`)).appendChild(style);//将节点附加到HTML.
  81. }
  82.  
  83. /**
  84. * 获取运行标志
  85. * @param {String} name
  86. * @return {undefined|Element}
  87. */
  88. function getRunFlag(name){
  89. return document.getElementById(name);
  90. }
  91.  
  92. /**
  93. * 检查是否设置了运行标志
  94. * @param {String} name
  95. * @return {Boolean}
  96. */
  97. function checkRunFlag(name){
  98. if(getRunFlag(name)){
  99. return true;
  100. }else{
  101. setRunFlag(name)
  102. return false;
  103. }
  104. }
  105.  
  106. /**
  107. * 生成去除广告的css元素style并附加到HTML节点上
  108. * @param {String} styles 样式文本
  109. * @return {undefined}
  110. */
  111. function generateRemoveADHTMLElement(id) {
  112. //如果已经设置过,退出.
  113. if (checkRunFlag(id)) {
  114. log(`屏蔽页面广告节点已生成`);
  115. return false
  116. }
  117.  
  118. //设置移除广告样式.
  119. let style = document.createElement(`style`);//创建style元素.
  120. (document.querySelector(`head`) || document.querySelector(`body`)).appendChild(style);//将节点附加到HTML.
  121. style.appendChild(document.createTextNode(generateRemoveADCssText(cssSeletorArr)));//附加样式节点到元素节点.
  122. log(`生成屏蔽页面广告节点成功`);
  123. }
  124.  
  125. /**
  126. * 生成去除广告的css文本
  127. * @param {Array} cssSeletorArr 待设置css选择器数组
  128. * @return {String}
  129. */
  130. function generateRemoveADCssText(cssSeletorArr){
  131. cssSeletorArr.forEach((seletor,index)=>{
  132. cssSeletorArr[index]=`${seletor}{display:none!important}`;//遍历并设置样式.
  133. });
  134. return cssSeletorArr.join(` `);//拼接成字符串.
  135. }
  136.  
  137. /**
  138. * 触摸事件
  139. * @return {undefined}
  140. */
  141. function nativeTouch(){
  142. // 创建 Touch 对象
  143. let touch = new Touch({
  144. identifier: Date.now(),
  145. target: this,
  146. clientX: 12,
  147. clientY: 34,
  148. radiusX: 56,
  149. radiusY: 78,
  150. rotationAngle: 0,
  151. force: 1
  152. });
  153.  
  154. // 创建 TouchEvent 对象
  155. let touchStartEvent = new TouchEvent(`touchstart`, {
  156. bubbles: true,
  157. cancelable: true,
  158. view: window,
  159. touches: [touch],
  160. targetTouches: [touch],
  161. changedTouches: [touch]
  162. });
  163.  
  164. // 分派 touchstart 事件到目标元素
  165. this.dispatchEvent(touchStartEvent);
  166.  
  167. // 创建 TouchEvent 对象
  168. let touchEndEvent = new TouchEvent(`touchend`, {
  169. bubbles: true,
  170. cancelable: true,
  171. view: window,
  172. touches: [],
  173. targetTouches: [],
  174. changedTouches: [touch]
  175. });
  176.  
  177. // 分派 touchend 事件到目标元素
  178. this.dispatchEvent(touchEndEvent);
  179. }
  180.  
  181. /**
  182. * 跳过广告
  183. * @return {undefined}
  184. */
  185. function skipAd(mutationsList, observer) {
  186. let video = document.querySelector(`.ad-showing video`) || document.querySelector(`video`);//获取视频节点
  187. let skipButton = document.querySelector(`.ytp-ad-skip-button`) || document.querySelector(`.ytp-skip-ad-button`) || document.querySelector(`.ytp-ad-skip-button-modern`);
  188. let shortAdMsg = document.querySelector(`.video-ads.ytp-ad-module .ytp-ad-player-overlay`) || document.querySelector(`.ytp-ad-button-icon`);
  189.  
  190. if(skipButton || shortAdMsg && window.location.href.indexOf("https://m.youtube.com/") === -1){ //移动端静音有bug
  191. video.muted = true;
  192. log(`静音~~~~~~~~~~~~~`);
  193. video.playbackRate = 16;
  194. log(`调速~~~~~~~~~~~~~`);
  195. }
  196.  
  197. if(skipButton){
  198. if(video.currentTime>0.5){
  199. video.currentTime = video.duration;//强制
  200. log(`特殊账号跳过按钮广告~~~~~~~~~~~~~`);
  201. return;
  202. }
  203. skipButton.click();//PC
  204. nativeTouch.call(skipButton);//Phone
  205. log(`按钮跳过广告~~~~~~~~~~~~~`);
  206. }else if(shortAdMsg && video.currentTime>0.5){//避免检查
  207. video.currentTime = video.duration;//强制
  208. log(`强制结束了该广告`);
  209. }
  210.  
  211. }
  212.  
  213. /**
  214. * 去除播放中的广告
  215. * @return {undefined}
  216. */
  217. function removePlayerAD(id){
  218. //如果已经在运行,退出.
  219. if (checkRunFlag(id)) {
  220. log(`去除播放中的广告功能已在运行`);
  221. return false
  222. }
  223. let observer;//监听器
  224. let timerID;//定时器
  225.  
  226. //开始监听
  227. function startObserve(){
  228. //广告节点监听
  229. const targetNode = document.querySelector(`.video-ads.ytp-ad-module`);
  230. if(!targetNode){
  231. log(`正在寻找待监听的目标节点`);
  232. return false;
  233. }
  234. //监听视频中的广告并处理
  235. const config = {childList: true, subtree: true };// 监听目标节点本身与子树下节点的变动
  236. observer = new MutationObserver(skipAd);// 创建一个观察器实例并设置处理广告的回调函数
  237. observer.observe(targetNode, config);// 以上述配置开始观察广告节点
  238. timerID=setInterval(skipAd, 500);//漏网鱼
  239. }
  240.  
  241. //轮询任务
  242. let startObserveID = setInterval(()=>{
  243. if(observer && timerID){
  244. clearInterval(startObserveID);
  245. }else{
  246. startObserve();
  247. }
  248. },16);
  249.  
  250. log(`运行去除播放中的广告功能成功`);
  251. }
  252.  
  253. /**
  254. * main函数
  255. */
  256. function main(){
  257. generateRemoveADHTMLElement(`removeADHTMLElement`);//移除界面中的广告.
  258. removePlayerAD(`removePlayerAD`);//移除播放中的广告.
  259. }
  260.  
  261. if (document.readyState === `loading`) {
  262. log(`YouTube去广告脚本即将调用:`);
  263. document.addEventListener(`DOMContentLoaded`, main);// 此时加载尚未完成
  264. } else {
  265. log(`YouTube去广告脚本快速调用:`);
  266. main();// 此时`DOMContentLoaded` 已经被触发
  267. }
  268.  
  269. })();