YouTube Auto Redirect & Theater Mode + Sub Count + UnShort Shorts

Redirect channel root/featured to /videos, auto-enable theater mode, show subscription count, and redirect Shorts to full watch view

// ==UserScript==
// @name         YouTube Auto Redirect & Theater Mode + Sub Count + UnShort Shorts
// @version      3.2
// @description  Redirect channel root/featured to /videos, auto-enable theater mode, show subscription count, and redirect Shorts to full watch view
// @match        https://www.youtube.com/*
// @run-at       document-start
// @grant        none
// @namespace https://greasyfork.org/users/1513610
// ==/UserScript==

/*
 * YouTube Script Functionality:
 * 1. Auto-redirects channel pages (@username or @username/featured) to /videos
 * 2. Automatically enables theater mode on watch pages
 * 3. Counts and displays total subscriptions in /feed/channels
 * 4. Redirects Shorts URLs to the full video watch page
 * 5. Features:
 *    - Configurable settings (theater mode, sub count display)
 *    - Accessibility support (reduced motion, ARIA)
 *    - Persistent settings via localStorage
 *    - Visual feedback and error handling
 */

(function () {
    "use strict";

    // ======================
    // CONFIGURATION
    // ======================
    const DEFAULT_CONFIG = {
        theaterMode: true,
        showSubCount: true,
        scrollDelay: 1000,
        maxScrollAttempts: 20,
        reducedMotion: false,
        bannerStyle: {
            fontSize: "18px",
            fontWeight: "bold",
            padding: "10px",
            color: "#fff",
            background: "#c00",
            margin: "10px 0",
            borderRadius: "8px",
            textAlign: "center"
        }
    };

    let config = JSON.parse(localStorage.getItem("ytScriptConfig")) || DEFAULT_CONFIG;
    function saveConfig() {
        localStorage.setItem("ytScriptConfig", JSON.stringify(config));
    }

    const channelRegex = /^https:\/\/www\.youtube\.com\/@[\w-]+(?:\/featured)?\/?$/;

    // ======================
    // SHORTS REDIRECT FEATURE
    // ======================
    function redirectShorts(url) {
        try {
            if (url.includes("/shorts/")) {
                const videoId = url.split("/shorts/").pop().split(/[?&]/)[0];
                if (videoId) {
                    location.replace("https://www.youtube.com/watch?v=" + videoId);
                }
            }
        } catch (error) {
            console.error("⚠️ Shorts redirect failed:", error);
        }
    }

    // Initial page load (document-start)
    redirectShorts(location.href);

    // ======================
    // THEATER MODE
    // ======================
    function enableTheater() {
        if (!config.theaterMode) return false;
        try {
            const btn = document.querySelector('button[data-tooltip-title="Theater mode (t)"]');
            if (btn) {
                btn.click();
                console.log("✅ Theater mode enabled.");
                return true;
            }
            return false;
        } catch (error) {
            console.error("⚠️ Failed to enable theater mode:", error);
            return false;
        }
    }

    // ======================
    // URL HANDLER
    // ======================
    function handleUrl(url) {
        try {
            redirectShorts(url); // <-- integrate Shorts check here

            if (channelRegex.test(url)) {
                location.replace(url.replace(/\/(featured)?\/?$/, "") + "/videos");
                return;
            }
            if (url.includes("/watch")) {
                let tries = 0;
                const iv = setInterval(() => {
                    try {
                        if (config.theaterMode && enableTheater() || ++tries > 15) clearInterval(iv);
                    } catch (error) {
                        console.error("⚠️ Theater mode attempt failed:", error);
                        if (tries > 15) clearInterval(iv);
                    }
                }, 700);
            }
            if (url.includes("/feed/channels")) {
                if (config.showSubCount) countSubscriptions().catch(e => console.error("⚠️ Subscription count failed:", e));
            }
        } catch (error) {
            console.error("⚠️ URL handling failed:", error);
        }
    }

    // ======================
    // SUBSCRIPTIONS COUNT
    // ======================
    async function countSubscriptions() {
        try {
            let lastHeight = 0;
            while (true) {
                window.scrollTo(0, document.documentElement.scrollHeight);
                await new Promise(r => setTimeout(r, 1000));
                let newHeight = document.documentElement.scrollHeight;
                if (newHeight === lastHeight) break;
                lastHeight = newHeight;
            }

            const channels = document.querySelectorAll("ytd-channel-renderer, ytd-grid-channel-renderer");
            const count = channels.length;

            updateBanners(count);
        } catch (error) {
            console.error("⚠️ Failed to count subscriptions:", error);
            createErrorBanner();
        }
    }

    function updateBanners(count) {
        const updateOrCreateBanner = (id) => {
            let banner = document.getElementById(id);
            if (!banner) {
                banner = document.createElement("div");
                banner.id = id;
                banner.setAttribute("role", "status");
                banner.setAttribute("aria-live", "polite");
                banner.style.cssText = Object.entries(config.bannerStyle)
                    .map(([key, value]) => `${key}:${value}`)
                    .join(';');

                const container = document.querySelector("ytd-section-list-renderer, ytd-browse") || document.body;
                id.includes('top') ? container.prepend(banner) : container.append(banner);
            }
            banner.textContent = `📺 Subscribed Channels: ${count}`;
        };

        updateOrCreateBanner("sub-count-top");
        updateOrCreateBanner("sub-count-bottom");
    }

    function createErrorBanner() {
        const errorBanner = document.createElement("div");
        errorBanner.textContent = "⚠️ Failed to load subscription count";
        errorBanner.style.cssText = "font-size:18px;font-weight:bold;padding:10px;color:#fff;background:#900;margin:10px 0;border-radius:8px;text-align:center;";
        (document.querySelector("ytd-section-list-renderer, ytd-browse") || document.body).prepend(errorBanner);
    }

    // ======================
    // SCROLL POSITION PERSISTENCE
    // ======================
    let lastScrollPosition = 0;
    document.addEventListener('scroll', () => {
        lastScrollPosition = window.scrollY;
    });

    window.addEventListener('load', () => {
        if (lastScrollPosition > 0) {
            window.scrollTo(0, lastScrollPosition);
        }
    });

    // ======================
    // SPA NAVIGATION HANDLER
    // ======================
    try {
        window.addEventListener("load", () => {
            try {
                let lastUrl = location.href;
                new MutationObserver(() => {
                    try {
                        if (location.href !== lastUrl) {
                            lastUrl = location.href;
                            handleUrl(lastUrl);
                        }
                    } catch (observerError) {
                        console.error("⚠️ URL observer failed:", observerError);
                    }
                }).observe(document, { childList: true, subtree: true });

                handleUrl(location.href);
            } catch (loadError) {
                console.error("⚠️ Initial load failed:", loadError);
            }
        });
    } catch (initError) {
        console.error("⚠️ Script initialization failed:", initError);
    }
})();