YouTube Keyword Blocker (Whole‑Word Mode)

Hide YouTube homepage videos by blacklisted words in title or channel name

目前為 2025-05-08 提交的版本,檢視 最新版本

// ==UserScript==
// @name         YouTube Keyword Blocker (Whole‑Word Mode)
// @description  Hide YouTube homepage videos by blacklisted words in title or channel name
// @match        https://www.youtube.com/*
// @run-at       document-end
// @version 0.0.1.20250508195725
// @namespace https://greasyfork.org/users/1435046
// ==/UserScript==

(function () {
  'use strict';

    // skip “results” and “@channel” pages
    if (location.pathname.startsWith('/results') || location.pathname.startsWith('/@')) return;

  // 1) List your raw terms here
  const terms = [
    'business insider', ... 'ray dalio'
  ];

  // 2) Convert to RegExp objects with \b boundaries, case‑insensitive
  const blacklist = terms.map(term => {
    const esc = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    return new RegExp(`\\b${esc}\\b`, 'i');
  });

  const ITEM_SELECTOR = 'ytd-rich-grid-media, ytd-video-renderer';
  const TITLE_SELECTOR = '#video-title';
  const CHANNEL_SELECTOR = 'ytd-channel-name a';

  function filterItem(item) {

    const linkEl = item.querySelector('a#video-title-link');
    const title = linkEl
      ? linkEl.getAttribute('aria-label').trim()
      : '‹no title›';
    console.log('› filterItem saw:', item.tagName, title);

    try {
      const channelEl = item.querySelector(CHANNEL_SELECTOR);
      const channel = channelEl ? channelEl.textContent.trim() : '';

      for (const re of blacklist) {
        if (re.test(title) || re.test(channel)) {
          console.log(
            `Blocking video:\n` +
            `  title:   "${title}"\n` +
            `  channel: "${channel}"\n` +
            `  matched: ${re}`
          );
          item.style.display = 'none';
          return;
        }
      }
    } catch (e) {
      console.error('Keyword Blocker error', e);
    }
}

  function scanPage() {
    document.querySelectorAll(ITEM_SELECTOR).forEach(filterItem);
  }

  new MutationObserver(mutations => {
    for (const { addedNodes } of mutations) {
      for (const node of addedNodes) {
        if (!(node instanceof HTMLElement)) continue;
        if (node.matches(ITEM_SELECTOR)) filterItem(node);
        else node.querySelectorAll && node.querySelectorAll(ITEM_SELECTOR).forEach(filterItem);
      }
    }
  }).observe(document.body, { childList: true, subtree: true });

  scanPage();

  window.addEventListener('yt-navigate-finish', scanPage);
})();