Youtube short remover

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

目前為 2025-08-05 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Youtube short remover
// @namespace    http://tampermonkey.net/
// @version      full.1.2
// @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 channel short pages
    //https://www.youtube.com/LinusTechTips/shorts or https://www.youtube.com/@LinusTechTips/shorts ...
    var youtubeChannelShortsPagePattern = /^(https?:\/\/(?:www\.)?youtube\.com\/(?!feed.*)(?!watch.*)(?!short.*)(?!playlist.*)(?!podcasts.*)(?!gaming.*)(?!results.*).+)\/shorts\/?$/;
    // 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) {
            const match = youtubeChannelShortsPagePattern.exec(currentURL);
            if (match) {
                window.location.href = match[1];
            }
            // 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.style.display = "none";
            });

            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,grid-shelf-view-model');

		// 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);
	}

})();