Greasy Fork is available in English.

YouTube Video Age and Category Filter

Filters old YouTube videos and hides videos in certain categories.

安裝腳本?
作者推薦腳本

您可能也會喜歡 YouTube Volume and Time Mouse Controlled

安裝腳本
// ==UserScript==
// @name         YouTube Video Age and Category Filter
// @namespace    PoKeRGT
// @version      1.03
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @description  Filters old YouTube videos and hides videos in certain categories.
// @author       PoKeRGT
// @match        https://www.youtube.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @connect      www.youtube.com
// @run-at       document-start
// @homepageURL  https://github.com/PoKeRGT/userscripts
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const MAX_VIDEO_AGE = GM_getValue('maxVideoAge', 15);
  const OPACITY = GM_getValue('opacity', 0.25);
  const CATEGORIES_TO_HIDE = GM_getValue('categoriesToHide', ['Music', 'Sports'])
  const NOT_SEEN_BORDER_COLOR = GM_getValue('notSeenBorderColor', '#00FF00');
  const SEEN_BORDER_COLOR = GM_getValue('seenBorderColor', '#FF0000');

  const observer = new MutationObserver(function (mutations) {
    mutations.forEach(function (mutation) {
      mutation.addedNodes.forEach(function (node) {
        if (node.nodeName === 'YTD-RICH-ITEM-RENDERER') {
          const videoElement = node.querySelector('#video-title');
          if (videoElement) {
            const progressBar = node.querySelector('#progress')
            if (progressBar) {
              changeElement(progressBar, '#thumbnail', "seen")
            } else {
              const videoUrl = getVideoUrlFromElement(videoElement);
              const videoTitle = videoElement.textContent.trim();
              fetchVideoDetails(videoUrl, videoTitle, node);
            }
          }
        }
      });
    });
  });

  observer.observe(document, { childList: true, subtree: true });

  function getVideoUrlFromElement(videoElement) {
    const anchorElement = videoElement.closest('a');
    if (anchorElement && anchorElement.href) {
      const baseUrl = 'https://www.youtube.com';
      const url = new URL(anchorElement.href, baseUrl);
      return url.href;
    }
    return null;
  }

  function changeElement(element, closest, prop) {
    const parentElement = element.closest(`div${closest}`);
    if (parentElement) {
      switch (prop) {
        case 'opacity':
          parentElement.style.opacity = OPACITY;
          break;
        case 'not_seen':
          parentElement.style.border = `5px solid ${NOT_SEEN_BORDER_COLOR}`;
          parentElement.style.borderRadius = '20px';
          break;
        case 'seen':
          parentElement.style.border = `5px solid ${SEEN_BORDER_COLOR}`;
          parentElement.style.borderRadius = '20px';
          break;
        default:
          break;
      }
    }
  }

  function fetchVideoDetails(videoUrl, videoTitle) {
    if (videoUrl) {
      GM_xmlhttpRequest({
        method: 'GET',
        url: videoUrl,
        fetch: true,
        onload: function (response) {
          let hidden = false;
          const metaTags = response.responseText.match(/<meta [^>]*>/g);
          const dateElement = findDateMetaTag(metaTags);
          const categoryElement = findCategoryMetaTag(metaTags);
          const videoPath = new URL(videoUrl).pathname + new URL(videoUrl).search
          if (categoryElement && categoryElement.content && CATEGORIES_TO_HIDE.includes(categoryElement.content)) {
            hidden = true;
            const videoElement = document.querySelector(`a[href="${videoPath}"]`);
            if (videoElement) {
              changeElement(videoElement, '#content', "opacity")
            }
            console.log(`Video "${videoTitle}" is in the category "${categoryElement.content}"`);
          }
          if (dateElement && dateElement.content && !hidden) {
            const uploadDate = new Date(dateElement.content);
            const today = new Date();
            const diffInDays = Math.ceil((today - uploadDate) / (1000 * 60 * 60 * 24));
            if (diffInDays > MAX_VIDEO_AGE) {
              const videoElement = document.querySelector(`a[href="${videoPath}"]`);
              if (videoElement) {
                changeElement(videoElement, '#content', "opacity")
              }
              console.log(`Video "${videoTitle}" is older than ${MAX_VIDEO_AGE} days (${diffInDays} days).`);
            } else {
              const videoElement = document.querySelector(`a[href="${videoPath}"]`);
              if (videoElement) {
                changeElement(videoElement, '#thumbnail', "not_seen")
              }
              console.log(`Video "${videoTitle}" is ${diffInDays} days old.`);
            }
          } else {
            console.log('No video upload date found.');
          }
        },
        onerror: function (error) {
          console.error('Error fetching video details:', error);
        }
      });
    }
  }

  function findDateMetaTag(metaTags) {
    const dateTag = metaTags.find(tag => {
      return tag.includes('itemprop="datePublished"') || tag.includes('itemprop="dateCreated"') || tag.includes('itemprop="uploadDate"');
    });
    if (dateTag) {
      const contentMatch = dateTag.match(/content="([^"]+)"/);
      if (contentMatch && contentMatch[1]) {
        return { content: contentMatch[1] };
      }
    }
    return null;
  }

  function findCategoryMetaTag(metaTags) {
    const categoryTag = metaTags.find(tag => {
      return tag.includes('itemprop="genre"') || tag.includes('itemprop="category"'); // Adjust this based on the actual category meta tag
    });
    if (categoryTag) {
      const contentMatch = categoryTag.match(/content="([^"]+)"/);
      if (contentMatch && contentMatch[1]) {
        return { content: contentMatch[1] };
      }
    }
    return null;
  }
})();