Youtube short remover

Removes Youtube shorts from search results and watch page. Configuration Menu to the Settings at https://www.youtube.com/account_playback

  1. // ==UserScript==
  2. // @name Youtube short remover
  3. // @namespace http://tampermonkey.net/
  4. // @version full.1.1
  5. // @description Removes Youtube shorts from search results and watch page. Configuration Menu to the Settings at https://www.youtube.com/account_playback
  6. // @author Mr_Comand
  7. // @license MIT
  8. // @match https://www.youtube.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // ==/UserScript==
  13.  
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. function log(...args) {
  19. const message = args.map(arg => String(arg)).join(' ');
  20. console.log('%c[ShortsRemover] '+message, 'color: ' + config.c_consoleColor);
  21. }
  22.  
  23. function updateConfig(key, value) {
  24. if (config.hasOwnProperty(key)) {
  25. config[key] = value;
  26. GM_setValue(key, value); // Update the value in GM storage
  27. }
  28. }
  29.  
  30.  
  31. // Configuration variables with default values
  32. var config = {
  33. c_removeFormStartPage: GM_getValue('c_removeFormStartPage', true),
  34. c_removeFormSubscriptionFeed: GM_getValue('c_removeFormSubscriptionFeed', true),
  35. c_removeFormAllFeeds: GM_getValue('c_removeFormAllFeeds', true), // except SubscriptionFeed
  36. c_removeFormFollowUp: GM_getValue('c_removeFormFollowUp', true),
  37. c_removeFormChannel: GM_getValue('c_removeFormChannel', true),
  38. c_removeSidebar: GM_getValue('c_removeSidebar', true),
  39. c_disableShortPage: GM_getValue('c_disableShortPage', true),
  40. c_removeFormSearch : GM_getValue('c_removeFormSearch', true),
  41. c_disableShortPageScrolling: GM_getValue('c_disableShortPageScrolling', true),
  42. c_consoleColor: GM_getValue('c_consoleColor', '#33bd52')
  43. };
  44. log("Configuration:");
  45. log("c_removeFormStartPage", config.c_removeFormStartPage);
  46. log("c_removeFormSubscriptionFeed", config.c_removeFormSubscriptionFeed);
  47. log("c_removeFormAllFeeds", config.c_removeFormAllFeeds);
  48. log("c_removeFormFollowUp", config.c_removeFormFollowUp);
  49. log("c_removeFormChannel", config.c_removeFormChannel);
  50. log("c_removeSidebar", config.c_removeSidebar);
  51. log("c_removeFormSearch", config.c_removeFormSearch);
  52. log("c_disableShortPage", config.c_disableShortPage);
  53. log("c_disableShortPageScrolling", config.c_disableShortPageScrolling);
  54. log("c_consoleColor", config.c_consoleColor);
  55. log("============================");
  56.  
  57.  
  58. // Define the regex pattern for the YouTube start page
  59. var youtubeStartPagePattern = /^https?:\/\/(www\.)?youtube\.com\/?$/;
  60.  
  61. // Define the regex pattern for the all YouTube feed pages
  62. //https://www.youtube.com/feed/*
  63. var youtubeFeedPagePattern = /^https?:\/\/(www\.)?youtube\.com\/((feed)|(gaming))(?!\/subscriptions.*).*$/;
  64.  
  65. // Define the regex pattern for the YouTube subscriptions feed page
  66. //https://www.youtube.com/feed/subscriptions
  67. var youtubeSubscriptionsPagePattern = /^https?:\/\/(www\.)?youtube\.com\/feed\/subscriptions\/?$/;
  68.  
  69. // Define the regex pattern for the YouTube watch pages
  70. //https://www.youtube.com/watch?v=*
  71. var youtubeWatchPagePattern = /^https?:\/\/(www\.)?youtube\.com\/watch\/?.*$/;
  72.  
  73.  
  74. // Define the regex pattern for the YouTube shorts pages
  75. //https://www.youtube.com/shorts/*
  76. var youtubeShortPagePattern = /^https?:\/\/(www\.)?youtube\.com\/shorts.*$/;
  77.  
  78. // Define the regex pattern for the YouTube channel pages
  79. //https://www.youtube.com/LinusTechTips or https://www.youtube.com/@LinusTechTips ...
  80. var youtubeChannelPagePattern = /^https?:\/\/(www\.)?youtube\.com\/(?!feed.*)(?!watch.*)(?!short.*)(?!playlist.*)(?!podcasts.*)(?!gaming.*)(?!results.*).+$/;
  81. // Define the regex pattern for the YouTube channel short pages
  82. //https://www.youtube.com/LinusTechTips/shorts or https://www.youtube.com/@LinusTechTips/shorts ...
  83. var youtubeChannelShortsPagePattern = /^(https?:\/\/(?:www\.)?youtube\.com\/(?!feed.*)(?!watch.*)(?!short.*)(?!playlist.*)(?!podcasts.*)(?!gaming.*)(?!results.*).+)\/shorts\/?$/;
  84. // Define the regex pattern for the YouTube search page
  85. //https://www.youtube.com/shorts/*
  86. var youtubeSearchPagePattern = /^https?:\/\/(www\.)?youtube\.com\/results.*$/;
  87.  
  88. // Define the regex pattern for the Config pages
  89. //https://www.youtube.com/account_playback
  90. var configPagePattern = /^https:\/\/www\.youtube\.com\/account_playback$/;
  91.  
  92.  
  93. if (config.c_disableShortPageScrolling){
  94. // Function to handle the custom scroll event
  95. function handleScroll(event) {
  96. if (youtubeShortPagePattern.test(window.location.href)) {
  97.  
  98. // Your custom scroll handling code goes here
  99. log("Scrolling is disabled.");
  100. sendToHome();
  101. // Prevent the default scroll behavior to disable other scroll listeners
  102. event.preventDefault();
  103. // Add a scroll listener to the window
  104. }
  105. }
  106. window.addEventListener('scroll', handleScroll);
  107. window.addEventListener('wheel', handleScroll);
  108. }
  109.  
  110. function removeShorts(){
  111.  
  112. // Get the current URL
  113. var currentURL = window.location.href;
  114. // Check if the current URL matches the YouTube short page URL pattern
  115. if (youtubeShortPagePattern.test(currentURL) && config.c_disableShortPage) {
  116. // URL & config matches
  117. sendToHome();
  118. log("Shorts page detected.");
  119. return;
  120. }
  121. if (configPagePattern.test(currentURL)) {
  122. // URL & config matches
  123. if (document.querySelector("div#contents.style-scope.ytd-section-list-renderer>#yt-short-remover-config-menu")){
  124. log("Config already exists.");
  125. return;
  126. }
  127. createConfigMenu();
  128. log("Config added.");
  129. return;
  130. }
  131. if (config.c_removeSidebar){
  132. removeSidebarElement();
  133. }
  134. // Check if the current URL matches the YouTube start page URL pattern
  135. if (youtubeStartPagePattern.test(currentURL) && config.c_removeFormStartPage) {
  136. // URL & config matches
  137. removeFormVideoOverview();
  138. log("Shorts removed from Startpage.");
  139. return;
  140. }
  141. // Check if the current URL matches the YouTube feed page URL pattern
  142. if (youtubeFeedPagePattern.test(currentURL) && config.c_removeFormAllFeeds) {
  143. // URL & config matches
  144. removeReelShelfRenderer();
  145. removeFormVideoOverview();
  146. log("Shorts removed from Feed.");
  147. return;
  148. }
  149. // Check if the current URL matches the YouTube subscriptions feed page URL pattern
  150. if (youtubeSubscriptionsPagePattern.test(currentURL) && config.c_removeFormSubscriptionFeed) {
  151. // URL & config matches
  152. removeFormVideoOverview();
  153. log("Shorts removed from subscriptions.");
  154. return;
  155. }
  156. // Check if the current URL matches the YouTube watch page URL pattern
  157. if (youtubeWatchPagePattern.test(currentURL) && config.c_removeFormFollowUp) {
  158. // URL & config matches
  159. removeReelShelfRenderer();
  160. return;
  161. }
  162.  
  163. if (youtubeChannelPagePattern.test(currentURL) && config.c_removeFormChannel) {
  164. const match = youtubeChannelShortsPagePattern.exec(currentURL);
  165. if (match) {
  166. window.location.href = match[1];
  167. }
  168. // URL & config matches
  169. removeReelShelfRenderer();
  170. removeFormVideoOverview();
  171. // Select all elements with tab-title Shorts
  172. var elementsToRemove = document.querySelectorAll('[tab-title="Shorts"]');
  173.  
  174. // Loop through each selected element and remove it
  175. elementsToRemove.forEach(function (element) {
  176. element.style.display = "none";
  177. });
  178.  
  179. log("Shorts removed from channel.");
  180. return;
  181. }
  182. if (youtubeSearchPagePattern.test(currentURL) && config.c_removeFormSearch) {
  183. // URL & config matches
  184. removeReelShelfRenderer();
  185. removeFormVideoOverview();
  186. removeByUrl();
  187. return;
  188. }
  189.  
  190. }
  191.  
  192.  
  193. // Remove shorts on videoOverview
  194. function removeFormVideoOverview() {
  195. // Select all elements with a specific attribute
  196. var elementsToRemove = document.querySelectorAll('[is-shorts],[is-reel-item-style-avatar-circle],ytd-reel-item-renderer');
  197.  
  198. // Loop through each selected element and remove it
  199. elementsToRemove.forEach(function (element) {
  200. element.parentNode.removeChild(element);
  201. });
  202.  
  203. }
  204.  
  205. //Remove Sidebar Element Shorts
  206. function removeSidebarElement() {
  207. // Select all elements with a title Shorts and specific class names
  208. var elementsToRemove = document.querySelectorAll('.yt-simple-endpoint.style-scope.ytd-guide-entry-renderer[title="Shorts"]');
  209.  
  210. // Loop through each selected element and remove the parent
  211. elementsToRemove.forEach(function (element) {
  212. element.parentNode.parentNode.removeChild(element.parentNode);
  213. });
  214. }
  215.  
  216.  
  217. //Remove shorts from video recommendations of a video
  218. function removeReelShelfRenderer() {
  219. // Select all "ytd-reel-shelf-renderer" elements
  220. var elementsToRemove = document.querySelectorAll('ytd-reel-shelf-renderer');
  221.  
  222. // Loop through each selected element and remove it
  223. elementsToRemove.forEach(function (element) {
  224. element.parentNode.removeChild(element);
  225. });
  226. }
  227. function removeByUrl() {
  228. // Select all "ytd-video-renderer" elements containing a a element wit a herf containing /shorts/
  229. var elementsToRemove = document.querySelectorAll('ytd-video-renderer:has([href*="/shorts/"])');
  230. // Loop through each selected element and remove it
  231. elementsToRemove.forEach(function (element) {
  232. element.parentNode.removeChild(element);
  233. });
  234. }
  235. function sendToHome(){
  236. window.location.href = "https://www.youtube.com/";
  237. }
  238.  
  239.  
  240. let progress = null;
  241. let timeoutId;
  242. function handleMutations(mutationsList, observer) {
  243. if (progress==null){
  244. progress = document.querySelector('yt-page-navigation-progress>#progress');
  245. if (progress != null){
  246. observer.observe(progress, observer_config);
  247. log("Added observer");
  248. }
  249. }
  250. for(let mutation of mutationsList) {
  251. if (mutation.target.id=="progress"){
  252. clearTimeout(timeoutId);
  253. timeoutId = setTimeout(() => {
  254. // Run your script here after all changes are done
  255. removeShorts();
  256. log("All Shorts are removed, detected by progress bar.");
  257. }, 500);
  258. break;
  259. }
  260. if (mutation.target.tagName=="YTD-VIDEO-RENDERER"){
  261. clearTimeout(timeoutId);
  262. timeoutId = setTimeout(() => {
  263. // Run your script here after all changes are done
  264. removeShorts();
  265. log("All Shorts are removed, detected by video renderer.");
  266. }, 500);
  267. break;
  268. }
  269. if(mutation.target.parentElement ==null ){}else
  270. if (mutation.target.parentElement.id === "page-manager" || (mutation.target.parentElement.id=="primary")) {
  271. // console.log('Changes detected', mutationsList);
  272. //console.log('Changes detected in ytd-page-manager');
  273. // Your code to execute when ytd-page-manager changes goes here
  274. clearTimeout(timeoutId);
  275. timeoutId = setTimeout(() => {
  276. // Run your script here after all changes are done
  277. removeShorts();
  278. log("All Shorts are removed, detected by page-manager.");
  279. }, 500); // Adjust the timeout duration as needed
  280. //end loop if one change is a direct child of #page-manager
  281. break;
  282. }
  283. }
  284. }
  285.  
  286. // Create a MutationObserver instance
  287. const observer = new MutationObserver(handleMutations);
  288.  
  289. // Select the target node
  290. const targetNode = document.querySelector('#page-manager');
  291. // yt-page-navigation-progress#progress
  292. // Options for the observer (which mutations to observe)
  293. const observer_config = { childList: true, attributes: true, subtree: true,};
  294. // Start observing the target node for configured mutations
  295. observer.observe(targetNode, observer_config);
  296. removeShorts();
  297. if (config.c_removeSidebar){
  298. removeSidebarElement();
  299. }
  300.  
  301. function createConfigMenu() {
  302. // Create menu container
  303. var menuContainer = document.createElement('div');
  304. menuContainer.id = 'yt-short-remover-config-menu';
  305. menuContainer.style.color = 'var(--yt-spec-text-secondary)';
  306. menuContainer.style.font_size = '1.4rem';
  307. menuContainer.style.lineHeight = '2rem';
  308. menuContainer.style.fontWeight = '400';
  309. menuContainer.style.padding = '10px';
  310. menuContainer.style.width = '50%';
  311.  
  312. var hr = document.createElement('hr');
  313. var headline = document.createElement('h1');
  314. headline.style.lineHeight="6rem";
  315. headline.style.color="var(--yt-spec-text-primary)";
  316. headline.innerText="Shorts(Tampermonkey script)";
  317. menuContainer.appendChild(hr);
  318. menuContainer.appendChild(headline);
  319.  
  320. // Create menu items
  321. var menuItems = [
  322. { label: 'Remove Shorts from Start Page', key: 'c_removeFormStartPage', type:"checkbox"},
  323. { label: 'Remove Shorts from Subscription Feed', key: 'c_removeFormSubscriptionFeed', type:"checkbox" },
  324. { label: 'Remove Shorts from All Feeds (except Subscription Feed)', key: 'c_removeFormAllFeeds', type:"checkbox" },
  325. { label: 'Remove Shorts from Follow Up Page', key: 'c_removeFormFollowUp', type:"checkbox" },
  326. { label: 'Remove Shorts from Channel Page', key: 'c_removeFormChannel', type:"checkbox" },
  327. { label: 'Remove Shorts from Search Page', key: 'c_removeFormSearch', type:"checkbox" },
  328. { label: 'Remove Sidebar Shorts', key: 'c_removeSidebar', type:"checkbox" },
  329. { label: 'Disable Short Page', key: 'c_disableShortPage', type:"checkbox" },
  330. { label: 'Disable Short Page Scrolling', key: 'c_disableShortPageScrolling', type:"checkbox" },
  331. { label: 'Console log Color:', key: 'c_consoleColor', type:"color", default:"#33bd52" }
  332. ];
  333.  
  334. // Add menu items to menu container
  335. menuItems.forEach(item => {
  336. var input = document.createElement('input');
  337. input.type = item.type
  338. if (input.type == "checkbox"){
  339. input.checked = GM_getValue(item.key, item.default ? item.default : true);
  340. }else{
  341. input.value = GM_getValue(item.key, item.default ? item.default : "undefine");
  342. }
  343. input.addEventListener('change', function() {
  344. if (this.type == "checkbox"){
  345. updateConfig(item.key, this.checked);
  346. log(item.key, "changed to", this.checked)
  347. }else{
  348. updateConfig(item.key, this.value);
  349. log(item.key, "changed to", this.value)
  350. }
  351. });
  352.  
  353. var label = document.createElement('label');
  354. label.textContent = item.label;
  355.  
  356. var menuItem = document.createElement('div');
  357. menuItem.style.display="flex";
  358. menuItem.style.justifyContent="space-between";
  359. menuItem.style.alignItems="center"
  360.  
  361. menuItem.appendChild(label);
  362. menuItem.appendChild(input);
  363. menuContainer.appendChild(menuItem);
  364. });
  365. config.c_consoleColor
  366. // Append menu container to config element
  367. document.querySelector("div#contents.style-scope.ytd-section-list-renderer").appendChild(menuContainer);
  368. }
  369.  
  370. })();