YouTube去廣告

這是一個去除YouTube廣告的腳本,輕量且高效,它能絲滑地去除界面廣告和視頻廣告,包括6s廣告。

目前為 2023-10-10 提交的版本,檢視 最新版本

  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 http://tampermonkey.net/
  8. // @version 5.94
  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. // @icon https://www.google.com/s2/favicons?sz=64&domain=YouTube.com
  17. // @grant none
  18. // @license MIT
  19. // ==/UserScript==
  20. (function() {
  21. `use strict`;
  22.  
  23. //界面广告选择器
  24. const cssSeletorArr = [
  25. `#masthead-ad`,//首页顶部横幅广告.
  26. `ytd-rich-item-renderer.style-scope.ytd-rich-grid-row #content:has(.ytd-display-ad-renderer)`,//首页视频排版广告.
  27. `.video-ads.ytp-ad-module`,//播放器底部广告.
  28. `tp-yt-paper-dialog:has(yt-mealbar-promo-renderer)`,//播放页会员促销广告.
  29. `ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"]`,//播放页右上方推荐广告.
  30. `#related #player-ads`,//播放页评论区右侧推广广告.
  31. `#related ytd-ad-slot-renderer`,//播放页评论区右侧视频排版广告.
  32. `ytd-ad-slot-renderer`,//搜索页广告.
  33. `yt-mealbar-promo-renderer`,//播放页会员推荐广告.
  34. `ad-slot-renderer`,//M播放页第三方推荐广告
  35. `ytm-companion-ad-renderer`,//M可跳过的视频广告链接处
  36. ];
  37. const dev = true;//开发使用
  38.  
  39. /**
  40. * 将标准时间格式化
  41. * @param {Date} time 标准时间
  42. * @param {String} format 格式
  43. * @return {String}
  44. */
  45. function moment(time, format = `YYYY-MM-DD HH:mm:ss`) {
  46. // 获取年⽉⽇时分秒
  47. let y = time.getFullYear()
  48. let m = (time.getMonth() + 1).toString().padStart(2, `0`)
  49. let d = time.getDate().toString().padStart(2, `0`)
  50. let h = time.getHours().toString().padStart(2, `0`)
  51. let min = time.getMinutes().toString().padStart(2, `0`)
  52. let s = time.getSeconds().toString().padStart(2, `0`)
  53. if (format === `YYYY-MM-DD`) {
  54. return `${y}-${m}-${d}`
  55. } else {
  56. return `${y}-${m}-${d} ${h}:${min}:${s}`
  57. }
  58. }
  59.  
  60. /**
  61. * 输出信息
  62. * @param {String} msg 信息
  63. * @return {undefined}
  64. */
  65. function log(msg) {
  66. if(!dev){
  67. return false;
  68. }
  69. console.log(`${moment(new Date())} ${msg}`)
  70. }
  71.  
  72. /**
  73. * 获取当前url的参数,如果要查询特定参数请传参
  74. * @param {String} 要查询的参数
  75. * @return {String || Object}
  76. */
  77. function getUrlParams(param) {
  78. // 通过 ? 分割获取后面的参数字符串
  79. let urlStr = location.href.split(`?`)[1]
  80. if(!urlStr){
  81. return ``;
  82. }
  83. // 创建空对象存储参数
  84. let obj = {};
  85. // 再通过 & 将每一个参数单独分割出来
  86. let paramsArr = urlStr.split(`&`)
  87. for(let i = 0,len = paramsArr.length;i < len;i++){
  88. // 再通过 = 将每一个参数分割为 key:value 的形式
  89. let arr = paramsArr[i].split(`=`)
  90. obj[arr[0]] = arr[1];
  91. }
  92.  
  93. if(!param){
  94. return obj;
  95. }
  96.  
  97. return obj[param]||``;
  98. }
  99.  
  100. /**
  101. * 生成去除广告的css元素style并附加到HTML节点上
  102. * @param {String} styles 样式文本
  103. * @return {undefined}
  104. */
  105. function generateRemoveADHTMLElement(styles) {
  106. //如果已经设置过,退出.
  107. if (document.getElementById(`RemoveADHTMLElement`)) {
  108. log(`屏蔽页面广告节点已生成`);
  109. return false
  110. }
  111.  
  112. //设置移除广告样式.
  113. let style = document.createElement(`style`);//创建style元素.
  114. style.id = `RemoveADHTMLElement`;
  115. (document.querySelector(`head`) || document.querySelector(`body`)).appendChild(style);//将节点附加到HTML.
  116. style.appendChild(document.createTextNode(styles));//附加样式节点到元素节点.
  117. log(`生成屏蔽页面广告节点成功`)
  118.  
  119. }
  120.  
  121. /**
  122. * 生成去除广告的css文本
  123. * @param {Array} cssSeletorArr 待设置css选择器数组
  124. * @return {String}
  125. */
  126. function generateRemoveADCssText(cssSeletorArr){
  127. cssSeletorArr.forEach((seletor,index)=>{
  128. cssSeletorArr[index]=`${seletor}{display:none!important}`;//遍历并设置样式.
  129. });
  130. return cssSeletorArr.join(` `);//拼接成字符串.
  131. }
  132.  
  133. /**
  134. * 触摸事件
  135. * @return {undefined}
  136. */
  137. function nativeTouch(){
  138. const minNum = 100;
  139. const maxNum = 999;
  140. const randomNum = (Math.floor(Math.random() * (maxNum - minNum + 1)) + minNum)/1000;
  141.  
  142. let element =this;
  143. // 创建 Touch 对象
  144. let touch = new Touch({
  145. identifier: Date.now(),
  146. target: element,
  147. clientX: 111+randomNum,
  148. clientY: 222+randomNum,
  149. radiusX: 333+randomNum,
  150. radiusY: 444+randomNum,
  151. rotationAngle: 0,
  152. force: 1
  153. });
  154.  
  155. // 创建 TouchEvent 对象
  156. let touchStartEvent = new TouchEvent("touchstart", {
  157. bubbles: true,
  158. cancelable: true,
  159. view: window,
  160. touches: [touch],
  161. targetTouches: [touch],
  162. changedTouches: [touch]
  163. });
  164.  
  165. // 分派 touchstart 事件到目标元素
  166. element.dispatchEvent(touchStartEvent);
  167.  
  168. // 创建 TouchEvent 对象
  169. let touchEndEvent = new TouchEvent("touchend", {
  170. bubbles: true,
  171. cancelable: true,
  172. view: window,
  173. touches: [],
  174. targetTouches: [],
  175. changedTouches: [touch]
  176. });
  177.  
  178. // 分派 touchend 事件到目标元素
  179. element.dispatchEvent(touchEndEvent);
  180. }
  181.  
  182. /**
  183. * 跳过广告
  184. * @return {undefined}
  185. */
  186. function skipAd(mutationsList, observer) {
  187. let video = document.querySelector(`.ad-showing video`) || document.querySelector(`video`);//获取视频节点
  188. let skipButton = document.querySelector(`.ytp-ad-skip-button`);
  189. let shortAdMsg = document.querySelector(`.video-ads.ytp-ad-module .ytp-ad-player-overlay`);
  190.  
  191. if(!skipButton && !shortAdMsg){
  192. log(`######广告不存在######`);
  193. return false;
  194. }
  195.  
  196. //拥有跳过按钮的广告.
  197. if(skipButton)
  198. {
  199. log(`普通视频广告~~~~~~~~~~~~~`);
  200. log(`总时长:`);
  201. log(`${video.duration}`)
  202. log(`当前时间:`);
  203. log(`${video.currentTime}`)
  204. // 跳过广告.
  205. skipButton.click();//PC
  206. nativeTouch.call(skipButton);//Phone
  207. log(`按钮跳过了该广告~~~~~~~~~~~~~`);
  208. return false;//终止
  209. }
  210.  
  211. //没有跳过按钮的短广告.
  212. if(shortAdMsg){
  213. log(`强制视频广告~~~~~~~~~~~~~`);
  214. log(`总时长:`);
  215. log(`${video.duration}`)
  216. log(`当前时间:`);
  217. log(`${video.currentTime}`)
  218. video.currentTime = video.duration;
  219. log(`强制结束了该广告~~~~~~~~~~~~~`);
  220. return false;//终止
  221. }
  222. }
  223.  
  224. /**
  225. * 去除播放中的广告
  226. * @return {undefined}
  227. */
  228. function removePlayerAD(){
  229.  
  230. //如果已经在运行,退出.
  231. if (document.getElementById(`removePlayerAD`)) {
  232. log(`去除播放中的广告功能已在运行`);
  233. return false
  234. }
  235. //设置运行tag.
  236. let style = document.createElement(`style`);
  237. style.id = `removePlayerAD`;
  238. (document.querySelector(`head`) || document.querySelector(`body`)).appendChild(style);//将节点附加到HTML.
  239.  
  240. let observer;//监听器
  241.  
  242. //开始监听
  243. function startObserve(){
  244. //广告节点监听
  245. const targetNode = document.querySelector(`.video-ads.ytp-ad-module`);
  246.  
  247. //这个视频不存在广告
  248. if(!targetNode){
  249. log(`这个视频不存在广告`);
  250. return false;
  251. }
  252.  
  253. //监听视频中的广告并处理
  254. const config = {childList: true, subtree: true };// 监听目标节点本身与子树下节点的变动
  255. observer = new MutationObserver(skipAd);// 创建一个观察器实例并设置处理广告的回调函数
  256. observer.observe(targetNode, config);// 以上述配置开始观察广告节点
  257.  
  258. //初始化监听,发现并处理广告
  259. let skipButton = document.querySelector(`.ytp-ad-skip-button`);
  260. let shortAdMsg = document.querySelector(`.video-ads.ytp-ad-module .ytp-ad-player-overlay`);
  261. if(skipButton || shortAdMsg){
  262. log(`初始化监听,发现并处理广告`);
  263. skipAd();
  264. }else{
  265. log(`初始化监听,没有发现广告并将在下一tick再次检查`);
  266. setTimeout(skipAd);//设置宏任务以防节点在同步代码和微任务之间更新时MutationObserver监听不到。
  267. }
  268.  
  269. }
  270.  
  271. //结束监听
  272. function closeObserve(){
  273. observer.disconnect();
  274. observer = null;
  275. }
  276.  
  277. //轮询任务
  278. setInterval(function(){
  279. //视频播放页
  280. if(getUrlParams(`v`)){
  281. if(observer){
  282. return false;
  283. }
  284. startObserve();
  285. }else{
  286. //其它界面
  287. if(!observer){
  288. return false;
  289. }
  290. closeObserve();
  291. }
  292. },16);
  293.  
  294. log(`运行去除播放中的广告功能成功`)
  295. }
  296.  
  297. /**
  298. * main函数
  299. */
  300. function main(){
  301. generateRemoveADHTMLElement(generateRemoveADCssText(cssSeletorArr));//移除界面中的广告.
  302. removePlayerAD();//移除播放中的广告.
  303. }
  304.  
  305. if (document.readyState === `loading`) {
  306. log(`YouTube去广告脚本即将调用:`);
  307. document.addEventListener(`DOMContentLoaded`, main);// 此时加载尚未完成
  308. } else {
  309. log(`YouTube去广告脚本快速调用:`);
  310. main();// 此时`DOMContentLoaded` 已经被触发
  311. }
  312.  
  313. })();

QingJ © 2025

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