YouTube Enhancer

Блокировка рекламы, скрытие Shorts, авто-пропуск, запоминание скорости, панель настроек.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

Advertisement:

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

Advertisement:

// ==UserScript==
// @name         YouTube Enhancer
// @namespace    https://gitlab.com/about-group/About-project
// @version      1.2.0
// @description  Блокировка рекламы, скрытие Shorts, авто-пропуск, запоминание скорости, панель настроек.
// @author       About-project
// @match        *://www.youtube.com/*
// @match        *://youtube.com/*
// @match        *://m.youtube.com/*
// @icon         https://www.youtube.com/favicon.ico
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const DEFAULT_SETTINGS = {
    blockAds: true,
    blockShorts: false,
    autoSkipAds: true,
    rememberSpeed: true,
    preferredSpeed: 1.0,
    hideEndCards: false,
    hideComments: false,
    confirmClose: false,
  };

  function loadSettings() {
    try {
      const raw = GM_getValue('yt_enhancer_settings', null);
      return raw ? Object.assign({}, DEFAULT_SETTINGS, JSON.parse(raw)) : { ...DEFAULT_SETTINGS };
    } catch {
      return { ...DEFAULT_SETTINGS };
    }
  }

  function saveSettings(s) {
    GM_setValue('yt_enhancer_settings', JSON.stringify(s));
  }

  let settings = loadSettings();

  const AD_CSS = `
    .ad-showing .video-ads,
    .ad-showing .ytp-ad-module,
    .ytp-ad-overlay-container,
    .ytp-ad-text-overlay,
    .ytp-ad-overlay-slot,
    .ytp-ad-image-overlay,
    #player-ads,
    #masthead-ad,
    #panels > ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-ads"],
    ytd-ad-slot-renderer,
    ytd-banner-promo-renderer,
    ytd-statement-banner-renderer,
    ytd-in-feed-ad-layout-renderer,
    ytd-promoted-sparkles-web-renderer,
    ytd-promoted-video-renderer,
    ytd-display-ad-renderer,
    ytd-companion-slot-renderer,
    ytd-action-companion-ad-renderer,
    tp-yt-paper-dialog:has(#dismiss-button),
    .ytd-mealbar-promo-renderer,
    ytd-popup-container:has(.ytd-enforcement-message-view-model),
    ytd-rich-item-renderer:has(.ytd-ad-slot-renderer),
    #related ytd-promoted-sparkles-web-renderer,
    #related ytd-promoted-video-renderer,
    ytd-popup-container:has(a[href*="/premium"]),
    tp-yt-paper-dialog:has(yt-upsell-dialog-renderer),
    yt-mealbar-promo-renderer {
      display: none !important;
    }
  `;

  const SHORTS_CSS = `
    ytd-rich-shelf-renderer[is-shorts],
    ytd-reel-shelf-renderer,
    ytd-mini-guide-entry-renderer[aria-label="Shorts"],
    ytd-guide-entry-renderer:has(a[title="Shorts"]),
    ytd-guide-entry-renderer:has(a[href*="/shorts"]),
    a[title="Shorts"],
    ytd-video-renderer:has(a[href*="/shorts/"]),
    ytd-grid-video-renderer:has(a[href*="/shorts/"]) {
      display: none !important;
    }
  `;

  const END_CARDS_CSS = `
    .ytp-ce-element,
    .ytp-cards-teaser,
    .ytp-ce-covering-overlay,
    .ytp-ce-element-shadow,
    .ytp-ce-covering-image,
    .ytp-ce-expanding-image,
    .ytp-ce-element.ytp-ce-video,
    .ytp-ce-element.ytp-ce-channel,
    .ytp-ce-element.ytp-ce-playlist {
      display: none !important;
    }
  `;

  const COMMENTS_CSS = `
    #comments, ytd-comments {
      display: none !important;
    }
  `;

  function injectStyle(id, css) {
    if (document.getElementById(id)) return;
    const el = document.createElement('style');
    el.id = id;
    el.textContent = css;
    (document.head || document.documentElement).appendChild(el);
  }

  function removeStyle(id) {
    document.getElementById(id)?.remove();
  }

  function applyStyles() {
    ['yt-enh-ads', 'yt-enh-shorts', 'yt-enh-endcards', 'yt-enh-comments'].forEach(removeStyle);
    if (settings.blockAds) injectStyle('yt-enh-ads', AD_CSS);
    if (settings.blockShorts) injectStyle('yt-enh-shorts', SHORTS_CSS);
    if (settings.hideEndCards) injectStyle('yt-enh-endcards', END_CARDS_CSS);
    if (settings.hideComments) injectStyle('yt-enh-comments', COMMENTS_CSS);
  }

  function el(tag, styles, text) {
    const node = document.createElement(tag);
    if (styles) node.setAttribute('style', styles);
    if (text != null) node.textContent = text;
    return node;
  }

  function showWelcome() {
    if (GM_getValue('yt_enhancer_welcome_seen', false)) return;

    function create() {
      if (!document.body) return;

      const overlay = el('div',
        'position:fixed!important;top:0!important;left:0!important;' +
        'width:100vw!important;height:100vh!important;z-index:2147483646!important;' +
        'background:rgba(0,0,0,.6)!important;display:flex!important;' +
        'align-items:center!important;justify-content:center!important;'
      );
      overlay.id = 'yt-enh-welcome-overlay';

      const box = el('div',
        'background:#1e1e2e!important;color:#cdd6f4!important;border-radius:18px!important;' +
        'padding:32px 36px!important;width:400px!important;max-width:90vw!important;' +
        'font-family:Segoe UI,Roboto,sans-serif!important;text-align:center!important;' +
        'box-shadow:0 12px 48px rgba(0,0,0,.6)!important;border:1px solid #45475a!important;' +
        'box-sizing:border-box!important;'
      );

      box.appendChild(el('div', 'font-size:48px!important;margin-bottom:12px!important;', '👋 Привет!'));
      box.appendChild(el('h2',
        'margin:0 0 8px!important;font-size:20px!important;color:#f38ba8!important;font-weight:600!important;',
        'Привет! Спасибо, что установили YouTube Enhancer'
      ));
      box.appendChild(el('p',
        'margin:0 0 6px!important;font-size:14px!important;line-height:1.5!important;color:#a6adc8!important;',
        'Присоединяйтесь к нашему Discord-серверу:'
      ));

      const features = el('div',
        'text-align:left!important;margin:14px 0 18px!important;padding:12px 16px!important;' +
        'background:#313244!important;border-radius:10px!important;' +
        'font-size:13px!important;line-height:1.7!important;color:#cdd6f4!important;'
      );
      [
        'Обновления и новые функции первыми',
        'Помощь и поддержка от сообщества',
        'Предлагайте свои идеи для скрипта',
        'Общайтесь с другими пользователями',
      ].forEach(line => features.appendChild(el('div', null, line)));
      box.appendChild(features);

      const btns = el('div', 'display:flex!important;gap:12px!important;justify-content:center!important;');

      const join = el('a',
        'padding:10px 24px!important;border-radius:10px!important;font-size:14px!important;' +
        'font-weight:600!important;cursor:pointer!important;text-decoration:none!important;' +
        'background:#5865F2!important;color:#fff!important;display:inline-block!important;',
        'Вступить в Discord'
      );
      join.href = 'https://discord.gg/UvsnYm2msa';
      join.target = '_blank';
      join.rel = 'noopener';
      join.addEventListener('click', () => dismiss());

      const later = el('button',
        'padding:10px 24px!important;border-radius:10px!important;font-size:14px!important;' +
        'font-weight:600!important;cursor:pointer!important;border:none!important;' +
        'background:#45475a!important;color:#cdd6f4!important;',
        'Позже'
      );
      later.addEventListener('click', () => dismiss());

      btns.appendChild(join);
      btns.appendChild(later);
      box.appendChild(btns);
      overlay.appendChild(box);
      document.documentElement.appendChild(overlay);

      function dismiss() {
        GM_setValue('yt_enhancer_welcome_seen', true);
        overlay.remove();
      }

      overlay.addEventListener('click', e => { if (e.target === overlay) dismiss(); });
    }

    if (document.body) create();
    else document.addEventListener('DOMContentLoaded', create);
  }

  function trySkipAd() {
    if (!settings.autoSkipAds) return;

    const skipBtns = document.querySelectorAll(
      '.ytp-ad-skip-button, .ytp-ad-skip-button-modern, .ytp-skip-ad-button, ' +
      'button.ytp-ad-skip-button-modern, .ytp-ad-skip-button-container button'
    );
    skipBtns.forEach(b => { if (b.offsetParent !== null) b.click(); });

    const player = document.querySelector('.ad-showing video.html5-main-video');
    if (player && player.duration && isFinite(player.duration)) {
      player.currentTime = player.duration;
    }

    const closeOverlay = document.querySelector('.ytp-ad-overlay-close-button');
    if (closeOverlay) closeOverlay.click();
  }

  function applyPlaybackSpeed() {
    if (!settings.rememberSpeed) return;
    const video = document.querySelector('video.html5-main-video');
    if (video && video.playbackRate !== settings.preferredSpeed) {
      video.playbackRate = settings.preferredSpeed;
    }
  }

  function watchSpeedChange() {
    const video = document.querySelector('video.html5-main-video');
    if (!video || video.__ytEnhSpeedWatcher) return;
    video.__ytEnhSpeedWatcher = true;
    video.addEventListener('ratechange', () => {
      if (!document.querySelector('.ad-showing') && settings.rememberSpeed) {
        settings.preferredSpeed = video.playbackRate;
        saveSettings(settings);
      }
    });
  }

  window.addEventListener('beforeunload', e => {
    if (!settings.confirmClose) return;
    const video = document.querySelector('video.html5-main-video');
    if (video && !video.paused) {
      e.preventDefault();
      e.returnValue = '';
    }
  });

  function openSettingsPanel() {
    if (document.getElementById('yt-enh-panel')) return;

    const overlay = el('div',
      'position:fixed!important;top:0!important;left:0!important;' +
      'width:100vw!important;height:100vh!important;z-index:2147483646!important;' +
      'background:rgba(0,0,0,.5)!important;display:block!important;'
    );
    overlay.id = 'yt-enh-overlay';
    overlay.addEventListener('click', closeSettingsPanel);

    const panel = el('div',
      'position:fixed!important;top:50%!important;left:50%!important;' +
      'transform:translate(-50%,-50%)!important;z-index:2147483647!important;' +
      'background:#1e1e2e!important;color:#cdd6f4!important;' +
      'border-radius:16px!important;padding:24px 28px!important;' +
      'width:380px!important;max-height:80vh!important;overflow-y:auto!important;' +
      'font-family:Segoe UI,Roboto,sans-serif!important;font-size:14px!important;' +
      'box-shadow:0 8px 32px rgba(0,0,0,.55)!important;' +
      'border:1px solid #45475a!important;box-sizing:border-box!important;' +
      'display:block!important;visibility:visible!important;opacity:1!important;'
    );
    panel.id = 'yt-enh-panel';

    panel.appendChild(el('h2',
      'margin:0 0 18px!important;font-size:18px!important;color:#f38ba8!important;text-align:center!important;font-weight:600!important;',
      'YouTube Enhancer'
    ));

    const toggles = [
      ['blockAds', 'Блокировать рекламу'],
      ['autoSkipAds', 'Авто-пропуск рекламы'],
      ['blockShorts', 'Скрыть Shorts'],
      ['hideEndCards', 'Скрыть подсказки в конце видео'],
      ['hideComments', 'Скрыть комментарии'],
      ['rememberSpeed', 'Запоминать скорость'],
      ['confirmClose', 'Подтверждение закрытия вкладки'],
    ];

    toggles.forEach(([key, text]) => {
      const row = el('label',
        'display:flex!important;align-items:center!important;justify-content:space-between!important;' +
        'padding:8px 0!important;border-bottom:1px solid #313244!important;cursor:pointer!important;color:#cdd6f4!important;'
      );
      row.appendChild(el('span', 'color:#cdd6f4!important;', text));
      const cb = el('input', 'width:18px!important;height:18px!important;accent-color:#f38ba8!important;cursor:pointer!important;margin:0!important;');
      cb.type = 'checkbox';
      cb.checked = !!settings[key];
      cb.addEventListener('change', () => { settings[key] = cb.checked; });
      row.appendChild(cb);
      panel.appendChild(row);
    });

    const speedRow = el('div',
      'display:flex!important;align-items:center!important;justify-content:space-between!important;' +
      'padding:10px 0!important;border-bottom:1px solid #313244!important;color:#cdd6f4!important;'
    );
    speedRow.appendChild(el('span', 'color:#cdd6f4!important;', 'Скорость по умолчанию'));
    const sel = el('select',
      'background:#313244!important;color:#cdd6f4!important;border:none!important;' +
      'border-radius:6px!important;padding:4px 8px!important;font-size:14px!important;'
    );
    [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3].forEach(s => {
      const opt = el('option', null, s + 'x');
      opt.value = s;
      if (settings.preferredSpeed === s) opt.selected = true;
      sel.appendChild(opt);
    });
    sel.addEventListener('change', () => { settings.preferredSpeed = parseFloat(sel.value); });
    speedRow.appendChild(sel);
    panel.appendChild(speedRow);

    const save = el('button',
      'display:block!important;margin:18px auto 0!important;background:#f38ba8!important;' +
      'color:#1e1e2e!important;border:none!important;border-radius:8px!important;' +
      'padding:8px 28px!important;font-size:14px!important;font-weight:600!important;cursor:pointer!important;',
      'Сохранить и закрыть'
    );
    save.addEventListener('click', () => {
      saveSettings(settings);
      closeSettingsPanel();
      applyStyles();
      applyPlaybackSpeed();
    });
    panel.appendChild(save);

    document.documentElement.appendChild(overlay);
    document.documentElement.appendChild(panel);
  }

  function closeSettingsPanel() {
    document.getElementById('yt-enh-panel')?.remove();
    document.getElementById('yt-enh-overlay')?.remove();
  }

  if (typeof GM_registerMenuCommand === 'function') {
    GM_registerMenuCommand('Настройки YouTube Enhancer', openSettingsPanel);
  }

  window.ytEnhOpenSettings = openSettingsPanel;

  document.addEventListener('keydown', e => {
    if (e.altKey && (e.code === 'KeyS' || e.key === 's' || e.key === 'S' || e.key === 'ы' || e.key === 'Ы')) {
      e.preventDefault();
      e.stopPropagation();
      openSettingsPanel();
    }
  }, true);

  function tick() {
    trySkipAd();
    applyPlaybackSpeed();
    watchSpeedChange();
  }

  applyStyles();
  showWelcome();

  const observer = new MutationObserver(tick);
  function startObserver() {
    observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
  }
  if (document.body) startObserver();
  else document.addEventListener('DOMContentLoaded', startObserver);

  setInterval(tick, 1000);

  document.addEventListener('yt-navigate-finish', () => {
    applyStyles();
    tick();
  });
})();