Youtube video blocker

Allows you to block videos from youtube

  1. // ==UserScript==
  2. // @name Youtube video blocker
  3. // @namespace Danielv123
  4. // @version 1.4
  5. // @description Allows you to block videos from youtube
  6. // @author Danielv123
  7. // @match https://www.youtube.com/*
  8. // @grant none
  9. // @require https://code.jquery.com/jquery.js
  10. // ==/UserScript==
  11.  
  12. //'use strict';
  13. /*
  14. Videos in sidebar
  15.  
  16. Get video ID
  17. document.querySelectorAll(".video-list-item .yt-uix-simple-thumb-wrap")[0].dataset.vid
  18. Get video title
  19. document.querySelectorAll(".video-list-item .content-wrapper a:nth-child(1)")[2].title
  20. Get username
  21. document.querySelectorAll(".video-list-item .content-wrapper .stat.attribution span")[2].innerHTML
  22. Hide
  23. document.querySelectorAll(".video-list-item")[2].style.display = "none";
  24. */
  25. var sidebar = {
  26. ID: function(x) {
  27. return document.querySelectorAll(".video-list-item.related-list-item-compact-video > .related-item-dismissable .yt-uix-simple-thumb-wrap")[x].dataset.vid;
  28. },
  29. title: function(x) {
  30. if(document.querySelectorAll(".video-list-item.related-list-item-compact-video > .related-item-dismissable > .content-wrapper a:nth-child(1)")[x]){
  31. return document.querySelectorAll(".video-list-item.related-list-item-compact-video > .related-item-dismissable > .content-wrapper a:nth-child(1)")[x].title;
  32. } else {
  33. return "";
  34. }
  35. },
  36. username: function(x) {
  37. let y=document.querySelectorAll(".video-list-item.related-list-item-compact-video > .related-item-dismissable > .content-wrapper .stat.attribution span")[x];
  38. if(y) return y.innerHTML;
  39. return "";
  40. },
  41. hide: function(x) {
  42. document.querySelectorAll(".video-list-item.related-list-item-compact-video")[x].style.display = "none";
  43. }
  44. };
  45. /*
  46. Videos on subscriptions page
  47.  
  48. Get video ID
  49. document.querySelectorAll(".yt-shelf-grid-item > div")[0].dataset.contextItemId
  50. Get video title
  51. document.querySelectorAll(".yt-shelf-grid-item .yt-lockup-content .yt-lockup-title a")[0].title
  52. Get username
  53. document.querySelectorAll(".yt-shelf-grid-item .yt-lockup-content .yt-lockup-byline a")[0].innerHTML
  54. Hide
  55. document.querySelectorAll(".yt-shelf-grid-item")[0].style.display = "none"
  56. */
  57. var shelfvideos = {
  58. ID: function(x) {
  59. return document.querySelectorAll(".yt-shelf-grid-item > div")[x].dataset.contextItemId;
  60. },
  61. title: function(x) {
  62. if(document.querySelectorAll(".yt-shelf-grid-item .yt-lockup-content .yt-lockup-title a")[x] && document.querySelectorAll(".yt-shelf-grid-item .yt-lockup-content .yt-lockup-title a")[x].title){
  63. return document.querySelectorAll(".yt-shelf-grid-item .yt-lockup-content .yt-lockup-title a")[x].title;
  64. }
  65. },
  66. username: function(x) {
  67. return document.querySelectorAll(".yt-shelf-grid-item .yt-lockup-content .yt-lockup-byline a")[x].innerHTML;
  68. },
  69. hide: function(x) {
  70. document.querySelectorAll(".yt-shelf-grid-item")[x].style.display = "none";
  71. }
  72. };
  73. /*
  74. Videos on autolplay list
  75.  
  76. Title
  77. document.querySelector("#watch-related > li:nth-child(1) > div.related-item-dismissable > div.content-wrapper > a > span.title")
  78. Username
  79. document.querySelector("#watch7-sidebar-modules > div:nth-child(1) > div > div.watch-sidebar-body > ul > li > div.content-wrapper > a > span.stat.attribution > span")
  80.  
  81. */
  82. // Code goes here and onwards
  83.  
  84. function main() {
  85. if(!localStorage.blockedUsers){
  86. localStorage.blockedUsers = JSON.stringify([]);
  87. }
  88. if(!localStorage.blockedTitles){
  89. localStorage.blockedTitles = JSON.stringify([]);
  90. }
  91. if(!localStorage.blockedRegex){
  92. localStorage.blockedRegex = JSON.stringify([]);
  93. }
  94. //hideSidebarVideos("","PewDiePie");
  95. //hideShelfVideos("","nigahiga");
  96. //hideShelfVideos("","","s");
  97.  
  98. // Hide videos
  99. let userFilters = JSON.parse(localStorage.blockedUsers);
  100. let titleFilters = JSON.parse(localStorage.blockedTitles);
  101. let regexFilters = JSON.parse(localStorage.blockedRegex);
  102. // Loop until we are through the largest of our filter sets
  103. for(let i = 0; i < Math.max(regexFilters.length, Math.max(userFilters.length, titleFilters.length));i++){
  104. // hideSidebarVideos(id, username, title, regex)
  105. let id = "";
  106. let user = userFilters[i] || "";
  107. let title = titleFilters[i] || "";
  108. let regex = regexFilters[i] || "";
  109. hideSidebarVideos(id, user, title);
  110. hideShelfVideos(id, user, title);
  111. // only responds to title, but whatever.
  112. hideEndscreenVideos(undefined, undefined, title);
  113. }
  114. }
  115. setTimeout(main, 500);
  116. injectScripts();
  117. makeFilterGui();
  118.  
  119. function makeFilterGui() {
  120. let HTML = '<div id="youtubeBlockerGui" style="">';
  121. // Close button
  122. HTML += '<div id="youtubeBlockerGuiCloseButton"><img src="http://www.drodd.com/images14/x23.png"></img></div>';
  123. HTML += '<h1>Youtube Blocker</h1><p>Below are a few textfields. Enter , (comma) seperated lists of titles and/or channels to exclude from your youtube feed.</p><p>Video titles don\'t have to match completely, just having part of the title is enough. Ex "porn,1000 degree,prank" will match "1000 degree knife vs water baloon EPIC"<br>';
  124. HTML += '<h2>Blocked channels</h2><p><i>All channels with this excact name will be hidden</i></p><textarea id="blockedChannels"></textarea>';
  125. HTML += '<h2>Blocked videos by name</h2><p><i>All videos with this in their names will be hidden</i></p><textarea id="blockedVideos"></textarea>';
  126. HTML += '<h2>Regex blocks</h2><p><i>All video titles matching this regex will be hidden</i></p><textarea id="blockedRegex"></textarea>';
  127. // Save button
  128. HTML += '<div id="saveButton"><h2>Save</h2></div>';
  129. // Close everything
  130. HTML += '</div>';
  131. // Open settings button in botttom right corner
  132. HTML += '<div id="gearwheelButton"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Simpleicons_Interface_gear-wheel-in-black.svg/2000px-Simpleicons_Interface_gear-wheel-in-black.svg.png"></img></div>';
  133. // inject into body
  134. let container = document.createElement('div');
  135. container.innerHTML = HTML;
  136. (document.body || document.head || document.documentElement).appendChild(container);
  137.  
  138. // Populate textareas with old settings
  139. let x = JSON.parse(localStorage.blockedUsers);
  140. let y = "";
  141. for(let i in x) {
  142. y += x[i]+",\n";
  143. }
  144. $("textarea#blockedChannels")[0].value = y;
  145. x = JSON.parse(localStorage.blockedTitles);
  146. y = "";
  147. for(let i in x) {
  148. y += x[i]+",\n";
  149. }
  150. $("textarea#blockedVideos")[0].value = y;
  151. x = JSON.parse(localStorage.blockedRegex);
  152. y = "";
  153. for(let i in x) {
  154. y += x[i]+",\n";
  155. }
  156. $("textarea#blockedRegex")[0].value = y;
  157.  
  158. document.querySelector("#youtubeBlockerGui #youtubeBlockerGuiCloseButton").onclick = toggleGuiDisplay;
  159. document.querySelector("#gearwheelButton").onclick = toggleGuiDisplay;
  160. document.querySelector("#saveButton").onclick = function(){
  161. console.log("Saving lists of blocked channels, users and regexes");
  162. // Regex to replace linebreaks
  163. localStorage.blockedUsers = JSON.stringify($("textarea#blockedChannels")[0].value.replace(/(\r\n|\n|\r)/gm,"").split(","));
  164. localStorage.blockedTitles = JSON.stringify($("textarea#blockedVideos")[0].value.replace(/(\r\n|\n|\r)/gm,"").split(","));
  165. localStorage.blockedRegex = JSON.stringify($("textarea#blockedRegex")[0].value.replace(/(\r\n|\n|\r)/gm,"").split(","));
  166. };
  167. }
  168. function readBlockSettings(){
  169. return $("#youtubeBlockerGui textarea")[0].value.replace(/(\r\n|\n|\r)/gm,"").split(",");
  170. }
  171. function toggleGuiDisplay(){
  172. let gui = $("#youtubeBlockerGui")[0];
  173. if(gui.style.display == "none"){
  174. gui.style.display = "block";
  175. console.log("showing blocker gui");
  176. } else {
  177. gui.style.display = "none";
  178. console.log("hiding blocker gui");
  179. }
  180. }
  181. function injectScripts(){
  182. // jQuery
  183. var s=document.createElement('script');
  184. s.setAttribute('src','https://code.jquery.com/jquery.js');
  185. document.getElementsByTagName('head')[0].appendChild(s);
  186. // CSS
  187. var d=document.createElement('style');
  188. d.innerHTML = "#youtubeBlockerGui {"+
  189. "display:none;"+
  190. "width:60%;"+
  191. "height:80%;"+
  192. "background-color:white;"+
  193. "position:fixed;"+
  194. "top:10%;"+
  195. "left:20%;"+
  196. "border:1px solid lightgrey;"+
  197. // damn, the youtube sidebar apparently has a z index of 1999999999...
  198. "z-index:2000000000;"+
  199. "}"+
  200. // red X on gui to close
  201. "#youtubeBlockerGuiCloseButton {"+
  202. "float:right;"+
  203. "height:30px;"+
  204. "width:30px;"+
  205. "cursor:pointer;"+
  206. "}"+
  207. "#youtubeBlockerGuiCloseButton img {"+
  208. "height:100%;"+
  209. "width:100%;"+
  210. "}"+
  211. // gearwheel in bottom right corner
  212. "#gearwheelButton {"+
  213. "position:fixed;"+
  214. "height:30px;"+
  215. "width:30px;"+
  216. "bottom:0px;"+
  217. "right:0px;"+
  218. "cursor:pointer;"+
  219. "}"+
  220. "#gearwheelButton img {"+
  221. "height:100%;"+
  222. "width:100%;"+
  223. "}"+
  224. "#saveButton {"+
  225. "cursor:pointer;"+
  226. "}"
  227. ;
  228. document.getElementsByTagName('body')[0].appendChild(d);
  229. }
  230. function hideShelfVideos(id, username,title, regex) {
  231. let videos = document.querySelectorAll(".yt-shelf-grid-item");
  232. for(let i = 0; i < videos.length; i++){
  233. let hideVideo = function(number, filter){
  234. // document.querySelectorAll(".yt-shelf-grid-item")[number].style.display = "none";
  235. shelfvideos.hide(number);
  236. console.log("Filter: " + filter + " Hidden: " + document.querySelectorAll(".yt-shelf-grid-item .yt-lockup-content .yt-lockup-title a")[number].title);
  237. };
  238. // Check by video ID
  239. if(id && id == shelfvideos.ID(i)){
  240. hideVideo(i, id);
  241. }
  242. // Check by channel name
  243. if(username && username.toLowerCase() == shelfvideos.username(i).toLowerCase()){
  244. hideVideo(i, username);
  245. }
  246. // Check if video title contains title
  247. if(title && title != " " && shelfvideos.title(i).toLowerCase().includes(title.toLowerCase())){
  248. hideVideo(i, title);
  249. }
  250. // Check if title matches regex
  251. if(regex && shelfvideos.title(i).match(regex)){
  252. hideVideo(i, regex);
  253. }
  254. }
  255. }
  256. function hideSidebarVideos(id, username, title, regex) {
  257. let videos = document.querySelectorAll(".video-list-item");
  258. for(let i = 0; i < videos.length; i++){
  259. let hideVideo = function(number, filter){
  260. // document.querySelectorAll(".video-list-item")[number].style.display = "none";
  261. sidebar.hide(number);
  262. console.log("Filter: " + filter + " Hidden sidebar: " + document.querySelectorAll(".video-list-item .content-wrapper a:nth-child(1)")[number].title);
  263. };
  264. // Check by video ID
  265. if(id && id == sidebar.ID(i)){
  266. hideVideo(i, id);
  267. }
  268. // Check by channel name
  269. if(username && username.toLowerCase() == sidebar.username(i).toLowerCase()){
  270. hideVideo(i, username);
  271. }
  272. // Check if video title contains title
  273. if(title && title != " " && sidebar.title(i).toLowerCase().includes(title.toLowerCase())){
  274. hideVideo(i, title);
  275. }
  276. // Check if title matches regex
  277. if(regex && sidebar.title(i).match(regex)){
  278. hideVideo(i, regex);
  279. }
  280. }
  281. }
  282. // hide endscreen videos
  283. function hideEndscreenVideos(id, username, title, regex){
  284. let x = document.querySelectorAll('[aria-label].ytp-videowall-still.ytp-suggestion-set');
  285. for(let i = 0;i<x.length;i++){
  286. // if elemnt has aria-label (lots of those that are unrelated) and includes title and title is not falsey (ex "") beause that would trigger on lots of non video things
  287. if(x[i].getAttribute("aria-label") && x[i].getAttribute("aria-label").toLowerCase().includes(title) && title){
  288. x[i].style.display = "none";
  289. console.log("Filter: "+title+" Hidden: " + x[i].getAttribute("aria-label"));
  290. }
  291. }
  292. }
  293.  
  294.  
  295. // https://stackoverflow.com/questions/3219758/detect-changes-in-the-dom
  296. var observeDOM = (function(){
  297. var MutationObserver = window.MutationObserver || window.WebKitMutationObserver,
  298. eventListenerSupported = window.addEventListener;
  299.  
  300. return function(obj, callback){
  301. if( MutationObserver ){
  302. // define a new observer
  303. var obs = new MutationObserver(function(mutations, observer){
  304. if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
  305. callback();
  306. });
  307. // have the observer observe foo for changes in children
  308. obs.observe( obj, { childList:true, subtree:true });
  309. }
  310. else if( eventListenerSupported ){
  311. obj.addEventListener('DOMNodeInserted', callback, false);
  312. obj.addEventListener('DOMNodeRemoved', callback, false);
  313. }
  314. };
  315. })();
  316. observeDOM( document.querySelector("#content") ,function(){
  317. console.log('dom changed, hiding videos...');
  318. main();
  319. });