您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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); } })();