YouTube Enhancer

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

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Advertisement:

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

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();
  });
})();