您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This is for people who hate these stupid trends
// ==UserScript== // @name DeTrend – Hide "CHECK THE SOUND" bait in YT Shorts // @namespace https://winverse.detrend // @version 1.0.0 // @description This is for people who hate these stupid trends // @author Winverse // @match *://*.youtube.com/shorts/* // @run-at document-idle // @icon https://i.ibb.co/VYG22nmP/Projekt-bez-nazwy.png // @grant none // ==/UserScript== (function() { 'use strict'; // --- Config --- const TARGET_CLASSES = [ 'yt-core-attributed-string', 'yt-core-attributed-string--white-space-pre-wrap', 'yt-core-attributed-string--link-inherit-color' ]; // If you prefer to hide instead of remove, set to true. const HIDE_INSTEAD_OF_REMOVE = true; // --- Fuzzy matcher for "CHECK THE SOUND" with optional "DON'T"/"DO NOT" and symbol junk --- // Allows symbols/spaces between letters to catch bypasses like: D[o}! N{{O}}T C#H(E)C[K] T--H[E] S*O^U!N?D const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const fuzzyWord = (w) => w.split('').map(ch => esc(ch) + '[\\W_]*').join(''); const OPT_DONT = `(?:${fuzzyWord("don't")}|${fuzzyWord("dont")}|${fuzzyWord("do not")})[\\W_]*`; const CHECK = fuzzyWord('check') + '[\\W_]*'; const THE = fuzzyWord('the') + '[\\W_]*'; const SOUND = fuzzyWord('sound'); // Build a regex that matches with or without the "DON'T/DO NOT" prefix, anywhere in the text. const baitRegex = new RegExp( `(?:${OPT_DONT})?${CHECK}${THE}${SOUND}`, 'i' ); // Old quick filter // const cheapContains = (t) => /check[\W_]*the[\W_]*sound/i.test(t); // New quick filter — allows any characters (including emoji) in between const cheapContains = (t) => /check[\s\S]*the[\s\S]*sound/i.test(t); function looksLikeShortsPage() { // Only act on Shorts contexts (URL contains /shorts/) or when Shorts are embedded in feeds // We still scan anywhere on YT because Shorts cards can appear on home/search. return location.pathname.includes('/shorts/') || true; } function isTargetTitleElement(el) { if (!(el instanceof HTMLElement)) return false; // Require at least the base class, others are commonly present on Shorts titles if (!el.classList.contains('yt-core-attributed-string')) return false; // If all three classes exist, even more certain const hasAll = TARGET_CLASSES.every(c => el.classList.contains(c)); return hasAll || el.closest('ytd-reel-video-renderer,ytd-reel-item-renderer,ytm-reel-video-renderer,ytm-reel-item-renderer,ytm-shorts-lockup-view-model') !== null; } function nuke(el) { if (!el || !el.parentElement) return; if (HIDE_INSTEAD_OF_REMOVE) { el.style.display = 'none'; el.style.visibility = 'hidden'; el.setAttribute('data-detrend-hidden', '1'); } else { el.remove(); } } let removedCount = 0; function scan(root = document) { if (!looksLikeShortsPage()) return; // Candidate text containers const nodes = root.querySelectorAll('.yt-core-attributed-string, yt-formatted-string, span, h1, h2, h3'); for (const el of nodes) { if (!isTargetTitleElement(el)) continue; const t = (el.textContent || '').trim(); if (!t) continue; if (!cheapContains(t)) continue; if (baitRegex.test(t)) { nuke(el); removedCount++; } } } // Debounced scanner for mutations let pending = false; const scheduleScan = () => { if (pending) return; pending = true; requestAnimationFrame(() => { try { scan(); } finally { pending = false; } }); }; // Initial scan scan(); // MutationObserver to catch dynamically loaded Shorts/cards const mo = new MutationObserver((mutations) => { for (const m of mutations) { if (m.addedNodes) { for (const n of m.addedNodes) { if (n.nodeType === 1) { // ELEMENT_NODE scheduleScan(); } } } if (m.type === 'characterData') scheduleScan(); } }); mo.observe(document.documentElement || document.body, { childList: true, subtree: true, characterData: true }); // Also rescan on key YT navigation events (SPA) window.addEventListener('yt-navigate-finish', scheduleScan, true); window.addEventListener('yt-page-data-updated', scheduleScan, true); window.addEventListener('yt-action', scheduleScan, true); // A tiny console status so you can verify it works const logOnce = () => { console.debug('[DeTrend] active. Removed so far:', removedCount); }; setTimeout(logOnce, 1500); setInterval(() => { console.debug('[DeTrend] removed:', removedCount, ' current URL:', location.href); }, 15000); })();