Shorts remover

Removes Shorts button and tabs from youtube.com

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Shorts remover
// @namespace    http://tampermonkey.net/
// @version      1.2.4
// @description  Removes Shorts button and tabs from youtube.com
// @author       Filodelphia
// @match        https://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    /*************************************************
     *  CSS: hide any loader we mark as "stuck"
     *************************************************/
    GM_addStyle(`
        ytd-continuation-item-renderer.yt-hidden-loader {
            display: none !important;
            visibility: hidden !important;
            height: 0 !important;
            margin: 0 !important;
            padding: 0 !important;
        }
    `);

    // Your code here...
    function autoRemove(selector) {
        console.log('Auto-removal enabled for selector:', selector);

        function removeMatchesInRoot(root) {
            if (!root || !(root instanceof Element || root instanceof Document || root instanceof DocumentFragment)) {
                return;
            }

            const elements = root.querySelectorAll(selector);
            if (elements.length > 0) {
                elements.forEach(el => el.remove());
                console.log(`Removed ${elements.length} element(s) matching: ${selector}`);
            }
        }

        // 1. Initial cleanup on the whole document
        removeMatchesInRoot(document);

        // 2. Observe DOM changes and remove matches as they appear
        const observer = new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                // Check newly added nodes
                mutation.addedNodes.forEach(node => {
                    if (!(node instanceof Element)) return;

                    // Case 1: the added node itself matches the selector
                    if (node.matches && node.matches(selector)) {
                        console.log('Removing newly added node matching:', selector);
                        node.remove();
                        return;
                    }

                    // Case 2: the added node contains matching elements
                    removeMatchesInRoot(node);
                });
            });
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
        });

        // Optional: return observer in case you ever want to stop it
        return observer;
    }


    /*************************************************
     * Smart loader hider:
     * If a continuation loader has a NORMAL video/video-grid item after it,
     * the loader is no longer needed → hide it.
     *************************************************/
    function setupLoaderHider() {
        const CONTENT_SELECTORS = [
            'ytd-rich-item-renderer',
            'ytd-video-renderer',
            'ytd-grid-video-renderer',
            'ytd-compact-video-renderer'
        ].join(', ');

        const isVideoItem = el => el && el.matches && el.matches(CONTENT_SELECTORS);

        const observer = new MutationObserver(() => {
            const loaders = document.querySelectorAll('ytd-continuation-item-renderer');

            loaders.forEach(loader => {
                let next = loader.nextElementSibling;
                if(next == null) exit;

                loader.classList.add('yt-hidden-loader');
            });
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }


    autoRemove('[title="Shorts"]');
    autoRemove('ytd-rich-section-renderer');
    autoRemove('ytd-reel-shelf-renderer');
    // Special handling for continuation loader
    setupLoaderHider();
})();