Auto Close Deez nutz

Close and/or Mute YouTube ads automatically!

  1. // ==UserScript==
  2. // @name Auto Close Deez nutz
  3. // @namespace http://fuzetsu.acypa.com
  4. // @version 1.1.13
  5. // @description Close and/or Mute YouTube ads automatically!
  6. // @author fuzetsu
  7. // @match https://*.youtube.com/*
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant GM_deleteValue
  11. // @grant GM_registerMenuCommand
  12. // @require https://greasyfork.org/scripts/5679-wait-for-elements/code/Wait%20For%20Elements.js?version=122976
  13. // ==/UserScript==
  14.  
  15. var Util = {
  16. log: function () {
  17. var args = [].slice.call(arguments);
  18. args.unshift('%c' + SCRIPT_NAME + ':', 'font-weight: bold;color: purple;');
  19. console.log.apply(console, args);
  20. },
  21. clearTicks: function(ticks) {
  22. ticks.forEach(function(tick) {
  23. if(typeof tick === 'number') {
  24. clearInterval(tick);
  25. } else {
  26. tick.stop();
  27. }
  28. });
  29. ticks.length = 0;
  30. },
  31. keepTrying: function(wait, action) {
  32. var tick = setInterval(function() {
  33. if(action()) {
  34. clearInterval(tick);
  35. }
  36. }, wait);
  37. },
  38. storeGet: function(key) {
  39. if (typeof GM_getValue === "undefined") {
  40. var value = localStorage.getItem(key);
  41. if (value === "true" || value === "false") {
  42. return (value === "true") ? true : false;
  43. }
  44. return value;
  45. }
  46. return GM_getValue(key);
  47. },
  48. storeSet: function(key, value) {
  49. if (typeof GM_setValue === "undefined") {
  50. return localStorage.setItem(key, value);
  51. }
  52. return GM_setValue(key, value);
  53. },
  54. storeDel: function(key) {
  55. if (typeof GM_deleteValue === "undefined") {
  56. return localStorage.removeItem(key);
  57. }
  58. return GM_deleteValue(key);
  59. },
  60. q: function(query, context) {
  61. return (context || document).querySelector(query);
  62. },
  63. qq: function(query, context) {
  64. return [].slice.call((context || document).querySelectorAll(query));
  65. }
  66. };
  67.  
  68. var SCRIPT_NAME = 'Auto Close YouTube Ads';
  69. var SEC_WAIT = parseInt(Util.storeGet('SEC_WAIT'));
  70. var MUTE_AD = Util.storeGet('MUTE_AD') || true;
  71. var MUTE_BUTTON_SELECTOR = '.ytp-mute-button';
  72. var ticks = [];
  73. var videoUrl;
  74.  
  75. if(!SEC_WAIT && SEC_WAIT !== 0) SEC_WAIT = 0.1;
  76.  
  77. function waitForAds() {
  78. ticks.push(
  79. waitAndClick('.videoAdUiSkipButton', function(btn) {
  80. Util.keepTrying(1000, function() {
  81. btn.click();
  82. if(!Util.q('.videoAdUiSkipButton')) {
  83. return true;
  84. }
  85. });
  86. }),
  87. waitAndClick('a.close-button', function(btn) {
  88. Util.q('div.recall-button').remove();
  89. })
  90. );
  91. if(MUTE_AD) {
  92. ticks.push(waitForElems('.videoAdUi', function(ad) {
  93. var muteButton = Util.q(MUTE_BUTTON_SELECTOR);
  94. if(muteButton.title === 'Unmute') {
  95. Util.log('Video ad detected, audio already muted so respecting user setting');
  96. return;
  97. }
  98. // mute the ad
  99. muteButton.click();
  100. Util.log('Video ad detected, muting audio');
  101. // wait for the ad to dissapear before unmuting
  102. Util.keepTrying(500, function() {
  103. if(!Util.q('.videoAdUi')) {
  104. if(muteButton.title === 'Unmute') {
  105. muteButton.click();
  106. Util.log('Video ad ended, unmuting audio');
  107. } else {
  108. Util.log('Video ad ended, audio already unmuted');
  109. }
  110. return true;
  111. }
  112. });
  113. }));
  114. }
  115. }
  116.  
  117. function waitAndClick(sel, cb, extraWait) {
  118. return waitForElems(sel, function(btn) {
  119. Util.log('Found ad, closing in', SEC_WAIT, 'seconds');
  120. setTimeout(function() {
  121. btn.click();
  122. if(cb) {
  123. cb(btn);
  124. }
  125. }, SEC_WAIT * 1000 + (extraWait || 0));
  126. });
  127. }
  128.  
  129. Util.log('Started');
  130.  
  131. if(window.self === window.top) {
  132. waitForUrl(/^https:\/\/www\.youtube\.com\/watch\?.*v=.+/, function() {
  133. if(videoUrl && location.href !== videoUrl) {
  134. Util.log('Changed video, removing old wait');
  135. Util.clearTicks(ticks);
  136. }
  137. videoUrl = location.href;
  138. Util.log('Entered video, waiting for ads');
  139. waitForAds();
  140. ticks.push(
  141. waitForUrl(function(url) {
  142. return url !== videoUrl;
  143. }, function() {
  144. videoUrl = null;
  145. Util.clearTicks(ticks);
  146. Util.log('Left video, stopped waiting for ads');
  147. }, true)
  148. );
  149. });
  150. } else {
  151. if(/https:\/\/www\.youtube\.com\/embed\//.test(location.href)) {
  152. Util.log('Found embedded video, waiting for ads');
  153. waitForAds();
  154. }
  155. }
  156.  
  157. // register menu commands
  158. GM_registerMenuCommand(SCRIPT_NAME + ': set ad close delay', function() {
  159. var wait = parseInt(prompt('Current setting is ' + SEC_WAIT + ' seconds. Enter the number of seconds you would like the script to wait before closing an ad. 0 means no delay.'));
  160. if(!isNaN(wait)) {
  161. SEC_WAIT = wait;
  162. Util.storeSet('SEC_WAIT', SEC_WAIT);
  163. }
  164. });
  165. GM_registerMenuCommand(SCRIPT_NAME + ': ' + (MUTE_AD ? 'disable' : 'enable') + ' video ad muting (warning clicking this will refresh the page)', function() {
  166. Util.storeSet('MUTE_AD', !MUTE_AD);
  167. location.reload();
  168. });