// ==UserScript== // @name Remove YouTube Shorts // @name:zh-CN 移除 YouTube Shorts // @name:zh-TW 移除 YouTube Shorts // @name:ja YouTube の Shorts を削除 // @name:ko YouTube Shorts 제거 // @name:es Eliminar YouTube Shorts // @name:pt-BR Remover YouTube Shorts // @name:ru Удалить YouTube Shorts // @name:id Hapus YouTube Shorts // @name:hi YouTube Shorts हटाएँ // @namespace https://github.com/strangeZombies // @version 2026.7.2.0 // @description Comprehensive removal of YouTube Shorts: home, search, watch, channel, playlist, history, sidebar, mobile, plus redirect + disguised-shorts detection. // @description:zh-CN 全面移除 YouTube 上的 Shorts:首页 / 搜索 / 观看页 / 频道 / 播放列表 / 历史记录 / 侧边栏 / 移动端,并支持自动重定向与"伪装 Shorts"检测 // @description:zh-TW 移除 YouTube 上的 Shorts 标签、Dismissible 元素、Shorts 链接和 Reel Shelf // @description:ja YouTube 上の Shorts タグ、ディスミッシブル要素、Shorts リンク、および Reel Shelf を削除 // @description:ko YouTube의 Shorts 태그, 해제 가능한 요소, Shorts 링크 및 Reel 선반 제거 // @description:es Eliminar etiquetas de Shorts de YouTube, elementos desechables, enlaces de Shorts y estante de carretes // @description:pt-BR Remover tags de Shorts do YouTube, elementos descartáveis, links de Shorts e prateleira de rolos // @description:ru Удалите теги YouTube Shorts, элементы, которые можно отклонить, ссылки на Shorts и полку с катушками // @description:id Hapus tag Shorts YouTube, elemen yang dapat dihapus, tautan Shorts, dan Rak Reel // @description:hi YouTube Shorts टैग, खारिज करने योग्य तत्व, Shorts लिंक और Reel Shelf निकालें // @author StrangeZombies // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @match https://*.youtube.com/* // @match https://m.youtube.com/* // @grant none // @run-at document-start // ==/UserScript== (function () { 'use strict'; /* ========================================================================================= * [MODULE] config.js —— 全局配置 * ========================================================================================= */ const CONFIG = { // 页面区域开关 hideHome: true, hideWatch: true, hideSearch: true, hideChannel: true, hidePlaylist: true, hideHistory: false, // 历史记录默认保留,改成 true 则也会清理 // 导航区域开关 hideSidebar: true, hideMiniGuide: true, hideBottomNav: true, hideExplore: true, // 增强功能 redirectShorts: true, // /shorts/xxx -> /watch?v=xxx detectDisguisedShorts: true,// 检测"点进去才发现是 Shorts 播放器"的情况 // 高风险功能,默认关闭:拦截部分 Shorts 相关的内部 API 请求 // 开启后可能导致某些区域的续加载 (continuation) 请求被误伤,请自行测试 blockApi: false, debug: false, }; /* ========================================================================================= * [MODULE] utils.js —— 通用工具函数 * ========================================================================================= */ const log = (...args) => { if (CONFIG.debug) console.log('[YT-Shorts-Pro]', ...args); }; function debounce(fn, delay) { let timer = null; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn.apply(null, args), delay); }; } // 找到"最近的可识别卡片容器",找不到就返回自身 const CONTAINER_SELECTOR = [ 'ytd-video-renderer', 'ytd-grid-video-renderer', 'ytd-compact-video-renderer', 'ytd-rich-item-renderer', 'ytd-rich-shelf-renderer', 'ytd-reel-shelf-renderer', 'ytd-reel-item-renderer', 'ytd-playlist-video-renderer', 'ytd-playlist-panel-video-renderer', 'ytm-video-with-context-renderer', 'ytm-shorts-lockup-view-model', 'ytm-reel-item-renderer', ].join(', '); function closestContainer(el) { if (!el || !el.closest) return el; return el.closest(CONTAINER_SELECTOR) || el; } function safeRemove(el, category) { if (!el || !el.isConnected) return; const target = closestContainer(el); if (target.dataset && target.dataset.ytShortsRemoved) return; if (target.dataset) target.dataset.ytShortsRemoved = 'true'; target.remove(); STATS.report(category); log(`removed [${category}]`, target); } /* ========================================================================================= * [MODULE] stats.js —— 统计与调试输出 * ========================================================================================= */ const STATS = { counts: {}, report(category) { this.counts[category] = (this.counts[category] || 0) + 1; }, print() { if (!CONFIG.debug) return; const rows = Object.entries(this.counts); if (!rows.length) return; console.groupCollapsed('%cRemove YouTube Shorts Pro — Removed', 'color:#e33;font-weight:bold'); rows.forEach(([k, v]) => console.log(`${k.padEnd(10, ' ')} ${v}`)); console.groupEnd(); }, }; /* ========================================================================================= * [MODULE] css.js —— document-start 阶段的即时隐藏样式(防闪烁) * ========================================================================================= */ function injectStyle() { if (document.getElementById('yt-shorts-pro-style')) return; const css = ` /* 首页 / 推荐 / 订阅 中的 Shorts 货架与卡片 */ ytd-reel-shelf-renderer, ytd-rich-shelf-renderer[is-shorts-shelf], ytd-reel-item-renderer, ytm-reel-shelf-renderer, ytm-shorts-lockup-view-model, [is-shorts-shelf] { display: none !important; } /* 侧边栏 / Mini Guide / 底部导航 中的 Shorts 入口 */ ytd-mini-guide-entry-renderer[aria-label="Shorts"], ytd-guide-entry-renderer:has(a[title="Shorts"]), .pivot-shorts { display: none !important; } /* 频道页 Shorts 标签(先隐藏,后续 JS 精确匹配文本再彻底移除) */ tp-yt-paper-tab:has(> .tab-content[title="Shorts"]) { display: none !important; } `; const style = document.createElement('style'); style.id = 'yt-shorts-pro-style'; style.textContent = css; (document.head || document.documentElement).appendChild(style); } /* ========================================================================================= * [MODULE] detectors.js —— 统一的"是不是 Shorts"判定 * ========================================================================================= */ function isShortsLink(el) { if (!el) return false; if (el.tagName === 'A' && el.getAttribute('href') && el.getAttribute('href').startsWith('/shorts')) return true; if (el.querySelector && el.querySelector('a[href^="/shorts"]')) return true; return false; } function isShortsMarked(el) { if (!el) return false; if (el.hasAttribute && (el.hasAttribute('is-shorts') || el.hasAttribute('is-shorts-shelf'))) return true; if (el.querySelector && el.querySelector('[overlay-style="SHORTS"], [is-shorts], [is-shorts-shelf], [data-style="SHORTS"]')) return true; return false; } const SHORTS_TAGS = new Set([ 'YTD-REEL-SHELF-RENDERER', 'YTD-REEL-ITEM-RENDERER', 'YTM-REEL-SHELF-RENDERER', 'YTM-REEL-ITEM-RENDERER', 'YTM-SHORTS-LOCKUP-VIEW-MODEL', ]); function isShortsTag(el) { return !!el && !!el.tagName && SHORTS_TAGS.has(el.tagName); } // 统一判定入口:以后 YouTube 改版,只需要扩充这个函数内部的规则 function isShorts(el) { if (!el || el.nodeType !== 1) return false; return isShortsTag(el) || isShortsMarked(el) || isShortsLink(el); } /* ========================================================================================= * [MODULE] redirect.js —— /shorts/xxx 自动重定向到 /watch?v=xxx * ========================================================================================= */ function extractVideoId(url) { const m = url.match(/\/shorts\/([a-zA-Z0-9_-]{6,})/); return m ? m[1] : null; } function redirectIfShorts() { if (!CONFIG.redirectShorts) return; const id = extractVideoId(location.pathname); if (!id) return; const target = `https://www.youtube.com/watch?v=${id}`; STATS.report('Redirect'); log('redirect', location.href, '->', target); location.replace(target); } // 拦截 SPA 内部跳转(点击 Shorts 卡片时 YouTube 常用 pushState/replaceState 切换,而不整页刷新) function hookHistoryForRedirect() { if (!CONFIG.redirectShorts) return; const wrap = (type) => { const orig = history[type]; history[type] = function (...args) { const ret = orig.apply(this, args); queueMicrotask(redirectIfShorts); return ret; }; }; wrap('pushState'); wrap('replaceState'); window.addEventListener('popstate', redirectIfShorts); } /* ========================================================================================= * [MODULE] api.js —— (可选,默认关闭)拦截明确的 Shorts 专属接口请求 * ========================================================================================= */ function hookApiBlocking() { if (!CONFIG.blockApi) return; // 只拦截路径中带有明确 "reel" 标识的 innertube 请求,避免误伤首页/搜索的通用 continuation const REEL_API_PATTERN = /\/youtubei\/v1\/reel\//; const origFetch = window.fetch; window.fetch = function (input, init) { const url = typeof input === 'string' ? input : (input && input.url) || ''; if (REEL_API_PATTERN.test(url)) { log('blocked fetch:', url); STATS.report('API'); return Promise.resolve(new Response('{}', { status: 200 })); } return origFetch.apply(this, arguments); }; const origOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url, ...rest) { this.__ytShortsBlocked = REEL_API_PATTERN.test(url); return origOpen.call(this, method, url, ...rest); }; const origSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.send = function (...args) { if (this.__ytShortsBlocked) { log('blocked xhr'); STATS.report('API'); // 直接不发送,交给页面自身的空结果兜底逻辑处理 return; } return origSend.apply(this, args); }; } /* ========================================================================================= * [MODULE] filters.js —— 各页面区域的清理规则 * ========================================================================================= */ // ---- 首页 / 推荐 / 探索 ---- function filterHome(root) { if (!CONFIG.hideHome) return; root.querySelectorAll('ytd-rich-shelf-renderer[is-shorts-shelf]').forEach((el) => safeRemove(el, 'Home')); root.querySelectorAll('ytd-reel-shelf-renderer').forEach((el) => safeRemove(el, 'Home')); root.querySelectorAll('ytd-rich-item-renderer a[href^="/shorts"]').forEach((el) => safeRemove(el, 'Home')); root.querySelectorAll('ytd-thumbnail-overlay-time-status-renderer[overlay-style="SHORTS"]').forEach((el) => safeRemove(el, 'Home')); } function filterExplore(root) { if (!CONFIG.hideExplore) return; if (!location.pathname.includes('/feed/explore')) return; root.querySelectorAll('a[href^="/shorts"], [is-shorts]').forEach((el) => safeRemove(el, 'Explore')); } // ---- 搜索 ---- function filterSearch(root) { if (!CONFIG.hideSearch) return; if (!location.pathname.includes('/results')) return; // 搜索结果里的 Shorts 卡片 root.querySelectorAll('ytd-video-renderer [overlay-style="SHORTS"]').forEach((el) => safeRemove(el, 'Search')); root.querySelectorAll('ytd-reel-shelf-renderer').forEach((el) => safeRemove(el, 'Search')); // 搜索结果页顶部的 "Shorts" 分类卡片组 root.querySelectorAll('ytd-shelf-renderer').forEach((shelf) => { const title = shelf.querySelector('#title, yt-formatted-string#title'); if (title && /shorts/i.test(title.textContent || '')) safeRemove(shelf, 'Search'); }); // 搜索筛选器(Filter)里的 "Shorts" 选项 root.querySelectorAll('yt-chip-cloud-chip-renderer').forEach((chip) => { const text = chip.textContent || ''; if (/^\s*Shorts\s*$/i.test(text)) safeRemove(chip, 'Search'); }); } // ---- 观看页 ---- function filterWatch(root) { if (!CONFIG.hideWatch) return; if (!location.pathname.startsWith('/watch')) return; // 右侧推荐栏里的 Shorts root.querySelectorAll('#related ytd-compact-video-renderer [overlay-style="SHORTS"]') .forEach((el) => safeRemove(el, 'Watch')); root.querySelectorAll('#related ytd-reel-shelf-renderer').forEach((el) => safeRemove(el, 'Watch')); // 播放结束后的 "接下来播放 Shorts" 推荐面板 root.querySelectorAll('.ytp-endscreen-content a[href^="/shorts"]').forEach((el) => { const card = el.closest('.ytp-videowall-still, .ytp-suggestion-set'); safeRemove(card || el, 'Watch'); }); } // ---- 频道 ---- function filterChannel(root) { if (!CONFIG.hideChannel) return; if (!(location.pathname.includes('/channel') || location.pathname.includes('/@') || location.pathname.includes('/c/'))) return; // Shorts 标签本身(用文本内容判断,而不是无效的 :has-text() 伪类) root.querySelectorAll('yt-tab-shape').forEach((tab) => { const title = (tab.getAttribute('tab-title') || tab.textContent || '').trim(); if (title === 'Shorts' && !tab.dataset.ytShortsRemoved) { tab.dataset.ytShortsRemoved = 'true'; tab.style.display = 'none'; STATS.report('Tabs'); } }); // 如果当前就停留在频道的 Shorts 子页面 (.../shorts),直接跳回主页 Videos 标签 if (/\/(shorts)(\/)?$/.test(location.pathname)) { const base = location.pathname.replace(/\/shorts\/?$/, ''); log('leaving channel shorts tab ->', base); location.replace(`https://www.youtube.com${base}${location.search}`); } // 频道首页里混入的 Shorts 货架 root.querySelectorAll('ytd-reel-shelf-renderer').forEach((el) => safeRemove(el, 'Channel')); } // ---- 播放列表 ---- function filterPlaylist(root) { if (!CONFIG.hidePlaylist) return; if (!(location.pathname.includes('/playlist') || location.search.includes('list='))) return; root.querySelectorAll('ytd-playlist-video-renderer a[href^="/shorts"]').forEach((el) => safeRemove(el, 'Playlist')); root.querySelectorAll('ytd-playlist-panel-video-renderer a[href^="/shorts"]').forEach((el) => safeRemove(el, 'Playlist')); } // ---- 订阅 (Grid / List / Rich Grid) ---- function filterSubscriptions(root) { if (!location.pathname.includes('/feed/subscriptions')) return; root.querySelectorAll('ytd-grid-video-renderer [overlay-style="SHORTS"]').forEach((el) => safeRemove(el, 'Home')); root.querySelectorAll('ytd-video-renderer [overlay-style="SHORTS"]').forEach((el) => safeRemove(el, 'Home')); root.querySelectorAll('ytd-rich-item-renderer [overlay-style="SHORTS"]').forEach((el) => safeRemove(el, 'Home')); } // ---- 历史记录(默认关闭) ---- function filterHistory(root) { if (!CONFIG.hideHistory) return; if (!location.pathname.includes('/feed/history')) return; root.querySelectorAll('ytd-reel-shelf-renderer').forEach((el) => safeRemove(el, 'History')); root.querySelectorAll('[overlay-style="SHORTS"]').forEach((el) => safeRemove(el, 'History')); } // ---- PC 侧边栏 / Mini Guide ---- function filterSidebar(root) { if (CONFIG.hideSidebar) { root.querySelectorAll('#guide a[href^="/shorts"], #guide [title="Shorts"]').forEach((el) => safeRemove(el, 'Sidebar')); root.querySelectorAll('ytd-guide-entry-renderer').forEach((entry) => { const title = entry.querySelector('yt-formatted-string, #endpoint'); const text = (entry.textContent || '').trim(); if (/^Shorts$/i.test(text.split('\n')[0] || '')) safeRemove(entry, 'Sidebar'); void title; }); } if (CONFIG.hideMiniGuide) { root.querySelectorAll('.ytd-mini-guide-entry-renderer[title="Shorts"], .ytd-mini-guide-entry-renderer[aria-label="Shorts"]') .forEach((el) => safeRemove(el, 'MiniGuide')); } } // ---- 移动端 ---- function filterMobile(root) { if (!location.hostname.includes('m.youtube.com')) return; if (CONFIG.hideBottomNav) { root.querySelectorAll('.pivot-shorts').forEach((el) => safeRemove(el, 'BottomNav')); } root.querySelectorAll('ytm-reel-shelf-renderer').forEach((el) => safeRemove(el, 'Shelf')); root.querySelectorAll('ytm-shorts-lockup-view-model').forEach((el) => safeRemove(el, 'Shelf')); if (CONFIG.hideSearch) { root.querySelectorAll('ytm-search ytm-video-with-context-renderer [data-style="SHORTS"]') .forEach((el) => safeRemove(el, 'Search')); } } // 汇总所有 filter,供 observer / 初次扫描调用 function runAllFilters(root) { filterHome(root); filterExplore(root); filterSearch(root); filterWatch(root); filterChannel(root); filterPlaylist(root); filterSubscriptions(root); filterHistory(root); filterSidebar(root); filterMobile(root); } /* ========================================================================================= * [MODULE] disguised.js —— 检测"点进普通链接却是 Shorts 播放器"的情况 * ========================================================================================= */ function checkDisguisedShorts() { if (!CONFIG.detectDisguisedShorts) return; if (!location.pathname.startsWith('/watch')) return; // 场景一:URL 仍是 /watch,但页面已经渲染出 Shorts 专属播放器结构 const shortsPlayer = document.querySelector('ytd-shorts, #shorts-container, ytd-reel-video-renderer'); if (shortsPlayer) { STATS.report('Disguised'); log('disguised shorts detected on watch page, player element:', shortsPlayer); // 尝试从当前 URL 里取出真实 videoId,跳回标准观看页;若拿不到就不强行处理,避免误伤正常内容 const params = new URLSearchParams(location.search); const v = params.get('v'); if (v) { location.replace(`https://www.youtube.com/watch?v=${v}`); } } } /* ========================================================================================= * [MODULE] observer.js —— 增量扫描:只处理新增节点,而不是每次全量 querySelectorAll(document) * ========================================================================================= */ function scanNode(node) { if (!node || node.nodeType !== 1) return; if (isShorts(node)) { safeRemove(node, 'Shelf'); return; // 已经把自己删了,没必要再往下扫子节点 } // 用当前区域的全部 filter 规则再扫一遍这个新增子树(子树通常很小,成本可忽略) runAllFilters(node); } function startObserver() { const debouncedFullScan = debounce(() => runAllFilters(document), 150); const observer = new MutationObserver((mutations) => { let sawAdded = false; for (const mutation of mutations) { mutation.addedNodes.forEach((node) => { sawAdded = true; scanNode(node); }); } // 增量扫描之外,隔一段时间兜底做一次全量扫描,覆盖属性变化(而非新增节点)触发的情况 if (sawAdded) debouncedFullScan(); checkDisguisedShorts(); }); observer.observe(document.documentElement, { childList: true, subtree: true }); return observer; } /* ========================================================================================= * [MODULE] navigation.js —— YouTube 是 SPA,需要在每次路由切换时重新跑一遍规则 * ========================================================================================= */ function onNavigate() { log('navigate ->', location.href); redirectIfShorts(); runAllFilters(document); checkDisguisedShorts(); STATS.print(); } function hookNavigation() { window.addEventListener('popstate', onNavigate); document.addEventListener('yt-navigate-finish', onNavigate); } /* ========================================================================================= * [MODULE] main.js —— 入口 * ========================================================================================= */ function init() { log('Remove YouTube Shorts Pro v2026 initialized', CONFIG); hookHistoryForRedirect(); hookApiBlocking(); redirectIfShorts(); runAllFilters(document); checkDisguisedShorts(); hookNavigation(); startObserver(); } // CSS 需要尽早注入,document-start 时 head 可能还不存在 if (document.head) { injectStyle(); } else { const headObserver = new MutationObserver(() => { if (document.head) { injectStyle(); headObserver.disconnect(); } }); headObserver.observe(document.documentElement, { childList: true }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();