Youtube short remover

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

// ==UserScript==
// @name         Youtube short remover
// @namespace    http://tampermonkey.net/
// @version      full.1.0
// @description  Removes Youtube shorts from search results and watch page. Configuration Menu to the Settings at https://www.youtube.com/account_playback
// @author       Mr_Comand
// @license      MIT
// @match        https://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==


(function() {
	'use strict';

	function log(...args) {
		const message = args.map(arg => String(arg)).join(' ');
		console.log('%c[ShortsRemover] '+message, 'color: ' + config.c_consoleColor);
	}

	function updateConfig(key, value) {
		if (config.hasOwnProperty(key)) {
			config[key] = value;
			GM_setValue(key, value); // Update the value in GM storage
		}
	}


	// Configuration variables with default values
	var config = {
		c_removeFormStartPage: GM_getValue('c_removeFormStartPage', true),
		c_removeFormSubscriptionFeed: GM_getValue('c_removeFormSubscriptionFeed', true),
		c_removeFormAllFeeds: GM_getValue('c_removeFormAllFeeds', true), // except SubscriptionFeed
		c_removeFormFollowUp: GM_getValue('c_removeFormFollowUp', true),
		c_removeFormChannel: GM_getValue('c_removeFormChannel', true),
		c_removeSidebar: GM_getValue('c_removeSidebar', true),
		c_disableShortPage: GM_getValue('c_disableShortPage', true),
		c_removeFormSearch : GM_getValue('c_removeFormSearch', true),
		c_disableShortPageScrolling: GM_getValue('c_disableShortPageScrolling', true),
		c_consoleColor: GM_getValue('c_consoleColor', '#33bd52')
	};
	log("Configuration:");
	log("c_removeFormStartPage", config.c_removeFormStartPage);
	log("c_removeFormSubscriptionFeed", config.c_removeFormSubscriptionFeed);
	log("c_removeFormAllFeeds", config.c_removeFormAllFeeds);
	log("c_removeFormFollowUp", config.c_removeFormFollowUp);
	log("c_removeFormChannel", config.c_removeFormChannel);
	log("c_removeSidebar", config.c_removeSidebar);
	log("c_removeFormSearch", config.c_removeFormSearch);
	log("c_disableShortPage", config.c_disableShortPage);
	log("c_disableShortPageScrolling", config.c_disableShortPageScrolling);
	log("c_consoleColor", config.c_consoleColor);
	log("============================");


	// Define the regex pattern for the YouTube start page
	var youtubeStartPagePattern = /^https?:\/\/(www\.)?youtube\.com\/?$/;

	// Define the regex pattern for the all YouTube feed pages
	//https://www.youtube.com/feed/*
	var youtubeFeedPagePattern = /^https?:\/\/(www\.)?youtube\.com\/((feed)|(gaming))(?!\/subscriptions.*).*$/;

	// Define the regex pattern for the YouTube subscriptions feed page
	//https://www.youtube.com/feed/subscriptions
	var youtubeSubscriptionsPagePattern = /^https?:\/\/(www\.)?youtube\.com\/feed\/subscriptions\/?$/;

	// Define the regex pattern for the YouTube watch pages
	//https://www.youtube.com/watch?v=*
	var youtubeWatchPagePattern = /^https?:\/\/(www\.)?youtube\.com\/watch\/?.*$/;


	// Define the regex pattern for the YouTube shorts pages
	//https://www.youtube.com/shorts/*
	var youtubeShortPagePattern = /^https?:\/\/(www\.)?youtube\.com\/shorts.*$/;

	// Define the regex pattern for the YouTube channel pages
	//https://www.youtube.com/LinusTechTips or https://www.youtube.com/@LinusTechTips ...
	var youtubeChannelPagePattern = /^https?:\/\/(www\.)?youtube\.com\/(?!feed.*)(?!watch.*)(?!short.*)(?!playlist.*)(?!podcasts.*)(?!gaming.*)(?!results.*).+$/;

     // Define the regex pattern for the YouTube search page
     //https://www.youtube.com/shorts/*
     var youtubeSearchPagePattern = /^https?:\/\/(www\.)?youtube\.com\/results.*$/;

	// Define the regex pattern for the Config pages
	//https://www.youtube.com/account_playback
	var configPagePattern = /^https:\/\/www\.youtube\.com\/account_playback$/;


	if (config.c_disableShortPageScrolling){
		// Function to handle the custom scroll event
		function handleScroll(event) {
			if (youtubeShortPagePattern.test(window.location.href)) {

				// Your custom scroll handling code goes here
				log("Scrolling is disabled.");
				sendToHome();
				// Prevent the default scroll behavior to disable other scroll listeners
				event.preventDefault();
				// Add a scroll listener to the window
			}
		}
		window.addEventListener('scroll', handleScroll);
		window.addEventListener('wheel', handleScroll);
	}

	function removeShorts(){

		// Get the current URL
		var currentURL = window.location.href;
		
		// Check if the current URL matches the YouTube short page URL pattern
		if (youtubeShortPagePattern.test(currentURL) && config.c_disableShortPage) {
			// URL & config matches
			sendToHome();
			log("Shorts page detected.");
			return;
		}
		if (configPagePattern.test(currentURL)) {
			// URL & config matches
			if (document.querySelector("div#contents.style-scope.ytd-section-list-renderer>#yt-short-remover-config-menu")){
				log("Config already exists.");
				return;
			}
			createConfigMenu();
			log("Config added.");
			return;
		}
		if (config.c_removeSidebar){
			removeSidebarElement();
		}
		// Check if the current URL matches the YouTube start page URL pattern
		if (youtubeStartPagePattern.test(currentURL) && config.c_removeFormStartPage) {
			// URL & config matches
			removeFormVideoOverview();
			log("Shorts removed from Startpage.");
			return;
		}
		// Check if the current URL matches the YouTube feed page URL pattern
		if (youtubeFeedPagePattern.test(currentURL) && config.c_removeFormAllFeeds) {
			// URL & config matches
			removeReelShelfRenderer();
			removeFormVideoOverview();
			log("Shorts removed from Feed.");
			return;
		}
		
		// Check if the current URL matches the YouTube subscriptions feed page URL pattern
		if (youtubeSubscriptionsPagePattern.test(currentURL) && config.c_removeFormSubscriptionFeed) {
			// URL & config matches
			removeFormVideoOverview();
			log("Shorts removed from subscriptions.");
			return;
		}
		
		// Check if the current URL matches the YouTube watch page URL pattern
		if (youtubeWatchPagePattern.test(currentURL) && config.c_removeFormFollowUp) {
			// URL & config matches
			removeReelShelfRenderer();
			return;
		}

		if (youtubeChannelPagePattern.test(currentURL)&& config.c_removeFormChannel) {
			// URL & config matches
			removeReelShelfRenderer();
			removeFormVideoOverview();
			// Select all elements with tab-title Shorts
			var elementsToRemove = document.querySelectorAll('[tab-title="Shorts"]');
			
			// Loop through each selected element and remove it
			elementsToRemove.forEach(function (element) {
				element.parentNode.removeChild(element);
			});
			log("Shorts removed from channel.");
			return;
		}
		if (youtubeSearchPagePattern.test(currentURL)&& config.c_removeFormSearch) {
			// URL & config matches
			removeReelShelfRenderer();
			removeFormVideoOverview();
			removeByUrl();
			return;
		}

	}


	// Remove shorts on videoOverview
	function removeFormVideoOverview() {
		// Select all elements with a specific attribute
		var elementsToRemove = document.querySelectorAll('[is-shorts],[is-reel-item-style-avatar-circle],ytd-reel-item-renderer');

		// Loop through each selected element and remove it
		elementsToRemove.forEach(function (element) {
			element.parentNode.removeChild(element);
		});

	}

	//Remove Sidebar Element Shorts
	function removeSidebarElement() {
		// Select all elements with a title Shorts and specific class names
		var elementsToRemove = document.querySelectorAll('.yt-simple-endpoint.style-scope.ytd-guide-entry-renderer[title="Shorts"]');

		// Loop through each selected element and remove the parent
		elementsToRemove.forEach(function (element) {
			element.parentNode.parentNode.removeChild(element.parentNode);
		});
	}


	//Remove shorts from video recommendations of a video
	function removeReelShelfRenderer() {
		// Select all "ytd-reel-shelf-renderer" elements
		var elementsToRemove = document.querySelectorAll('ytd-reel-shelf-renderer');

		// Loop through each selected element and remove it
		elementsToRemove.forEach(function (element) {
			element.parentNode.removeChild(element);
		});
	}
	function removeByUrl() {
		// Select all "ytd-video-renderer" elements containing a a element wit a herf containing /shorts/
		var elementsToRemove = document.querySelectorAll('ytd-video-renderer:has([href*="/shorts/"])');
		// Loop through each selected element and remove it
		elementsToRemove.forEach(function (element) {
			element.parentNode.removeChild(element);
		});
	}
	function sendToHome(){
		window.location.href = "https://www.youtube.com/";
	}


	let progress = null;
	let timeoutId;
	function handleMutations(mutationsList, observer) {
		if (progress==null){
			progress = document.querySelector('yt-page-navigation-progress>#progress'); 
			if (progress != null){
				observer.observe(progress, observer_config);
				log("Added observer");
			}
		}
		for(let mutation of mutationsList) {
			if (mutation.target.id=="progress"){
				clearTimeout(timeoutId);
				timeoutId = setTimeout(() => {
					// Run your script here after all changes are done
					removeShorts();
					log("All Shorts are removed, detected by progress bar.");
				}, 500);
				break;
			}
			if (mutation.target.tagName=="YTD-VIDEO-RENDERER"){
				clearTimeout(timeoutId);
				timeoutId = setTimeout(() => {
				     // Run your script here after all changes are done
					removeShorts();
					log("All Shorts are removed, detected by video renderer.");
				}, 500);
				break;
			}
			if(mutation.target.parentElement ==null ){}else
			if (mutation.target.parentElement.id === "page-manager" || (mutation.target.parentElement.id=="primary")) {
				// console.log('Changes detected', mutationsList);
				//console.log('Changes detected in ytd-page-manager');
				// Your code to execute when ytd-page-manager changes goes here
				clearTimeout(timeoutId);
				timeoutId = setTimeout(() => {
					// Run your script here after all changes are done
					removeShorts();
					log("All Shorts are removed, detected by page-manager.");
				}, 500); // Adjust the timeout duration as needed
				//end loop if one change is a direct child of #page-manager
				break;
			}
		}
	}
	

	// Create a MutationObserver instance
	const observer = new MutationObserver(handleMutations);

	// Select the target node
	const targetNode = document.querySelector('#page-manager'); 
	// yt-page-navigation-progress#progress
	// Options for the observer (which mutations to observe)
	const observer_config = { childList: true, attributes: true, subtree: true,};
	// Start observing the target node for configured mutations
	observer.observe(targetNode, observer_config);
	
	removeShorts();
	if (config.c_removeSidebar){
		removeSidebarElement();
	}

	function createConfigMenu() {
		// Create menu container
		var menuContainer = document.createElement('div');
		menuContainer.id = 'yt-short-remover-config-menu';
		menuContainer.style.color = 'var(--yt-spec-text-secondary)';
		menuContainer.style.font_size = '1.4rem';
		menuContainer.style.lineHeight = '2rem';
		menuContainer.style.fontWeight = '400';
		menuContainer.style.padding = '10px';
		menuContainer.style.width = '50%';

		var hr = document.createElement('hr');
		var headline = document.createElement('h1');
		headline.style.lineHeight="6rem";
		headline.style.color="var(--yt-spec-text-primary)";
		headline.innerText="Shorts(Tampermonkey script)";
		menuContainer.appendChild(hr);
		menuContainer.appendChild(headline);

		// Create menu items
		var menuItems = [
			{ label: 'Remove Shorts from Start Page', key: 'c_removeFormStartPage', type:"checkbox"},
			{ label: 'Remove Shorts from Subscription Feed', key: 'c_removeFormSubscriptionFeed', type:"checkbox" },
			{ label: 'Remove Shorts from All Feeds (except Subscription Feed)', key: 'c_removeFormAllFeeds', type:"checkbox" },
			{ label: 'Remove Shorts from Follow Up Page', key: 'c_removeFormFollowUp', type:"checkbox" },
			{ label: 'Remove Shorts from Channel Page', key: 'c_removeFormChannel', type:"checkbox" },
			{ label: 'Remove Shorts from Search Page', key: 'c_removeFormSearch', type:"checkbox" },
			{ label: 'Remove Sidebar Shorts', key: 'c_removeSidebar', type:"checkbox" },
			{ label: 'Disable Short Page', key: 'c_disableShortPage', type:"checkbox" },
			{ label: 'Disable Short Page Scrolling', key: 'c_disableShortPageScrolling', type:"checkbox" },
			{ label: 'Console log Color:', key: 'c_consoleColor', type:"color", default:"#33bd52" }
		];

		// Add menu items to menu container
		menuItems.forEach(item => {
			var input = document.createElement('input');
			input.type = item.type
			if (input.type == "checkbox"){
				input.checked = GM_getValue(item.key, item.default ? item.default : true);
			}else{
				input.value = GM_getValue(item.key, item.default ? item.default : "undefine");
			}
			input.addEventListener('change', function() {
				if (this.type == "checkbox"){
					updateConfig(item.key, this.checked);
					log(item.key, "changed to", this.checked)
				}else{
					updateConfig(item.key, this.value);
					log(item.key, "changed to", this.value)
				}
			});

			var label = document.createElement('label');
			label.textContent = item.label;

			var menuItem = document.createElement('div');
			menuItem.style.display="flex";
			menuItem.style.justifyContent="space-between";
			menuItem.style.alignItems="center"

			menuItem.appendChild(label);
			menuItem.appendChild(input);
			menuContainer.appendChild(menuItem);
		});
		config.c_consoleColor
		// Append menu container to config element
		document.querySelector("div#contents.style-scope.ytd-section-list-renderer").appendChild(menuContainer);
	}

})();