Focused YouTube

Remove ads, shorts, and algorithmic suggestions on YouTube (EN/NL/DE/FR)

目前為 2025-07-04 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Focused YouTube
// @version      30
// @author       Richard B
// @namespace    https://www.365devnet.eu/focusedyoutube
// @description  Remove ads, shorts, and algorithmic suggestions on YouTube (EN/NL/DE/FR)
// @match        *://*.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @run-at       document-start
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

// Credits:
// Originally based on Focused YouTube by Kervyn
// https://github.com/KervynH/Focused-YouTube
//
// Additional credits: https://github.com/lawrencehook/remove-youtube-suggestions

/*
MIT License

Copyright (c) 2025 Richard B

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// Config custom settings here
const DEFAULT_SETTINGS = {
  /// homepage redirect ///
  redirectHomepage: false, // Options: 'wl', 'subs', 'lib', false
  hideHomepageButton: false,
  /// homepage suggestions ///
  hideAllSuggestions: false,
  hideAllButOneRow: false,
  hideInfiniteScroll: false,
  /// video player ///
  skipAds: true,
  hideLiveChat: true,
  hideRelatedVideos: true,
  hideMiniPlayerButton: true,
  hidePlayNextButton: true,
  forceCinemaMode: true,
  /// shorts ///
  hideShorts: true,
  redirectShortsPlayer: true,
  /// misc ///
  hideSearchButton: false,
  cleanSearchResults: true,
  hideSponsoredContent: true,
  hideFilterBar: true,
  forceAudioTrack: true,
  preferredAudioLanguage: 'en', // Options: 'en', 'nl', 'de', 'fr', 'es', 'it', 'pt', 'ja', 'ko', 'zh', etc.
  /// audio control ///
  blockMultiAudio: true,
  aggressiveAudioControl: true,
};

const SETTINGS = DEFAULT_SETTINGS;

// Mark settings in HTML
const HTML = document.documentElement;
Object.keys(SETTINGS).forEach(key => {
  HTML.setAttribute(key, SETTINGS[key]);
});

// Add css to remove unnecessary elements
const DESKTOP_BLOCK_LIST = [
  // Ads 
  '#masthead-ad',
  'ytd-mealbar-promo-renderer',
  'ytd-carousel-ad-renderer',
  '.ytd-display-ad-renderer',
  'ytd-ad-slot-renderer',
  'div.ytp-ad-overlay-image',
  '.iv-branding.annotation-type-custom.annotation',

  // Shorts
  'html[hideShorts="true"] ytd-rich-section-renderer',
  'html[hideShorts="true"] ytd-reel-shelf-renderer',
  'html[hideShorts="true"] ytd-shelf-renderer',

  // Left Bar Navigation 
  'a[href="/feed/trending"]',
  'a[href="/feed/explore"]',
  'html[hideShorts="true"] ytd-guide-section-renderer a[title="Shorts"]',
  'html[hideShorts="true"] ytd-mini-guide-entry-renderer[aria-label="Shorts"]',
  'ytd-guide-section-renderer.ytd-guide-renderer.style-scope:nth-of-type(4)',
  'ytd-guide-section-renderer.ytd-guide-renderer.style-scope:nth-of-type(3)',

  // Homepage 
  'html[hideHomepageButton="true"] a:not(#logo)[href="/"]',
  'html[hideAllSuggestions="true"] ytd-browse[page-subtype="home"]',
  'html[hideAllButOneRow="true"] ytd-browse[page-subtype="home"] #header',
  'html[hideAllButOneRow="true"] ytd-browse[page-subtype="home"] ytd-rich-grid-renderer>#contents>ytd-rich-grid-row:nth-child(n+2)',
  'html[hideInfiniteScroll="true"] ytd-browse[page-subtype="home"] ytd-rich-grid-renderer>#contents>ytd-continuation-item-renderer',

  // Video Player
  'html[hideRelatedVideos="true"] #secondary>div.circle',
  'html[hideRelatedVideos="true"] #related',
  'html[hideRelatedVideos="true"] .html5-endscreen',
  'html[hidePlayNextButton="true"] a.ytp-next-button.ytp-button',
  'html[hidePlayNextButton="true"] a.ytp-prev-button.ytp-button',
  'html[hideChat="true"] #chat',
  'html[hideMiniPlayerButton="true"] .ytp-button.ytp-miniplayer-button',
  '.ytd-download-button-renderer.style-scope',

  // Search
  'div.sbdd_a',
  '#container.ytd-search ytd-search-pyv-renderer',
  'html[hideSearchButton="true"] div.ytd-masthead>ytd-searchbox',
  'html[hideSearchButton="true"] div.ytd-masthead>#voice-search-button',

  // Filter Bar (Chips)
  'html[hideFilterBar="true"] ytd-feed-filter-chip-bar-renderer',
  'html[hideFilterBar="true"] #chips-wrapper',
];

const MOBILE_BLOCK_LIST = [
  // Ads 
  'ytm-companion-ad-renderer',
  'ytm-promoted-sparkles-web-renderer',

  // Homepage 
  'html[hideHomepageButton="true"] div[tab-identifier="FEwhat_to_watch"]',
  'html[hideSearchButton="true"] #header-bar > header > div > button',
  'html[hideSearchButton="true"] #center.style-scope.ytd-masthead',

  // Shorts in search results
  'html[hideShorts="true"] ytm-reel-shelf-renderer.item',

  // Video Player 
  'html[hideRelatedVideos="true"] ytm-item-section-renderer[section-identifier="related-items"]>lazy-list',
  'html[hidePlayNextButton="true"] .player-controls-middle-core-buttons > div:nth-child(1)',
  'html[hidePlayNextButton="true"] .player-controls-middle-core-buttons > div:nth-child(5)',

  // Navigation Bar 
  'html[hideHomepageButton="true"] ytm-pivot-bar-item-renderer:nth-child(1)',
  'html[hideShorts="true"] ytm-pivot-bar-item-renderer:nth-child(2)',
  'ytm-chip-cloud-chip-renderer[chip-style="STYLE_EXPLORE_LAUNCHER_CHIP"]',

  // Filter Bar (Chips) - Mobile
  'html[hideFilterBar="true"] ytm-feed-filter-chip-bar-renderer',
];

function addStyle(css) {
  const style = document.createElement('style');
  style.textContent = css;
  document.head.appendChild(style);
}

// Add spacing fix for when filter bar is hidden
function addFilterBarSpacingFix() {
  const spacingCSS = `
    /* Fix spacing when filter bar is hidden - Multiple selectors for better compatibility */
    html[hideFilterBar="true"] ytd-browse[page-subtype="home"] #primary #contents,
    html[hideFilterBar="true"] ytd-browse[page-subtype="home"] ytd-rich-grid-renderer,
    html[hideFilterBar="true"] ytd-browse[page-subtype="home"] #contents.ytd-rich-grid-renderer {
      padding-top: 32px !important;
      margin-top: 16px !important;
    }
    
    /* Target the first video row specifically */
    html[hideFilterBar="true"] ytd-browse[page-subtype="home"] ytd-rich-grid-row:first-child {
      margin-top: 24px !important;
      padding-top: 16px !important;
    }
    
    /* Target the rich grid container */
    html[hideFilterBar="true"] ytd-browse[page-subtype="home"] #primary > #contents {
      padding-top: 32px !important;
    }
    
    /* Mobile spacing fix - multiple selectors */
    html[hideFilterBar="true"] ytm-browse[page-subtype="home"] #contents,
    html[hideFilterBar="true"] ytm-rich-grid-renderer #contents {
      padding-top: 24px !important;
      margin-top: 12px !important;
    }
    
    /* Subscriptions page spacing */
    html[hideFilterBar="true"] ytd-browse[page-subtype="subscriptions"] #primary #contents {
      padding-top: 32px !important;
    }
  `;
  addStyle(spacingCSS);
}

// Audio track control initialization
function initializeAudioBlocking() {
  // Hook into YouTube's player methods
  function hookYouTubePlayer() {
    if (!location.hostname.includes('youtube.com')) return;
    
    const playerElement = document.querySelector('#movie_player');
    if (!playerElement) return;
    
    try {
      // Override setAudioTrack
      if (playerElement.setAudioTrack && !playerElement.setAudioTrackHooked) {
        playerElement.setAudioTrackHooked = true;
        const originalSetAudioTrack = playerElement.setAudioTrack;
        playerElement.setAudioTrack = function(trackId) {
          if (SETTINGS.aggressiveAudioControl && trackId !== 0) {
            return; // Block non-original tracks
          }
          return originalSetAudioTrack.call(this, trackId);
        };
      }
      
      // Override selectAudioTrack
      if (playerElement.selectAudioTrack && !playerElement.selectAudioTrackHooked) {
        playerElement.selectAudioTrackHooked = true;
        const originalSelectAudioTrack = playerElement.selectAudioTrack;
        playerElement.selectAudioTrack = function(trackId) {
          if (SETTINGS.aggressiveAudioControl && trackId !== 0) {
            return; // Block non-original tracks
          }
          return originalSelectAudioTrack.call(this, trackId);
        };
      }
      
      // Override changeAudioTrack
      if (playerElement.changeAudioTrack && !playerElement.changeAudioTrackHooked) {
        playerElement.changeAudioTrackHooked = true;
        const originalChangeAudioTrack = playerElement.changeAudioTrack;
        playerElement.changeAudioTrack = function(trackId) {
          if (SETTINGS.aggressiveAudioControl && trackId !== 0) {
            return; // Block non-original tracks
          }
          return originalChangeAudioTrack.call(this, trackId);
        };
      }
      
    } catch (e) {
      // Fail silently
    }
  }
  
  // Monitor video elements for audio track changes
  const originalCreateElement = document.createElement;
  document.createElement = function(tagName) {
    const element = originalCreateElement.call(this, tagName);
    
    if (tagName.toLowerCase() === 'video') {
      // Monitor for audio tracks
      const checkForTracks = setInterval(() => {
        if (element.audioTracks && element.audioTracks.length > 0) {
          // Force to original track
          for (let i = 0; i < element.audioTracks.length; i++) {
            element.audioTracks[i].enabled = (i === 0);
          }
          
          // Set up continuous monitoring
          const monitorInterval = setInterval(() => {
            if (!document.contains(element)) {
              clearInterval(monitorInterval);
              return;
            }
            
            if (element.audioTracks && element.audioTracks.length > 1) {
              const currentActive = Array.from(element.audioTracks).findIndex(track => track.enabled);
              if (currentActive !== 0) {
                // Force back to original track
                for (let i = 0; i < element.audioTracks.length; i++) {
                  element.audioTracks[i].enabled = (i === 0);
                }
              }
            }
          }, 2000);
          
          clearInterval(checkForTracks);
        }
      }, 500);
      
      setTimeout(() => clearInterval(checkForTracks), 30000);
    }
    
    return element;
  };
  
  // Hook player methods periodically
  hookYouTubePlayer();
  setInterval(hookYouTubePlayer, 2000);
  
  // Add CSS to hide audio track UI elements
  if (SETTINGS.blockMultiAudio) {
    const audioBlockCSS = `
      /* Hide audio track related UI elements */
      html[blockMultiAudio="true"] .ytp-menuitem[role="menuitem"]:has([aria-label*="audio" i]),
      html[blockMultiAudio="true"] .ytp-menuitem[role="menuitem"]:has([aria-label*="sprache" i]),
      html[blockMultiAudio="true"] .ytp-menuitem[role="menuitem"]:has([aria-label*="langue" i]),
      html[blockMultiAudio="true"] .ytp-menuitem[role="menuitem"]:has([aria-label*="track" i]) {
        display: none !important;
      }
      
      /* Hide audio track indicators */
      html[blockMultiAudio="true"] .ytp-chrome-controls .ytp-button[aria-label*="audio" i] {
        display: none !important;
      }
      
      /* Hide audio track menu items */
      html[blockMultiAudio="true"] .ytp-panel-menu .ytp-menuitem:has([class*="audio"]) {
        display: none !important;
      }
    `;
    addStyle(audioBlockCSS);
  }
}

if (location.hostname.startsWith('www.')) {
  const styles = DESKTOP_BLOCK_LIST.map(e => `${e} {display: none !important}`).join('\n');
  addStyle(styles);
  addFilterBarSpacingFix();
}
if (location.hostname.startsWith('m.')) {
  const styles = MOBILE_BLOCK_LIST.map(e => `${e} {display: none !important}`).join('\n');
  addStyle(styles);
  addFilterBarSpacingFix();
}

// Initialize audio blocking
if (SETTINGS.blockMultiAudio) {
  initializeAudioBlocking();
}

// Start running dynamic settings
runDynamicSettings();

/***** Functions *****/

function runDynamicSettings() {
  if (SETTINGS.redirectHomepage) redirectHomepage();
  if (SETTINGS.redirectShortsPlayer) redirectShortsPlayer();
  if (SETTINGS.cleanSearchResults) cleanSearchResults();
  if (SETTINGS.skipAds) skipVideoAds();
  if (SETTINGS.hideRelatedVideos) disableRelatedAutoPlay();
  if (SETTINGS.forceCinemaMode) forceCinemaMode();
  if (SETTINGS.forceAudioTrack) forceAudioTrack();
  setTimeout(runDynamicSettings, 500);
}

function redirectHomepage() {
  if (location.pathname == '/') {
    if (SETTINGS.redirectHomepage == 'wl') {
      location.replace('/playlist/?list=WL');
    }
    if (SETTINGS.redirectHomepage == 'subs') {
      location.replace('/feed/subscriptions');
    }
    if (SETTINGS.redirectHomepage == 'lib') {
      location.replace('/feed/library');
    }
  }
}

function redirectShortsPlayer() {
  if (location.pathname.startsWith('/shorts')) {
    const redirPath = location.pathname.replace('shorts', 'watch');
    location.replace(redirPath);
  }
}

function disableRelatedAutoPlay() {
  // turn off auto play button
  const autoplayButton = document.querySelectorAll('.ytp-autonav-toggle-button[aria-checked=true]');
  autoplayButton?.forEach(e => {
    if (e && e.offsetParent) {
      e.click();
    }
  });
  // turn off auto play button on mobile
  const mAutoplayButton = document.querySelectorAll('.ytm-autonav-toggle-button-container[aria-pressed=true]');
  mAutoplayButton?.forEach(e => {
    if (e && e.offsetParent) {
      e.click();
    }
  });
}

function forceAudioTrack() {
  if (!location.pathname.startsWith('/watch')) return;
  
  const video = document.querySelector('.html5-main-video');
  if (!video || !video.audioTracks) return;
  
  if (video.audioTracks.length > 1) {
    // Look for preferred language audio track
    const preferredLang = SETTINGS.preferredAudioLanguage.toLowerCase();
    let targetTrack = 0; // Default to first track
    
    for (let i = 0; i < video.audioTracks.length; i++) {
      const track = video.audioTracks[i];
      const trackLang = track.language ? track.language.toLowerCase() : '';
      
      // Check for exact match or partial match
      if (trackLang === preferredLang || trackLang.startsWith(preferredLang + '-')) {
        targetTrack = i;
        break;
      }
    }
    
    // Set the target track
    for (let i = 0; i < video.audioTracks.length; i++) {
      video.audioTracks[i].enabled = (i === targetTrack);
    }
  }
}

function forceCinemaMode() {
  if (location.pathname.startsWith('/watch')) {
    // Check if we're not already in cinema mode
    const pageManager = document.querySelector('ytd-watch-flexy');
    if (pageManager && !pageManager.hasAttribute('theater')) {
      // Find and click the cinema mode button
      const cinemaButton = document.querySelector('.ytp-size-button');
      if (cinemaButton && cinemaButton.offsetParent) {
        cinemaButton.click();
      }
      
      // Alternative method: try to find the theater mode button
      const theaterButton = document.querySelector('button[aria-keyshortcuts="t"]');
      if (theaterButton && theaterButton.offsetParent) {
        theaterButton.click();
      }
      
      // Another alternative: look for the specific theater mode button
      const theaterModeBtn = document.querySelector('.ytp-button[data-tooltip-target-id*="theater"]');
      if (theaterModeBtn && theaterModeBtn.offsetParent) {
        theaterModeBtn.click();
      }
    }
  }
}

function skipVideoAds() {
  if (location.pathname.startsWith('/watch')) {
    // click "skip ad" button if it exists
    const adSkipButton = document.querySelector(".ytp-ad-skip-button-slot button,.ytp-ad-overlay-close-button");
    adSkipButton?.click();

    // skip ad video
    const adVideo = document.querySelector('.ad-showing');
    if (adVideo) {
      const video = document.querySelector('.html5-main-video');
      if (video && !isNaN(video?.duration)) {
        video.play();
        video.currentTime = video?.duration;
      }
    }
  }
}

function cleanSearchResults() {
  if (location.pathname.startsWith('/results')) {
    // Mobile
    const badges = document.querySelectorAll('ytm-badge');
    badges.forEach(badge => {
      // Only support Chinese and English now
      if (badge.innerText == '相關影片' || badge.innerText == '相关视频' || badge.innerText == 'Related') {
        badge.closest('ytm-video-with-context-renderer')?.remove();
      }
    });
  }
}