vBlockTube

Blocks YouTube ads and provides enhanced features for a better viewing experience.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         vBlockTube
// @namespace    https://www.github.com/vippium/
// @version      1.8.2
// @description  Blocks YouTube ads and provides enhanced features for a better viewing experience.
// @author       vippium
// @match        https://www.youtube.com/*
// @match        https://m.youtube.com/*
// @match        https://music.youtube.com/*
// @match        https://www.youtubekids.com/*
// @exclude      https://www.youtube.com/live_chat*
// @exclude      https://www.youtube.com/embed*
// @connect      api.sponsor.ajay.app
// @connect      update.greasyfork.org
// @connect      cnv.cx
// @connect      googlevideo.com
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_info
// @grant        GM_addValueChangeListener
// @grant        GM_removeValueChangeListener
// @grant        GM_setClipboard
// @grant        GM_listValues
// @grant        GM_deleteValue
// @run-at       document-start
// @icon         https://cdn.iconscout.com/icon/free/png-256/free-youtube-logo-icon-svg-download-png-3649968.png?f=webp
// @homepageURL  https://github.com/vippium/vBlockTube
// @license      MIT
// ==/UserScript==

(function () {
  let uuid = GM_getValue("uuid");
  if (!uuid) {
    uuid = crypto
      .randomUUID()
      .substring(0, Math.floor(Math.random() * 5) + 6)
      .replace(/-/g, "");
    GM_setValue("uuid", uuid);
  }

  if (unsafeWindow[uuid]) {
    console.log("Duplicate injection!");
    return;
  }

  unsafeWindow[uuid] = true;

  let debugger_fun_name;

  const disableRemovePlayerAd = false;

  const open_config_keyword = "2333";
  const display_error_keyword = "2444";
  const reset_config_keyword = "2555";
  const custom_panel_keyword = "2666";

  let channel_id = GM_getValue("last_channel_id", "default");

  const user_data_listener = get_user_data_listener();
  const user_data_api = get_user_data_api();
  let user_data = user_data_api.get();

  let tmp_debugger_value;

  let limit_eval = false;

  let element_monitor_observer;

  let is_account_init;

  let fake_fetch;

  const inject_info = {
    ytInitialPlayerResponse: false,
    ytInitialData: false,
    ytInitialReelWatchSequenceResponse: false,
    xhr: false,
    fetch: false,
  };

  const $ = unsafeWindow.document.querySelector.bind(unsafeWindow.document);
  const $$ = unsafeWindow.document.querySelectorAll.bind(unsafeWindow.document);


  const debounce = (func, wait) => {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  };

  const throttle = (func, limit) => {
    let inThrottle;
    return function (...args) {
      if (!inThrottle) {
        func.apply(this, args);
        inThrottle = true;
        setTimeout(() => (inThrottle = false), limit);
      }
    };
  };

  // Selector Caching System
  const selectorCache = {
    cache: new Map(),
    ttl: 5000,
    timestamps: new Map(),
    frequentSelectors: {
      video: "video",
      player: ".html5-video-player",
      secondary: "#secondary",
      videoContainer: ".html5-video-container",
      playerContainer: "#movie_player",
    },

    get(selector) {
      const now = Date.now();
      const timestamp = this.timestamps.get(selector);

      if (this.cache.has(selector) && timestamp && now - timestamp < this.ttl) {
        return this.cache.get(selector);
      }

      try {
        const element = $(selector);
        if (element) {
          this.cache.set(selector, element);
          this.timestamps.set(selector, now);
          return element;
        }
      } catch (e) {}

      this.cache.delete(selector);
      this.timestamps.delete(selector);
      return null;
    },

    getVideo() {
      return this.get(this.frequentSelectors.video);
    },

    getPlayer() {
      return this.get(this.frequentSelectors.player);
    },

    getSecondary() {
      return this.get(this.frequentSelectors.secondary);
    },

    getVideoContainer() {
      return this.get(this.frequentSelectors.videoContainer);
    },

    getPlayerContainer() {
      return this.get(this.frequentSelectors.playerContainer);
    },

    invalidate(selector) {
      this.cache.delete(selector);
      this.timestamps.delete(selector);
    },

    invalidateAll() {
      this.cache.clear();
      this.timestamps.clear();
    },
  };

  const observerManager = {
    observers: new Map(),
    listeners: new Map(),
    mediaQueryListeners: [],

    addObserver(name, observer) {
      if (this.observers.has(name)) {
        this.observers.get(name).disconnect();
      }
      this.observers.set(name, observer);
    },

    removeObserver(name) {
      if (this.observers.has(name)) {
        this.observers.get(name).disconnect();
        this.observers.delete(name);
      }
    },

    addListener(target, eventName, handler, options) {
      if (!target) return;
      target.addEventListener(eventName, handler, options);
      const key = `${target.nodeName || "window"}_${eventName}`;
      if (!this.listeners.has(key)) {
        this.listeners.set(key, []);
      }
      this.listeners.get(key).push({ target, eventName, handler, options });
    },

    addMediaQueryListener(mql, handler) {
      mql.addListener(handler);
      this.mediaQueryListeners.push({ mql, handler });
    },

    disconnectAll() {
      for (const [name, observer] of this.observers) {
        observer.disconnect();
      }
      this.observers.clear();

      for (const [key, listeners] of this.listeners) {
        for (const { target, eventName, handler } of listeners) {
          target.removeEventListener(eventName, handler);
        }
      }
      this.listeners.clear();

      for (const { mql, handler } of this.mediaQueryListeners) {
        mql.removeListener(handler);
      }
      this.mediaQueryListeners = [];
    },

    logStats() {
      log(
        `[ObserverManager] Observers: ${this.observers.size}, Listeners: ${this.listeners.size}, MediaQueries: ${this.mediaQueryListeners.length}`,
        0,
      );
    },
  };

  const darkModeSystem = {
    styleId: "vblocktube-dark-mode-style",
    isDarkMode: false,
    mediaQuery: null,
    mediaListener: null,

    getSystemPreference() {
      return unsafeWindow.matchMedia("(prefers-color-scheme: dark)").matches;
    },

    shouldUseDarkMode() {
      if (!user_data.dark_mode) return false;
      if (user_data.dark_mode === "on") return true;
      if (user_data.dark_mode === "off") return false;
      if (user_data.dark_mode === "auto") return this.getSystemPreference();
      return false;
    },

    apply() {
      this.isDarkMode = this.shouldUseDarkMode();
      const existing = unsafeWindow.document.getElementById(this.styleId);

      if (!this.isDarkMode) {
        if (existing) existing.remove();
        return;
      }

      const darkCSS = `
          #xxx_popup,
          #yt-hide-buttons-popup,
          #yt-error-popup,
          .popup {
            background-color: #1e1e1e !important;
            color: #e0e0e0 !important;
            border-color: #404040 !important;
          }

          #xxx_popup .popup-header,
          #yt-hide-buttons-popup #yt-hide-buttons-header,
          #yt-error-header,
          .popup-header {
            background-color: #2d2d2d !important;
            color: #e0e0e0 !important;
            border-color: #404040 !important;
          }

          #xxx_popup input[type="text"],
          #xxx_popup input[type="number"],
          #xxx_popup select,
          #yt-hide-buttons-popup input[type="text"],
          #yt-hide-buttons-popup select,
          .popup input[type="text"],
          .popup select {
            background-color: #2d2d2d !important;
            color: #e0e0e0 !important;
            border-color: #404040 !important;
          }

          #xxx_popup input[type="checkbox"],
          #yt-hide-buttons-popup input[type="checkbox"],
          .popup input[type="checkbox"] {
            accent-color: #3498db;
          }

          #xxx_popup button,
          #yt-hide-buttons-popup button,
          #yt-error-close,
          #yt-vbt-settings-btn,
          .popup button {
            background-color: #3498db !important;
            color: white !important;
          }

          #xxx_popup button:hover,
          #yt-hide-buttons-popup button:hover,
          #yt-error-close:hover,
          #yt-vbt-settings-btn:hover,
          .popup button:hover {
            background-color: #2980b9 !important;
          }

          #xxx_popup .popup-content,
          #yt-hide-buttons-popup .yt-hide-buttons-body,
          #yt-error-body,
          .popup-content {
            background-color: #1e1e1e !important;
            color: #e0e0e0 !important;
          }

          #yt-error-body {
            color: #e0e0e0 !important;
          }

          #xxx_popup .yt-hb-section-title,
          #xxx_popup .recommend-title,
          #yt-error-header {
            color: #e0e0e0 !important;
            background-color: #2d2d2d !important;
            border-color: #404040 !important;
          }

          #xxx_popup label,
          #yt-hide-buttons-popup label {
            color: #e0e0e0 !important;
          }

          #xxx_popup .popup-body,
          #yt-hide-buttons-popup .yt-hide-buttons-body {
            background-color: #1e1e1e !important;
          }

          #xxx_popup h1,
          #xxx_popup .item-group {
            color: #e0e0e0 !important;
          }

          /* Settings backup sub-popup dark mode */

          #yt-vbt-backup-popup {
            background-color: #1e1e1e !important;
            border-color: #404040 !important;
            color: #e0e0e0 !important;
          }

          #yt-vbt-backup-header {
            background-color: #2d2d2d !important;
            color: #e0e0e0 !important;
          }

          #yt-vbt-backup-body {
            background-color: #1e1e1e !important;
            color: #e0e0e0 !important;
          }

          #yt-vbt-backup-body button {
            background-color: #3498db !important;
            color: white !important;
          }

          #yt-vbt-backup-body button:hover {
            background-color: #2980b9 !important;
          }

          #yt-vbt-backup-note {
            color: #aaa !important;
          }

          /* Watch Page Tweaks (2666) panel dark mode */
          #yt-hide-buttons-popup .yt-hb-row {
            color: #e0e0e0 !important;
          }

          #yt-hide-buttons-popup .yt-hb-row label {
            color: #e0e0e0 !important;
          }

          #yt-hide-buttons-popup .yt-hb-row.disabled label {
            color: #666666 !important;
          }

          #yt-hide-buttons-popup .yt-hb-select-row {
            color: #e0e0e0 !important;
          }

          #yt-hide-buttons-popup .yt-hb-select-row label {
            color: #e0e0e0 !important;
          }

          #yt-hide-buttons-popup .yt-hb-section {
            border-color: #404040 !important;
            background-color: #1e1e1e !important;
          }

          #yt-hide-buttons-popup .yt-hb-section-header {
            background-color: #2d2d2d !important;
            color: #e0e0e0 !important;
            border-color: #404040 !important;
          }

          #yt-hide-buttons-popup .yt-hb-caret {
            color: #e0e0e0 !important;
          }

          #yt-hide-buttons-popup .yt-hb-section-body {
            background-color: #1e1e1e !important;
            color: #e0e0e0 !important;
          }

          /* Scrollbar styling for dark mode */
          #xxx_popup::-webkit-scrollbar,
          #yt-error-body::-webkit-scrollbar,
          #yt-hide-buttons-popup::-webkit-scrollbar {
            width: 8px;
          }

          #xxx_popup::-webkit-scrollbar-track,
          #yt-error-body::-webkit-scrollbar-track,
          #yt-hide-buttons-popup::-webkit-scrollbar-track {
            background: #2d2d2d !important;
          }

          #xxx_popup::-webkit-scrollbar-thumb,
          #yt-error-body::-webkit-scrollbar-thumb,
          #yt-hide-buttons-popup::-webkit-scrollbar-thumb {
            background: #404040 !important;
            border-radius: 4px;
          }

          #xxx_popup::-webkit-scrollbar-thumb:hover,
          #yt-error-body::-webkit-scrollbar-thumb:hover,
          #yt-hide-buttons-popup::-webkit-scrollbar-thumb:hover {
            background: #555555 !important;
          }
        `;

      if (!existing) {
        const style = unsafeWindow.document.createElement("style");
        style.id = this.styleId;
        style.textContent = darkCSS;
        unsafeWindow.document.head.appendChild(style);
      } else {
        existing.textContent = darkCSS;
      }
    },

    init() {
      if (!this.mediaQuery) {
        this.mediaQuery = unsafeWindow.matchMedia(
          "(prefers-color-scheme: dark)",
        );
        this.mediaListener = (e) => {
          if (user_data.dark_mode === "auto") {
            this.apply();
          }
        };
        this.mediaQuery.addListener(this.mediaListener);
        observerManager.addMediaQueryListener(
          this.mediaQuery,
          this.mediaListener,
        );
      }
    },

    destroy() {
      if (this.mediaQuery && this.mediaListener) {
        this.mediaQuery.removeListener(this.mediaListener);
      }
    },
  };

  const origin_console = console;
  const script_url =
    "https://update.greasyfork.org/scripts/557720/vBlockTube.user.js";
  let href = location.href;
  let ytInitialPlayerResponse_rule;
  let ytInitialData_rule;
  let ytInitialReelWatchSequenceResponse_rule;
  let open_debugger = false;
  let isinint = false;
  let mobile_web;
  let movie_channel_info;
  let mobile_movie_channel_info;
  let flag_info;

  let debugger_ytInitialPlayerResponse;
  let debugger_ytInitialData;
  let debugger_ytInitialReelWatchSequenceResponse;
  let debugger_music_initialData;
  const error_messages = [];
  let data_process = get_data_process();
  let shorts_fun = get_shorts_fun();
  let yt_api = get_yt_api();
  const shorts_parse_delay = 500;
  const browser_info = getBrowserInfo();
  let page_type = get_page_type();
  const config_api = get_config_api();
  if (disableRemovePlayerAd) {
    config_api.common_ytInitialPlayerResponse_rule =
      config_api.common_ytInitialPlayerResponse_rule.slice(3);
  }
  const SPLIT_TAG = "###";
  let cur_watch_channle_id;
  const trustedScript = trustedScriptInit();
  setSecurePolicy();

  const QUALITY_ORDER = [
    "highres",
    "hd4320",
    "hd2880",
    "hd2160",
    "hd1440",
    "hd1080",
    "hd720",
    "large",
    "medium",
    "small",
    "tiny",
    "auto",
  ];

  function init_old_player_ui() {
    if (user_data.old_player_ui !== "on") return;

    const DELHI_FLAGS_REGEX =
      /&?delhi_modern_web_player(?:=true)?|&?delhi_modern_web_player_icons=true/g;
    const STYLE_ID = "yt-old-player-ui-style";

    function stripDelhiFlags() {
      const yt = unsafeWindow.yt;
      if (!yt?.config_?.WEB_PLAYER_CONTEXT_CONFIGS) return false;
      let changed = false;
      for (const key in yt.config_.WEB_PLAYER_CONTEXT_CONFIGS) {
        const cfg = yt.config_.WEB_PLAYER_CONTEXT_CONFIGS[key];
        if (typeof cfg?.serializedExperimentFlags === "string") {
          const cleaned = cfg.serializedExperimentFlags
            .replace(DELHI_FLAGS_REGEX, "")
            .replace(/&&+/g, "&")
            .replace(/^&|&$/g, "");
          if (cleaned !== cfg.serializedExperimentFlags) {
            cfg.serializedExperimentFlags = cleaned;
            changed = true;
          }
        }
      }
      return changed;
    }

    function injectOldPlayerCSS() {
      if (unsafeWindow.document.getElementById(STYLE_ID)) return;
      const style = unsafeWindow.document.createElement("style");
      style.id = STYLE_ID;
      style.textContent = `
        .ytp-fullscreen-quick-actions,
        .ytp-fullscreen-grid {
          display: none !important;
        }
      `;
      (unsafeWindow.document.head || unsafeWindow.document.documentElement).appendChild(style);
    }

    injectOldPlayerCSS();

    const observer = new MutationObserver(() => {
      if (stripDelhiFlags()) {
        observer.disconnect();
      }
    });
    observer.observe(unsafeWindow.document.documentElement, {
      childList: true,
      subtree: false,
    });

    unsafeWindow.addEventListener("yt-navigate-finish", () => {
      stripDelhiFlags();
      injectOldPlayerCSS();
    });
  }

  function init_quality_preset() {
    const setQuality = () => {
      try {
        if (!user_data.default_quality || user_data.default_quality === "off")
          return;

        const video = selectorCache.getVideo();
        if (!video) return;

        const player = selectorCache.getPlayer();
        if (!player) return;

        const targetQuality = user_data.default_quality;
        const levels = player.getAvailableQualityLevels?.();
        if (!levels || levels.length === 0) return;

        const startIndex = QUALITY_ORDER.indexOf(targetQuality);
        const candidates =
          startIndex >= 0 ? QUALITY_ORDER.slice(startIndex) : QUALITY_ORDER;
        const chosen =
          candidates.find((q) => levels.includes(q)) ||
          levels[levels.length - 1];

        if (chosen && player.getPlaybackQualityLabel?.() !== chosen) {
          player.setPlaybackQualityRange?.(chosen, chosen);
        }
      } catch (e) {}
    };

    const observer = new MutationObserver(() => {
      const video = selectorCache.getVideo();
      if (video && !video.hasAttribute("data-quality-set")) {
        video.setAttribute("data-quality-set", "true");
        observerManager.addListener(video, "loadeddata", setQuality, {
          once: true,
        });
        debounce(setQuality, 300)();
      }
    });

    observer.observe($("body"), { childList: true, subtree: true });
    observerManager.addObserver("quality_preset", observer);
  }

  function init_speed_preset() {
    const setSpeed = () => {
      try {
        if (!user_data.default_speed) return;

        const video = selectorCache.getVideo();
        if (!video) return;

        const targetSpeed = parseFloat(user_data.default_speed);
        if (video.playbackRate !== targetSpeed) {
          video.playbackRate = targetSpeed;
        }
      } catch (e) {}
    };

    const debouncedSetSpeed = debounce(setSpeed, 300);
    const observer = new MutationObserver(() => {
      const video = selectorCache.getVideo();
      if (video && !video.hasAttribute("data-speed-set")) {
        video.setAttribute("data-speed-set", "true");
        debouncedSetSpeed();
        observerManager.addListener(video, "loadeddata", setSpeed, {
          once: true,
        });
      }
    });

    observer.observe($("body"), { childList: true, subtree: true });
    observerManager.addObserver("speed_preset", observer);
  }


  function init_restore_red_progress_bar() {
    if (user_data.restore_red_progress_bar !== "on") return;

    const style = unsafeWindow.document.createElement("style");
    style.textContent = `
        .ytp-play-progress,
        #progress.ytd-thumbnail-overlay-resume-playback-renderer,
        .ytThumbnailOverlayProgressBarHostWatchedProgressBarSegment,
        .ytChapteredProgressBarChapteredPlayerBarChapterSeen,
        .ytChapteredProgressBarChapteredPlayerBarFill,
        .ytProgressBarLineProgressBarPlayed,
        #progress.yt-page-navigation-progress,
        .progress-bar-played.ytd-progress-bar-line,
        .thumbnail-overlay-resume-playback-progress {
          background: #f03 !important;
        }
      `;
    unsafeWindow.document.head.appendChild(style);
  }

  function init_search_thumbnail_small() {
    if (user_data.search_thumbnail_small !== "on") return;

    const style = unsafeWindow.document.createElement("style");
    style.textContent = `
        ytd-search ytd-video-renderer ytd-thumbnail.ytd-video-renderer,
        ytd-search yt-lockup-view-model .yt-lockup-view-model__content-image,
        ytd-search ytd-channel-renderer #avatar-section {
          max-width: 360px !important;
        }
      `;
    unsafeWindow.document.head.appendChild(style);
  }

  function init_restore_related_sidebar_layout() {
    if (user_data.restore_related_sidebar_layout !== "on") return;
    if (!["yt_watch"].includes(page_type)) return;

    const style = unsafeWindow.document.createElement("style");
    style.textContent = `
        ytd-watch-flexy #secondary {
          max-width: 402px !important;
        }

        #secondary #related .yt-lockup-view-model--vertical {
          display: flex !important;
          flex-direction: row !important;
          height: inherit !important;
        }

        #secondary #related .yt-lockup-view-model--vertical .yt-lockup-view-model__content-image {
          display: flex !important;
          flex: none !important;
          padding-right: 16px !important;
          justify-content: center !important;
          width: 168px !important;
          padding-bottom: 0 !important;
        }

        #secondary #related .yt-lockup-view-model__content-image {
          max-width: 168px !important;
        }

        #secondary #related .yt-lockup-view-model--vertical .yt-lockup-view-model__metadata {
          flex: 1 !important;
        }

        #secondary #related .yt-lockup-view-model--vertical.yt-lockup-view-model--collection-stack-1 {
          position: relative !important;
          margin-top: 6px !important;
        }

        #secondary #related .yt-lockup-view-model--vertical.yt-lockup-view-model--collection-stack-2 {
          position: relative !important;
          margin-top: 10px !important;
        }

        #secondary #related .yt-lockup-view-model--vertical.yt-lockup-view-model--compact .yt-lockup-view-model__content-image {
          padding-right: 8px !important;
        }

        #secondary #related .yt-lockup-metadata-view-model--vertical .yt-lockup-metadata-view-model__avatar {
          display: none !important;
        }

        #secondary #related ytd-watch-next-secondary-results-renderer[use-dynamic-secondary-columns]:not(:has(ytd-item-section-renderer)) #items,
        #secondary #related ytd-watch-next-secondary-results-renderer[use-dynamic-secondary-columns] #contents {
          grid-template-columns: 1fr !important;
        }

        #secondary #related ytd-watch-next-secondary-results-renderer[use-dynamic-secondary-columns] .lockup {
          margin-bottom: 0 !important;
        }
      `;
    unsafeWindow.document.head.appendChild(style);

    const applyInlineStyles = (element) => {
      if (!element) return;

      element
        .querySelectorAll(".yt-lockup-view-model--vertical")
        .forEach((el) => {
          el.style.cssText =
            "display: flex !important; flex-direction: row !important; height: inherit !important;";
        });

      element
        .querySelectorAll(
          ".yt-lockup-view-model--vertical .yt-lockup-view-model__content-image",
        )
        .forEach((el) => {
          el.style.cssText =
            "display: flex !important; flex: none !important; padding-right: 16px !important; justify-content: center !important; width: 168px !important; padding-bottom: 0 !important;";
        });

      element
        .querySelectorAll(".yt-lockup-view-model__content-image")
        .forEach((el) => {
          if (!el.querySelector(".yt-lockup-view-model--vertical")) {
            el.style.maxWidth = "168px !important";
          }
        });

      element
        .querySelectorAll(
          ".yt-lockup-view-model--vertical .yt-lockup-view-model__metadata",
        )
        .forEach((el) => {
          el.style.flex = "1 !important";
        });

      element
        .querySelectorAll(
          ".yt-lockup-metadata-view-model--vertical .yt-lockup-metadata-view-model__avatar",
        )
        .forEach((el) => {
          el.style.display = "none !important";
        });
    };

    const relatedContainer = $("#secondary #related");
    if (relatedContainer) {
      applyInlineStyles(relatedContainer);
    }

    const debouncedApplyStyles = debounce(() => {
      applyInlineStyles($("#secondary #related"));
    }, 300);

    const observer = new MutationObserver((mutations) => {
      let hasRelevantChange = false;
      mutations.forEach((mutation) => {
        if (mutation.type === "childList") {
          mutation.addedNodes.forEach((node) => {
            if (node.nodeType === 1) {
              hasRelevantChange = true;
              applyInlineStyles(node);
            }
          });
        }
      });
      if (hasRelevantChange) {
        debouncedApplyStyles();
      }
    });

    const secondary = $("#secondary");
    if (secondary) {
      observer.observe(secondary, {
        childList: true,
        subtree: true,
      });
    }
  }

  function restore_sidebar_layout_on_ytInitialData(data) {
    return data;
  }

  init_old_player_ui();
  init_disable_saturated_hover();

  init();
  function init() {
    log("Initialization started!" + href, 0);
    url_observer();
    is_account_init = false;
    data_process.set_obj_filter(obj_process_filter);
    config_api.config_init(user_data.language);
    const init_hook = init_hook_collection();
    init_hook.property();
    init_hook.other();
    init_hook.request();

    unsafeWindow.document.addEventListener("DOMContentLoaded", function () {
      set_search_listen();
      on_page_change();
      init_create_button_observer();
      init_quality_preset();
      init_speed_preset();
      darkModeSystem.init();
      darkModeSystem.apply();

      if (document.readyState === "complete") {
        runDeferredInit();
      } else {
        if ("requestIdleCallback" in window) {
          requestIdleCallback(() => runDeferredInit(), { timeout: 5000 });
        } else {
          setTimeout(runDeferredInit, 100);
        }
      }

      function runDeferredInit() {
        init_restore_red_progress_bar();
        init_search_thumbnail_small();
        init_restore_related_sidebar_layout();
        init_disable_ambient_mode();
        init_disable_saturated_hover();
        init_disable_play_on_hover();
        init_disable_end_cards();
        init_interruptions_remover();
        init_miniplayer_button();
      }

      const hoverToggleListener = (key, _oldValue, newValue) => {
        if (key !== channel_id || !newValue) return;
        user_data = newValue;
        darkModeSystem.apply();
        init_disable_saturated_hover();
        init_disable_play_on_hover();
        init_disable_end_cards();
      };
      GM_addValueChangeListener(channel_id, hoverToggleListener);
    });

    init_global_shorts_blocker();

    isinint = true;
    log("Initialization finished!" + href, 0);
    open_debugger && set_debugger();
  }

  function setSecurePolicy() {
    if (
      !unsafeWindow.isSecureContext ||
      !unsafeWindow.trustedTypes?.createPolicy
    )
      return;
    try {
      unsafeWindow.trustedTypes.createPolicy("default", {
        createScriptURL: (url) => url,
        createHTML: (html) => html,
        createScript: (script) => script,
      });
    } catch (error) {}
  }

  function trustedScriptInit() {
    try {
      let test_value;
      eval("test_eval = 1");
      return function (str) {
        return str;
      };
    } catch (error) {
      if (unsafeWindow.trustedTypes) {
        const policy = unsafeWindow.trustedTypes.createPolicy("eval", {
          createScript: (script) => {
            return script;
          },
        });
        return function (str) {
          return policy.createScript(str);
        };
      } else {
        log("trustedTypes not support", error, -1);
      }
    }
  }

  function init_hook_collection() {
    return {
      property() {
        const already_inject = [];
        let ytInitialPlayerResponse_value =
          unsafeWindow["ytInitialPlayerResponse"];
        function process_property(name, value, rule, reverse = false) {
          if (!value) return value;
          if (already_inject.includes(name)) {
            log(`${name} duplicate modification intercepted`, 0);
            return value;
          }
          const start_time = Date.now();
          if (typeof value === "object") {
            already_inject.push(name);
            open_debugger &&
              !limit_eval &&
              !eval(trustedScript(`debugger_${name}`)) &&
              eval(
                trustedScript(
                  `debugger_${name} = JSON.parse(JSON.stringify(value))`,
                ),
              );
            rule && data_process.obj_process(value, rule, reverse);
          }
          if (typeof value === "string") {
            already_inject.push(name);
            open_debugger &&
              !limit_eval &&
              !eval(trustedScript(`debugger_${name}`)) &&
              eval(trustedScript(`debugger_${name} = JSON.parse(value)`));
            value = data_process.text_process(value, rule, "insert", reverse);
          }
          log(`${name} time:`, Date.now() - start_time, "spend_time");
          return value;
        }

        define_property_hook(unsafeWindow, "ytInitialPlayerResponse", {
          get: function () {
            return ytInitialPlayerResponse_value;
          },
          set: function (value) {
            inject_info.ytInitialPlayerResponse = true;
            value = process_property(
              "ytInitialPlayerResponse",
              value,
              config_api.common_ytInitialPlayerResponse_rule,
            );
            ytInitialPlayerResponse_value = value;
          },
          configurable: false,
        });
        let ytInitialReelWatchSequenceResponse_value =
          unsafeWindow["ytInitialReelWatchSequenceResponse"];
        define_property_hook(
          unsafeWindow,
          "ytInitialReelWatchSequenceResponse",
          {
            get: function () {
              return ytInitialReelWatchSequenceResponse_value;
            },
            set: function (value) {
              inject_info.ytInitialReelWatchSequenceResponse = true;
              if (["yt_shorts", "mobile_yt_shorts"].includes(page_type)) {
                value = process_property(
                  "ytInitialReelWatchSequenceResponse",
                  value,
                  config_api.get_rules(
                    mobile_web ? "yt_shorts_mobile" : "yt_shorts",
                  ).ytInitialReelWatchSequenceResponse_rule,
                );
              }
              ytInitialReelWatchSequenceResponse_value = value;
            },
            configurable: false,
          },
        );

        let ytInitialData_value = unsafeWindow["ytInitialData"];
        define_property_hook(unsafeWindow, "ytInitialData", {
          get: function () {
            return ytInitialData_value;
          },
          set: function (value) {
            inject_info.ytInitialData = true;
            let rules = config_api.get_rules(page_type);
            ![
              "yt_watch",
              "mobile_yt_watch",
              "mobile_yt_watch_searching",
            ].includes(page_type) && (rules = rules.ytInitialData_rule);
            value = process_property("ytInitialData", value, rules);
            value = restore_sidebar_layout_on_ytInitialData(value);
            ytInitialData_value = value;
          },
          configurable: false,
        });

        const origin_ua = navigator.userAgent;
        define_property_hook(navigator, "userAgent", {
          get: function () {
            return browser_info.isMobile || browser_info.name === "Chrome"
              ? origin_ua
              : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36";
          },
        });
        if (unsafeWindow.ytcfg) {
          if (
            unsafeWindow.ytcfg.data_ &&
            typeof unsafeWindow.ytcfg.data_.LOGGED_IN === "boolean"
          ) {
            account_data_init(unsafeWindow.ytcfg.data_.LOGGED_IN);
          } else {
            if (
              unsafeWindow.ytcfg.data_ &&
              typeof unsafeWindow.ytcfg.data_ === "object"
            ) {
              define_property_hook(unsafeWindow.ytcfg.data_, "LOGGED_IN", {
                get: function () {
                  return unsafeWindow.ytcfg.data_.LOGGED_IN_;
                },
                set: function (value) {
                  unsafeWindow.ytcfg.data_.LOGGED_IN_ = value;
                  account_data_init(value);
                },
              });
            }
          }
          if (!unsafeWindow.ytcfg.data_) {
            if (unsafeWindow.yt?.config_) {
              const config_ = unsafeWindow.yt.config_;
              if (typeof config_.LOGGED_IN === "boolean") {
                account_data_init(config_.LOGGED_IN);
              }
              config_.HL && config_api.config_init(config_.HL);
            }
          } else {
            if (unsafeWindow.ytcfg.data_?.HL) {
              config_api.config_init(unsafeWindow.ytcfg.data_.HL);
            } else {
              if (unsafeWindow.ytcfg.msgs) {
                unsafeWindow.ytcfg.msgs.__lang__ &&
                  config_api.config_init(unsafeWindow.ytcfg.msgs.__lang__);
              } else {
                unsafeWindow.ytcfg._msgs = unsafeWindow.ytcfg.msgs;
                define_property_hook(unsafeWindow.ytcfg, "msgs", {
                  get: function () {
                    return this._msgs;
                  },
                  set: function (newValue) {
                    if (newValue.__lang__)
                      config_api.config_init(newValue.__lang__);
                    this._msgs = newValue;
                  },
                });
              }
            }
          }
        } else {
          define_property_hook(unsafeWindow, "ytcfg", {
            get: function () {
              return this._ytcfg;
            },
            set: function (newValue) {
              if (newValue === unsafeWindow.ytcfg) return;
              if (newValue.set) {
                const origin_set = newValue.set;
                newValue.set = function () {
                  if (arguments?.[0].YTMUSIC_INITIAL_DATA) {
                    const yt_music_init_data =
                      arguments[0].YTMUSIC_INITIAL_DATA;
                    if (yt_music_init_data?.length > 0) {
                      const browse_data = yt_music_init_data[1];
                      if (browse_data.path === "/browse") {
                        const rule =
                          config_api.get_rules("yt_music").ytInitialData_rule;
                        browse_data.data = process_property(
                          "music_initialData",
                          browse_data.data,
                          rule,
                        );
                      }
                    }
                  }
                  origin_set.apply(this, arguments);
                  if (
                    arguments[0] &&
                    typeof arguments[0].LOGGED_IN === "boolean"
                  ) {
                    account_data_init(arguments[0].LOGGED_IN);
                  }
                  if (arguments[0].HL) {
                    config_api.config_init(arguments[0].HL);
                  }
                };
              }
              this._ytcfg = newValue;
            },
          });
        }
      },
      other() {
        const origin_createElement = unsafeWindow.document.createElement;
        unsafeWindow.document.createElement = function () {
          const node = origin_createElement.apply(this, arguments);
          if (arguments[0] === "IFRAME") {
            const contentWindow_getter = Object.getOwnPropertyDescriptor(
              HTMLIFrameElement.prototype,
              "contentWindow",
            ).get;
            define_property_hook(node, "contentWindow", {
              get: function () {
                const contentWindow = contentWindow_getter.call(node);
                if (
                  !contentWindow ||
                  this.src !== "about:blank" ||
                  contentWindow.change_history
                )
                  return contentWindow;
                set_history_hook(contentWindow);
                contentWindow.fetch = fake_fetch;
                contentWindow.change_history = true;
                return contentWindow;
              },
            });
          }
          return node;
        };
        unsafeWindow.document.createElement.toString =
          origin_createElement.toString.bind(origin_createElement);
      },
            request() {
                async function deal_response(name, response, rule) {
                    if (!rule) return response;
                    let is_deal = false;
                    const responseClone = response.clone();
                    let result = await responseClone.text();
                    let origin_result = result;
                    if (name === 'subscribe' || name === 'unsubscribe') {
                        let match_list = result.match(/channelId":\"(.*?)"/);
                        const match_channel_id = match_list && match_list.length > 1 ? match_list[1] : '';
                        let channel_infos = user_data.channel_infos;
                        if (match_channel_id) {
                            if (name === 'unsubscribe') {
                                let index = channel_infos.ids.indexOf(match_channel_id);
                                if (index > -1) {
                                    channel_infos.ids.splice(index, 1);
                                    channel_infos.names.splice(index, 1);
                                }
                            } else {
                                channel_infos.ids.push(match_channel_id);
                                channel_infos.names.push('');
                            }
                            user_data.channel_infos = channel_infos;
                            user_data_api.set();
                            log(name, match_channel_id, 0);
                        }
                        is_deal = true;
                    }
                    if (name === 'playlist') {
                        let obj;
                        try {
                            obj = JSON.parse(result);
                            data_process.obj_process(obj.playerResponse, config_api.common_ytInitialPlayerResponse_rule, false);
                            data_process.obj_process(obj.response, config_api.get_rules('yt_watch', 'init'), false);
                            result = JSON.stringify(obj);
                        } catch (error) {
                            log('playlist 解析失败', error, -1);
                            result = origin_result;
                        }
                        is_deal = true;
                    }
                    if (!is_deal) {
                        let start_time = Date.now();
                        result = data_process.text_process(result, rule, 'insert', false);
                        log(name + ' 时间:', Date.now() - start_time, 'spend_time');
                    }
                    if (!result) {
                        result = origin_result;
                        debugger;
                    }
                    return new Response(result, response);
                }
                const origin_fetch = unsafeWindow.fetch;
                if (!check_native('fetch', origin_fetch)) {
                    log('fetch have been modified', -1);
                }
                fake_fetch = function () {
                    const fetch_ = async function (uri, options) {
                        async function fetch_request(response) {
                            let url = response.url;
                            inject_info.fetch = true;
                            let request_body;
                            try {
                                request_body = uri.body_ ? JSON.parse(uri.body_) : null;
                            } catch (error) {
                                request_body = null;
                            }
                            if (url.includes('youtubei/v1/next')) {
                                const rule = config_api.get_rules(mobile_web ? 'mobile_yt_watch' : 'yt_watch', request_body?.videoId ? "init" : 'next');
                                return await deal_response('next', response, rule);
                            }
                            if (url.includes('youtubei/v1/player')) {
                                return await deal_response('player', response, config_api.common_ytInitialPlayerResponse_rule);
                            }
                            if (url.includes('youtubei/v1/reel/reel_watch_sequence')) {
                                const rule = config_api.get_rules(mobile_web ? 'mobile_yt_shorts' : 'yt_shorts').ytInitialReelWatchSequenceResponse_rule;
                                return await deal_response('reel_watch_sequence', response, rule);
                            }
                            if (url.includes('youtubei/v1/reel/reel_item_watch')) {
                                // shorts 内容
                                const rule = config_api.get_rules(mobile_web ? 'mobile_yt_shorts' : 'yt_shorts').ytInitialData_rule;
                                return await deal_response('reel_item_watch', response, rule);
                            }
                            if (url.includes('youtubei/v1/browse?prettyPrint=false')) {
                                let browse_id = request_body?.browseId;
                                let rule;
                                if (href.includes('https://music.youtube.com/')) {
                                    rule = config_api.get_rules('yt_music', 'browse').ytInitialData_rule;
                                }
                                // 忽略音乐主页 影视主页
                                if (!rule && (['yt_home', 'mobile_yt_home'].includes(page_type) || browse_id === 'FEwhat_to_watch')) {
                                    if (!browse_id) {
                                        let node, category_text, node_list, node_index;
                                        if (mobile_web) {
                                            node = $('#filter-chip-bar > div > ytm-chip-cloud-chip-renderer.selected');
                                            node_list = $$('#filter-chip-bar > div > ytm-chip-cloud-chip-renderer');
                                            node_index = Array.from(node_list).indexOf(node);
                                            if (node_index !== 1) return response;
                                        } else {
                                            node = $('#chips > yt-chip-cloud-chip-renderer.style-scope.ytd-feed-filter-chip-bar-renderer.iron-selected');
                                            node_list = $$('#chips > yt-chip-cloud-chip-renderer.style-scope.ytd-feed-filter-chip-bar-renderer');
                                            node_index = Array.from(node_list).indexOf(node);
                                            if (node_index !== 0) return response;
                                        }

                                    }
                                    rule = config_api.get_rules(mobile_web ? 'mobile_yt_home' : 'yt_home', request_body?.browseId ? 'init' : 'browse').ytInitialData_rule;
                                }

                                return await deal_response('browse', response, rule);
                            }
                            if (url.startsWith('https://www.youtube.com/playlist?list=')) {
                                return await deal_response('playlist', response, []);
                            }
                            // if (url.includes('https://m.youtube.com/youtubei/v1/guide')) {
                            //     return response;
                            // }
                            if (url.includes('/youtubei/v1/search')) {
                                const rule = config_api.get_rules(mobile_web ? 'mobile_yt_search' : 'yt_search').ytInitialData_rule;
                                return await deal_response('search', response, rule);
                            }
                            if (url.includes('/unsubscribe?prettyPrint=false')) {
                                return await deal_response('unsubscribe', response, []);
                            }
                            if (url.includes('/subscribe?prettyPrint=false')) {
                                return await deal_response('subscribe', response, []);
                            }
                            if (url.includes('/v1/get_watch')) {
                                const originalBody = response.body;
                                const reader = originalBody.getReader();
                                const stream = new ReadableStream({
                                    async start(controller) {
                                        const chunks = [];
                                        try {
                                            // Read all the data first
                                            while (true) {
                                                const { done, value } = await reader.read();
                                                if (done) break;
                                                chunks.push(value);
                                            }
                                            // Merge all the chunks
                                            const allChunks = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
                                            let position = 0;
                                            for (const chunk of chunks) {
                                                allChunks.set(chunk, position);
                                                position += chunk.length;
                                            }
                                            // Convert to text
                                            let text = new TextDecoder().decode(allChunks);
                                            const save = text;
                                            try {
                                                let json = JSON.parse(text);
                                                const rules = [
                                                    "abs:[0].playerResponse.adBreakHeartbeatParams=- $exist",
                                                    "abs:[0].playerResponse.adSlots=- $exist",
                                                    "abs:[0].playerResponse.adPlacements=- $exist",
                                                    "adSlotRenderer.=- $exist",
                                                    "merchandiseShelfRenderer.=- $exist"
                                                ];
                                                const traverse_all = true;
                                                data_process.obj_process(json, rules, { traverse_all });
                                                text = JSON.stringify(json);
                                            } catch (error) {
                                                log('fetch response text error', error, -1);
                                                text = save;
                                            }
                                            // Convert the modified text back to a Uint8Array and return it.
                                            const modifiedData = new TextEncoder().encode(text);
                                            controller.enqueue(modifiedData);
                                            controller.close();
                                        } catch (error) {
                                            log('Stream error: ' + error, -1);
                                            controller.error(error);
                                        }
                                    }
                                });
                                Object.defineProperty(response, 'body', {
                                    get() {
                                        return stream;
                                    }
                                });
                            }

                            return response;
                        }
                        return origin_fetch(uri, options).then(fetch_request);
                    };
                    return fetch_;
                }();
                unsafeWindow.fetch = fake_fetch;
                unsafeWindow.fetch.toString = origin_fetch.toString.bind(origin_fetch);
                const origin_Request = unsafeWindow.Request;
                if (!check_native('Request', origin_Request)) {
                    log('Request have been modified', -1);
                }
                unsafeWindow.Request = class extends unsafeWindow.Request {
                    constructor(input, options = void 0) {
                        super(input, options);
                        this.url_ = input;
                        if (options && 'body' in options) {
                            setTimeout(async () => {
                                const ds = new DecompressionStream('gzip');
                                const stream = new Blob([options.body]).stream().pipeThrough(ds);
                                const requestBody = await new Response(stream).text();
                                this.body_ = requestBody;
                            }, 0);
                        }

                    }
                };

                unsafeWindow.XMLHttpRequest = class extends unsafeWindow.XMLHttpRequest {
                    open(method, url, ...opts) {
                        inject_info.xhr = true;
                        if (['mobile_yt_watch'].includes(page_type) && url.includes('m.youtube.com/watch?v')) {
                            log('xhr watch 返回空', 0);
                            return null;
                        }
                        if (['mobile_yt_home'].includes(page_type) && url.includes('m.youtube.com/?pbj')) {
                            log('xhr home 返回空', 0);
                            return null;
                        }
                        this.url_ = url;
                        return super.open(method, url, ...opts);
                    }
                    send(body) {
                        this.body_ = body;
                        super.send(body);
                    }
                    get xhrResponseValue() {
                        const xhr = this;
                        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
                            let result = super.response;
                            const url = xhr.responseURL;
                            const result_type = typeof result;
                            try {
                                if (url.includes('youtubei/v1/player')) {
                                    // music_watch shorts
                                    if (result_type !== 'string') {
                                        log(`XHR ${url} 返回值不是字符串!`, 0);
                                        return result;
                                    };
                                    result = data_process.text_process(result, config_api.common_ytInitialPlayerResponse_rule, 'insert', false);
                                    return result;
                                }
                                if (url.includes('youtube.com/playlist')) {
                                    debugger;
                                    let obj;
                                    obj = JSON.parse(result);
                                    log(`出现 ${url} !`, 0);
                                    data_process.obj_process(obj[2].playerResponse, ytInitialPlayerResponse_rule, false);
                                    data_process.obj_process(obj[3].response, ytInitialData_rule, false);
                                    tmp_debugger_value = obj;
                                    result = JSON.stringify(obj);
                                    return result;
                                }
                            } catch (error) {
                                log(`XHR ${url} 解析失败!`, error, -1);
                            }
                        }
                        return super.response;
                    }
                    get responseText() {
                        return this.xhrResponseValue;
                    }
                    get response() {
                        return this.xhrResponseValue;
                    }
                };

            }
    };
  }

  function on_page_change() {
    selectorCache.invalidateAll();
    observerManager.disconnectAll();

    // Cleanup duplicate song prevention when leaving YT Music
    if (!["yt_music_home", "yt_music_watch"].includes(page_type)) {
      cleanup_duplicate_song_prevention();
    }

    function common() {
      if (page_type === "yt_shorts") {
        shorts_fun.check_shorts_exist();
      }
    }

    function element_monitor() {
      const configs = wait_configs[page_type] || [];
      if (configs.length === 0) return;

      const debouncedCallback = debounce(function (mutationsList) {
        for (let i = configs.length - 1; i >= 0; i--) {
          const config = configs[i];
          const selector = config.seletor;
          const nodes = $$(selector);
          for (let node of nodes) {
            if (node.offsetHeight > 0) {
              if (config.inject) {
                if (!node.inject_xxxx) {
                  node.inject_xxxx = true;
                } else {
                  configs.splice(i, 1);
                  break;
                }
              }
              if ("count" in config) {
                if (config.count > 0) {
                  config.count--;
                  if (config.count === 0) {
                    configs.splice(i, 1);
                  }
                }
              }
              const funs = Array.isArray(config.fun)
                ? config.fun
                : [config.fun];
              for (let fun of funs) {
                fun(node);
              }
              break;
            }
          }
        }
        if (configs.length === 0) {
          log("monitor end", 0);
          observerManager.removeObserver("element_monitor");
          return;
        }
      }, 100);

      const observer = new MutationObserver(debouncedCallback);
      observer.observe($("body"), {
        childList: true,
        subtree: true,
      });
      observerManager.addObserver("element_monitor", observer);
    }

    const wait_configs = {
      yt_shorts: [
        {
          seletor: "ytd-reel-video-renderer[is-active] video",
          inject: true,
          fun: [
            shorts_auto_scroll,
            set_shorts_dbclick_like,
            set_shorts_progress,
          ],
        },
        {
          seletor: "ytd-reel-video-renderer[is-active] #comments-button",
          inject: true,
          fun: [shorts_change_comment_click],
        },
        {
          seletor: "ytd-reel-video-renderer[is-active] video",
          count: 30,
          fun: [],
        },
      ],
      mobile_yt_shorts: [
        {
          seletor:
            'div.carousel-item[aria-hidden="false"] ytm-like-button-renderer',
          count: 10,
          fun: [
            shorts_auto_scroll,
            set_shorts_dbclick_like,
            set_shorts_progress,
          ],
        },
      ],

      yt_home: [
        {
          seletor:
            "#contents ytd-rich-section-renderer yt-shelf-header-layout h2.yt-shelf-header-layout__title span.yt-core-attributed-string",
          inject: true,
          fun: hide_explore_more_topics_section,
        },
      ],

      yt_watch: [
        {
          seletor:
            "#contents ytd-rich-section-renderer yt-shelf-header-layout h2.yt-shelf-header-layout__title span.yt-core-attributed-string",
          inject: true,
          fun: hide_explore_more_topics_section,
        },
        {
          seletor: "#teaser-carousel",
          inject: true,
          fun: hide_teaser_carousel,
        },
      ],
    };

    common();
    hide_create_button();
    element_monitor();

    init_sponsorblock();
    init_duplicate_song_prevention();
    init_quality_preset();
    init_speed_preset();

    apply_hide_buttons_css();
    init_restore_related_sidebar_layout();

    hide_shorts_sections_if_disabled();

    function set_dbclick(node, handler) {
      if (node.inject_dbclick) return;
      node.inject_dbclick = true;
      let corgin_onclick = node.onclick;
      let timers = [];
      node.onclick = node.onclick_ = function (event) {
        if (
          node.dbclick_intercept_propagation ||
          node.click_intercept_propagation
        ) {
          event.stopPropagation();
        }
        const timer = setTimeout(() => {
          if (
            node.dbclick_intercept_propagation &&
            !node.click_intercept_propagation
          ) {
            let parent = node.parentElement;
            if (parent) {
              let parentHandler = parent.onclick;
              if (typeof parentHandler === "function") {
                parentHandler.call(parent, event);
              }
              parent.dispatchEvent(event);
            }
          }
          timers.splice(timers.indexOf(timer), 1);
          corgin_onclick?.call(this, event);
        }, 300);
        timers.push(timer);
      };
      define_property_hook(node, "onclick", {
        get: function () {
          return this.onclick_;
        },
        set: function (fun) {
          corgin_onclick = fun;
        },
      });
      node.addEventListener("dblclick", function (event) {
        if (node.dbclick_intercept_propagation) event.stopPropagation();
        for (let timer of timers) {
          clearInterval(timer);
        }
        timers.length = 0;
        handler?.call(this, event);
      });
    }
    function set_shorts_dbclick_like(video_node) {
      video_node =
        page_type === "yt_shorts"
          ? video_node
          : $('div.carousel-item[aria-hidden="false"] div.video-wrapper');
      if (!video_node) return;
      video_node.dbclick_intercept_propagation = true;
      set_dbclick(video_node, function () {
        if (user_data.shorts_dbclick_like === "off") return;
        const like_seltor =
          page_type === "yt_shorts"
            ? "ytd-reel-video-renderer[is-active] #like-button > yt-button-shape > label > button"
            : 'div.carousel-item[aria-hidden="false"] ytm-like-button-renderer button';
        $(like_seltor)?.click();
      });
    }
    function set_shorts_progress(node) {
      const video_node = page_type === "yt_shorts" ? node : $("video");
      if (!video_node || video_node.inject_shorts_progress) return;
      video_node.inject_shorts_progress = true;
      observerManager.addListener(video_node, "timeupdate", function () {
        if (user_data.shorts_add_video_progress === "off") return;
        const shape_button =
          page_type === "yt_shorts"
            ? $("ytd-reel-video-renderer[is-active] #button-shape > button")
            : $(
                'div.carousel-item[aria-hidden="false"] ytm-bottom-sheet-renderer button',
              );
        if (!shape_button) return;
        const progress = (video_node.currentTime / video_node.duration) * 100;
        const transparency = page_type === "yt_shorts" ? "0.05" : "0.3";
        const progress_color =
          page_type === "yt_shorts"
            ? "rgba(0, 0, 255, 0.4)"
            : "rgba(255, 255, 0, 0.4)";
        shape_button.style.background = `linear-gradient(to top, ${progress_color} ${progress}%, rgba(0, 0, 0, ${transparency}) ${progress}%)`;
      });
    }
    function shorts_change_comment_click(comments_node) {
      const comments_button = comments_node.querySelector(
        "ytd-button-renderer > yt-button-shape > label > button",
      );
      const onclick_setter = Object.getOwnPropertyDescriptor(
        HTMLElement.prototype,
        "onclick",
      ).set;
      const current_render_node = $("ytd-reel-video-renderer[is-active]");
      const wrap = function (fun) {
        return function (event) {
          const expand_node = current_render_node.querySelector(
            "#watch-while-engagement-panel > ytd-engagement-panel-section-list-renderer:nth-child(1)",
          );
          if (
            expand_node?.visibility === "ENGAGEMENT_PANEL_VISIBILITY_EXPANDED"
          ) {
            const expand_close_node = current_render_node.querySelector(
              "#visibility-button > ytd-button-renderer > yt-button-shape > button",
            );
            expand_close_node?.click();
          } else {
            fun.call(this, event);
          }
        };
      };
      comments_button.onclick = comments_button.onclick_ = wrap(
        comments_button.onclick,
      );
      define_property_hook(comments_button, "onclick", {
        get: function () {
          return this.onclick_;
        },
        set: function (fun) {
          this.onclick_ = wrap(fun);
          onclick_setter.call(comments_button, this.onclick_);
        },
      });
    }
    function shorts_auto_scroll(video_node) {
      video_node = page_type === "yt_shorts" ? video_node : $("video");
      if (!video_node) return;
      if (video_node?.inject_auto_scroll) return;
      video_node.inject_auto_scroll = true;
      video_node.loop = false;
      define_property_hook(video_node, "loop", {
        get: function () {
          return false;
        },
      });
      observerManager.addListener(video_node, "ended", function () {
        if (user_data.shorts_auto_scroll === "on") {
          if (page_type === "yt_shorts") {
            $(
              "#navigation-button-down > ytd-button-renderer > yt-button-shape > button",
            ).click();
          } else {
            simulate_swipeup(this, 500, 100);
          }
          return;
        }
        if (user_data.shorts_disable_loop_play === "on") {
          return;
        }
        this.play();
      });
    }
  }

  function get_user_data_listener() {
    return {
      cur_channel_id: null,
      listener_id: null,
      set: function () {
        if (channel_id === this.cur_channel_id) {
          return;
        }
        !this.cur_channel_id && GM_removeValueChangeListener(this.listener_id);
        this.cur_channel_id = channel_id;
        this.listener_id = GM_addValueChangeListener(
          channel_id,
          (name, oldValue, newValue, remote) => {
            if (!remote || this.cur_channel_id !== name) return;
            newValue.language = "en";
            user_data = newValue;
            config_api.config_init();
            const popup_node =
              unsafeWindow.document.getElementById("xxx_popup");
            popup_node && display_config_win();
          },
        );
      },
    };
  }

  async function account_data_init(login) {
    if (is_account_init) return;
    is_account_init = true;
    if (login) {
      yt_api.get_channel_id();
      yt_api.get_subscribe_data();
    } else if (channel_id !== "default") {
      channel_id = "default";
      user_data.login = false;
      user_data = user_data_api.get();
    }
  }

  function native_method_hook(method_path, handler) {
    try {
      let [last_path, last_key] =
        data_process.get_lastPath_and_key(method_path);
      let last_obj = data_process.string_to_value(
        unsafeWindow,
        "unsafeWindow." + last_path,
      );
      let dec_obj = last_obj[last_key];
      last_obj[last_key + "__"] = dec_obj;
      if (typeof dec_obj !== "function") {
        log(method_path, "have been modified", -1);
        return;
      }
      const method_name = dec_obj.name;
      if (
        dec_obj.toString() !==
        "function " + method_name + "() { [native code] }"
      ) {
        log(method_path, "have been modified!", -1);
      }
      last_obj[last_key] = handler;
    } catch (error) {
      log(method_path, "hook failed!", error, -1);
    }
  }

  function define_property_hook(obj, property, descriptor) {
    const old_descriptor = Object.getOwnPropertyDescriptor(obj, property);
    if (old_descriptor?.configurable === false) {
      debugger;
      log(property, "is not configurable, hook error!", old_descriptor, -1);
      return;
    }
    try {
      Object.defineProperty(obj, property, descriptor);
    } catch (error) {
      log(property, "hook failed!", error, -1);
    }
  }

  function get_config_api() {
    return {
      flag_infos: {
        en: {
          sponsored: "Sponsored Ads",
          free_movie: "Free (with ads)",
          live: "LIVE",
          movie_channel: "Movies & TV",
          Playables: "Playables",
          short_buy_super_thanks: "Buy Super Thanks",
          think_video:
            "What did you think of this video? | How is this recommended content?",
          try: "Try",
          recommend_popular: "Trending",
          featured: "Featured",
          category_live: "Live",
          category_game: "Gaming",
          category_news: "News",
          btn_recommend_movie: "Movie Recommendations",
          btn_recommend_shorts: "Shorts Recommendations",
          btn_recommend_liveroom: "Live Recommendations",
          btn_recommend_popular: "Trending",
          btn_recommend_game: "Playables Recommendations",
          btn_save: "Save",
          goodselect: "Featured",
          music_ad_flag: "ad-free",
          upcoming: "UPCOMING",
          init: "Initialize",
          ctoc: "Copied to clipboard",
          runing_normally: "running normally",
          err_msg: "error message",
          success: "Success",
          failed: "Failed",
          tips: "You can send an error message or screenshot to the developer",
          exists_error:
            "Error message exists (It is recommended to refresh multiple times to see if it is the same error message)",
          inject: "Inject",
          btn_lable_open: "On",
          btn_lable_close: "Off",
          btn_lable_subscribed: "Only subscribed",
          recommend_subscribed_lable_tips:
            "Only show subscribed recommendations",
          title_add_shorts_upload_date: "Add Shorts upload time",
          title_shorts_change_author_name:
            "Change Shorts username to channel name",
          config_info: "Config info",
          page_info: "Page info",
          rule_info: "Rule info",
          del_config_confirm_tips:
            "Are you sure you want to delete all configuration settings?",
          btn_shorts_auto_scroll_title: "AutoScroll",
          bt_shorts_disable_loop_play_title: "DisableLoopPlay",
          btn_shorts_dbclick_like_title: "DoubleClickLikeVideo",
          btn_shorts_add_video_progress_title: "AddVideoProgress",
          shorts_recommend_split_tag: "ShortsConfig",
          btn_sponsorblock_title: "SponsorBlock skip sponsors",
          btn_sponsorblock_tips:
            "Automatically skip sponsor segments using SponsorBlock API",
          btn_duplicate_song_prevention_title: "Prevent Duplicate Songs",
          btn_duplicate_song_prevention_tips:
            "Prevent the same song or artist from playing consecutively",
        },
      },

      common_ytInitialPlayerResponse_rule: [
        "abs:playerAds=- $exist",
        "abs:adSlots=- $exist",
        "abs:adPlacements=- $exist",
        'abs:auxiliaryUi.messageRenderers.bkaEnforcementMessageViewModel.isVisible=json("true") $exist',
        "abs:adBreakHeartbeatParams=- $exist",
        "abs:messages[*]=- /.mealbarPromoRenderer$exist",
      ],
      default_language: "en",
      config_init: function (tmp_language = null) {
        // Always use English
        user_data.language = "en";
        user_data_api.set();
        flag_info = this.flag_infos["en"];
        movie_channel_info = {
          guideEntryRenderer: {
            navigationEndpoint: {
              clickTrackingParams: "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
              commandMetadata: {
                webCommandMetadata: {
                  url: "/feed/storefront",
                  webPageType: "WEB_PAGE_TYPE_BROWSE",
                  rootVe: 6827,
                  apiUrl: "/youtubei/v1/browse",
                },
              },
              browseEndpoint: {
                browseId: "FEstorefront",
              },
            },
            icon: {
              iconType: "CLAPPERBOARD",
            },
            trackingParams: "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
            formattedTitle: {
              simpleText: flag_info.movie_channel,
            },
            accessibility: {
              accessibilityData: {
                label: flag_info.movie_channel,
              },
            },
          },
        };
        data_process.storage_obj("movie_channel_info", movie_channel_info);
        mobile_movie_channel_info = {
          navigationItemViewModel: {
            text: {
              content: flag_info.movie_channel,
            },
            icon: {
              sources: [
                {
                  clientResource: {
                    imageName: "CLAPPERBOARD",
                  },
                },
              ],
            },
            onTap: {
              parallelCommand: {
                commands: [
                  {
                    innertubeCommand: {
                      clickTrackingParams:
                        "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
                      hideMoreDrawerCommand: {},
                    },
                  },
                  {
                    innertubeCommand: {
                      clickTrackingParams:
                        "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
                      commandMetadata: {
                        webCommandMetadata: {
                          url: "/feed/storefront",
                          webPageType: "WEB_PAGE_TYPE_CHANNEL",
                          rootVe: 3611,
                          apiUrl: "/youtubei/v1/browse",
                        },
                      },
                      browseEndpoint: {
                        browseId: "FEstorefront",
                      },
                    },
                  },
                ],
              },
            },
            loggingDirectives: {
              trackingParams: "CBQQnOQDGAIiEwj5l8SLqPiCAxUXSEwIHbf1Dw0=",
              visibility: {
                types: "12",
              },
              enableDisplayloggerExperiment: true,
            },
          },
        };
        data_process.storage_obj(
          "mobile_movie_channel_info",
          mobile_movie_channel_info,
        );
        ytInitialData_rule = null;
        ytInitialReelWatchSequenceResponse_rule = null;
        ytInitialPlayerResponse_rule = null;
        mobile_web = page_type.startsWith("mobile");
      },
      get_rules: function (page_type_, type) {
        page_type_ = page_type_ || page_type;
        if (page_type_ === "mobile_yt_watch_searching")
          page_type_ = "mobile_yt_watch";
        else if (page_type_ === "mobile_yt_home_searching")
          page_type_ = "mobile_yt_home";
        else if (page_type_ === "yt_music_channel") page_type_ = "yt_watch";

        let tmp_ytInitialData_rule = null;
        let tmp_ytInitialReelWatchSequenceResponse_rule = null;
        let tmp_ytInitialPlayerResponse_rule = null;
        const common_ytInitialData_rule = ["adSlotRenderer.=-"];
        const return_obj = {
          ytInitialData_rule: null,
          ytInitialReelWatchSequenceResponse_rule: null,
          ytInitialPlayerResponse_rule: null,
          reverse: false,
        };
        if (page_type_ === "yt_search") {
          tmp_ytInitialData_rule = [
            ...common_ytInitialData_rule,
            "abs:contents[*][*].videoRenderer=- /.isAd",
            "abs:contents[*][*].gridVideoRenderer=- /.isAd",
            "abs:contents[*][*].videoWithContextRenderer=- /.isAd",
            "abs:results[*].videoRenderer=- /.isAd",
            "abs:results[*].gridVideoRenderer=- /.isAd",
          ];
          return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
          return return_obj;
        }

        if (page_type_ === "yt_music") {
          return_obj.ytInitialData_rule = [
            "abs:overlay.mealbarPromoRenderer=- $exist",
          ];
          return return_obj;
        }

        if (page_type_ === "mobile_yt_search") {
          tmp_ytInitialData_rule = [
            ...common_ytInitialData_rule,
            "abs:contents[*][*].videoRenderer=- /.isAd",
            "abs:contents[*][*].gridVideoRenderer=- /.isAd",
            "abs:contents[*][*].videoWithContextRenderer=- /.isAd",
            "abs:results[*].videoRenderer=- /.isAd",
            "abs:results[*].gridVideoRenderer=- /.isAd",
          ];
          return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
          return return_obj;
        }

        if (page_type_ === "yt_kids_watch") {
          tmp_ytInitialData_rule = common_ytInitialData_rule;
          return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
          return return_obj;
        }

        if (page_type_ === "yt_music_watch") {
          tmp_ytInitialData_rule = common_ytInitialData_rule;
          return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
          return return_obj;
        }

        if (page_type_.includes("yt_shorts")) {
          const tmp_ytInitialData_rule__ = [];
          if (
            user_data.add_shorts_upload_date === "on" ||
            user_data.shorts_change_author_name === "on"
          ) {
            let dec_path =
              "overlay.reelPlayerOverlayRenderer.reelPlayerHeaderSupportedRenderers.reelPlayerHeaderRenderer.channelTitleText.runs[0].text";
            let name_base_path =
              "json_obj.engagementPanels[1].engagementPanelSectionListRenderer.content.structuredDescriptionContentRenderer.items[0].videoDescriptionHeaderRenderer.channel.";
            let time_tag_path;
            let name_tag_path;
            if (mobile_web) {
              user_data.add_shorts_upload_date === "on" &&
                (time_tag_path = "....timestampText.runs[0].text");
              user_data.shorts_change_author_name === "on" &&
                (name_tag_path = name_base_path + "runs[0].text");
            } else {
              user_data.add_shorts_upload_date === "on" &&
                (time_tag_path = "....timestampText.simpleText");
              user_data.shorts_change_author_name === "on" &&
                (name_tag_path = name_base_path + "simpleText");
            }
            let rule = `abs:${dec_path}={absObj(${
              name_tag_path ? name_tag_path : "json_obj." + dec_path
            })\}${time_tag_path ? "\n{pathObj(" + time_tag_path + ")}" : ""}`;
            tmp_ytInitialData_rule__.push(rule);
          }

          if (user_data.short_buy_super_thanks === "off") {
            !mobile_web &&
              tmp_ytInitialData_rule__.push(
                "abs:overlay.reelPlayerOverlayRenderer.suggestedAction=- $exist",
              );
          }
          tmp_ytInitialReelWatchSequenceResponse_rule = [
            "abs:entries[*]=- /.command.reelWatchEndpoint.adClientParams$exist",
          ];
          tmp_ytInitialData_rule__.length &&
            (tmp_ytInitialData_rule = tmp_ytInitialData_rule__);
          return_obj.ytInitialReelWatchSequenceResponse_rule =
            tmp_ytInitialReelWatchSequenceResponse_rule;
          return_obj.ytInitialData_rule = tmp_ytInitialData_rule;
          return return_obj;
        }

        if (page_type_.includes("yt_watch")) {
          return function (json_obj) {
            if (json_obj.continuation) return [];
            let video_item_base_path;
            let video_sub_path;
            let section_sub_path;
            let player_bottom_path;
            let player_bottom_section_path;
            type = type || "init";
            if (type === "next") {
              if (
                json_obj.onResponseReceivedEndpoints?.[0]
                  ?.appendContinuationItemsAction?.continuationItems?.length
              ) {
                let target_id =
                  json_obj.onResponseReceivedEndpoints[0]
                    .appendContinuationItemsAction.targetId;
                if (target_id.startsWith("comment-replies")) return [];
                video_item_base_path =
                  "abs:onResponseReceivedEndpoints[0].appendContinuationItemsAction.continuationItems[*]";
                video_sub_path = "/.videoWithContextRenderer";
                section_sub_path = "/.reelShelfRenderer";
              }
            } else if (type === "init") {
              if (mobile_web) {
                if (
                  json_obj.contents?.singleColumnWatchNextResults?.results
                    ?.results?.contents?.length
                ) {
                  let length =
                    json_obj.contents.singleColumnWatchNextResults.results
                      .results.contents.length;
                  video_item_base_path = `abs:contents.singleColumnWatchNextResults.results.results.contents[${
                    length - 1
                  }].itemSectionRenderer.contents[*]`;
                  length > 1 &&
                    (player_bottom_path = `abs:contents.singleColumnWatchNextResults.results.results.contents[0-${
                      length - 2
                    }]`);
                  cur_watch_channle_id =
                    json_obj.contents.singleColumnWatchNextResults.results
                      .results.contents?.[1]?.slimVideoMetadataSectionRenderer
                      ?.contents?.[1]?.slimOwnerRenderer?.title.runs[0]
                      .navigationEndpoint.browseEndpoint.browseId;
                  player_bottom_section_path =
                    "/.itemSectionRenderer.contents[0].reelShelfRenderer";
                  video_sub_path = "/.videoWithContextRenderer";
                  section_sub_path = "/.reelShelfRenderer";
                }
              } else {
                let is_next_target_id;
                if (
                  json_obj.contents?.twoColumnWatchNextResults?.secondaryResults
                    ?.secondaryResults?.results?.[1]?.itemSectionRenderer
                    ?.contents?.length
                ) {
                  video_item_base_path =
                    "abs:contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[1].itemSectionRenderer.contents[*]";
                  player_bottom_path =
                    "abs:contents.twoColumnWatchNextResults.results.results.contents[*]";
                  is_next_target_id =
                    json_obj.contents.twoColumnWatchNextResults.secondaryResults
                      .secondaryResults.results[1].itemSectionRenderer
                      .targetId === "watch-next-feed";
                  cur_watch_channle_id =
                    json_obj.contents.twoColumnWatchNextResults.results.results
                      .contents?.[1]?.videoSecondaryInfoRenderer?.owner
                      ?.videoOwnerRenderer?.title.runs[0].navigationEndpoint
                      .browseEndpoint.browseId;
                  player_bottom_section_path =
                    "/.itemSectionRenderer.contents[0]";
                  video_sub_path = "/.compactVideoRenderer";
                  section_sub_path = "/.reelShelfRenderer";
                }
                if (
                  !is_next_target_id &&
                  json_obj.contents?.twoColumnWatchNextResults?.secondaryResults
                    ?.secondaryResults?.results?.[0]?.richGridRenderer?.contents
                    ?.length
                ) {
                  video_item_base_path =
                    "abs:contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[0].richGridRenderer.contents[*]";
                  player_bottom_path =
                    "abs:contents.twoColumnWatchNextResults.results.results.contents[*]";
                  is_next_target_id =
                    json_obj.contents.twoColumnWatchNextResults.secondaryResults
                      .secondaryResults.results[0].richGridRenderer.targetId ===
                    "watch-next-feed";
                  cur_watch_channle_id =
                    json_obj.contents.twoColumnWatchNextResults.results.results
                      .contents?.[1]?.videoSecondaryInfoRenderer?.owner
                      ?.videoOwnerRenderer?.title.runs[0].navigationEndpoint
                      .browseEndpoint.browseId;
                  player_bottom_section_path =
                    "/.itemSectionRenderer.contents[0]";
                  video_sub_path = "/.richItemRenderer.content.videoRenderer";
                  section_sub_path =
                    "/.richSectionRenderer.content.richShelfRenderer";
                }
                if (
                  !is_next_target_id &&
                  json_obj.contents?.twoColumnWatchNextResults?.secondaryResults
                    ?.secondaryResults?.results?.length
                ) {
                  video_item_base_path =
                    "abs:contents.twoColumnWatchNextResults.secondaryResults.secondaryResults.results[*]";
                  player_bottom_path =
                    "abs:contents.twoColumnWatchNextResults.results.results.contents[*]";
                  cur_watch_channle_id =
                    json_obj.contents.twoColumnWatchNextResults.results.results
                      .contents?.[1]?.videoSecondaryInfoRenderer?.owner
                      ?.videoOwnerRenderer?.title.runs[0].navigationEndpoint
                      .browseEndpoint.browseId;
                  player_bottom_section_path =
                    "/.itemSectionRenderer.contents[0]";
                  video_sub_path = "/.compactVideoRenderer";
                  section_sub_path = "/.reelShelfRenderer";
                }
              }
            }
            if (!video_item_base_path) return [];

            const rules = [];
            let video_item_rules = [];
            let section_item_rules = [];
            let player_bottom_rules = [];

            mobile_web &&
              type === "init" &&
              player_bottom_rules.push(
                `${player_bottom_section_path.replace(
                  /\.[^\.]+$/,
                  "",
                )}.adSlotRenderer$exist`,
              );
            !mobile_web && type === "init" && player_bottom_rules.push(`/.merchandiseShelfRenderer$exist`);
            video_item_rules.push(
              `${video_sub_path.replace(/\.[^\.]+$/, ".adSlotRenderer$exist")}`,
            );

            if (
              user_data.open_recommend_movie === "off" &&
              cur_watch_channle_id !== "UClgRkhTL3_hImCAmdLfDE4g"
            ) {
              if (mobile_web) {
                video_item_rules.push(
                  `${video_sub_path}.badges[0].metadataBadgeRenderer.style=BADGE_STYLE_TYPE_YPC`,
                );
              } else {
                video_item_rules.push(
                  `${video_sub_path.replace(
                    /\.[^\.]+$/,
                    ".compactMovieRenderer",
                  )}$exist`,
                );
              }
            }

            if (
              ["off", "subscribed"].includes(user_data.open_recommend_liveroom)
            ) {
              if (mobile_web)
                video_item_rules.push(
                  `${video_sub_path}.thumbnailOverlays[0].thumbnailOverlayTimeStatusRenderer.style=LIVE|UPCOMING`,
                );
              else
                video_item_rules.push(
                  `${video_sub_path}.badges[0].metadataBadgeRenderer.style=BADGE_STYLE_TYPE_LIVE_NOW`,
                );
            }

            if (
              user_data.open_recommend_shorts === "subscribed" &&
              type === "init" &&
              page_type !== "mobile_yt_watch"
            ) {
              rules.push(
                `${video_item_base_path.replace(
                  "[*]",
                  "",
                )}=+(arr_insert,method(shorts_fun.get_shorts_section()),0) @user_data.shorts_list.length$value>0`,
              );
            }

            if (
              ["off", "subscribed"].includes(user_data.open_recommend_shorts)
            ) {
              section_item_rules.push(
                `${section_sub_path}.icon.iconType=YOUTUBE_SHORTS_BRAND_24`,
              );
              mobile_web &&
                type === "init" &&
                player_bottom_rules.push(
                  `${player_bottom_section_path}.icon.iconType=YOUTUBE_SHORTS_BRAND_24`,
                );
            }

            player_bottom_rules.length &&
              rules.push(
                `${player_bottom_path}=- ${player_bottom_rules.join(
                  data_process.condition_split_or_tag,
                )}`,
              );
            section_item_rules.length &&
              video_item_rules.push(...section_item_rules);
            video_item_rules.length &&
              rules.push(
                `${video_item_base_path}=- ${video_item_rules.join(
                  data_process.condition_split_or_tag,
                )}`,
              );
            return rules;
          };
        }

        if (page_type_.includes("yt_home")) {
          let item_path;
          let item_rules = [];
          let rules = [];
          type = type || "init";
          if (type === "browse") {
            item_path =
              "abs:onResponseReceivedActions[0].appendContinuationItemsAction.continuationItems[*]";
          } else if (type === "init") {
            item_path = `abs:contents.${
              mobile_web
                ? "singleColumnBrowseResultsRenderer"
                : "twoColumnBrowseResultsRenderer"
            }.tabs[0].tabRenderer.content.richGridRenderer.contents[*]`;
          } else {
            return {};
          }
          const video_path = `/.richItemRenderer.content.${
            mobile_web ? "videoWithContextRenderer" : "videoRenderer"
          }`;
          const section_path = `/.richSectionRenderer.content.${
            mobile_web ? "reelShelfRenderer" : "richShelfRenderer"
          }`;

          item_rules.push("/.richItemRenderer.content.adSlotRenderer$exist");

          !mobile_web &&
            type === "init" &&
            rules.push(
              "abs:contents.twoColumnBrowseResultsRenderer.tabs[0].tabRenderer.content.richGridRenderer.masthead=- $exist",
            );

          if (["off", "subscribed"].includes(user_data.open_recommend_shorts)) {
            item_rules.push(
              `${section_path}.icon.iconType=YOUTUBE_SHORTS_BRAND_24`,
            );
          }

          if (user_data.open_recommend_popular === "off") {
            item_rules.push(
              `${section_path}.endpoint.browseEndpoint.browseId=FEtrending`,
            );
          }

          if (user_data.open_recommend_playables === "off") {
            item_rules.push(
              "/.richSectionRenderer.content.richShelfRenderer.endpoint.browseEndpoint.browseId=FEmini_app_destination",
            );
          }

          if (
            user_data.open_recommend_shorts === "subscribed" &&
            type === "init"
          ) {
            rules.push(
              item_path.replace("[*]", "") +
                "=+(arr_insert,method(shorts_fun.get_shorts_section()),0) @user_data.shorts_list.length$value>0",
            );
          }

          if (
            ["off", "subscribed"].includes(user_data.open_recommend_liveroom)
          ) {
            !mobile_web &&
              item_rules.push(
                `${video_path}.badges[0].metadataBadgeRenderer.style=BADGE_STYLE_TYPE_LIVE_NOW`,
              );
            const tag_express = `UPCOMING${
              mobile_web ? data_process.value_split_or_tag + "LIVE" : ""
            }`;
            item_rules.push(
              `${video_path}.thumbnailOverlays[-1].thumbnailOverlayTimeStatusRenderer.style=${tag_express}`,
            );
          }

          if (user_data.open_recommend_movie === "off") {
            item_rules.push(
              `${section_path}.endpoint.browseEndpoint.browseId=FEstorefront|UClgRkhTL3_hImCAmdLfDE4g`,
            );
            item_rules.push(
              `${video_path}.badges[0].metadataBadgeRenderer.style=BADGE_STYLE_TYPE_YPC`,
            );
          }

          item_rules.push(
            "/.richSectionRenderer.content.statementBannerRenderer$exist",
          );

          rules.push("abs:survey=- $exist");

          item_rules.push(
            section_path.replace(/\.[^\.]+$/, ".inlineSurveyRenderer$exist"),
          );

          item_rules.push(
            section_path.replace(/\.[^\.]+$/, ".primetimePromoRenderer$exist"),
          );

          const add_movie_channel_rule =
            "loadingStrategy.inlineContent.moreDrawerViewModel.content=+sobj(" +
            (mobile_web ? "mobile_" : "") +
            "movie_channel_info) !~=" +
            flag_info.movie_channel;
          rules.push(add_movie_channel_rule);

          rules.push(
            `${item_path}=- ${item_rules.join(
              data_process.condition_split_or_tag,
            )}`,
          );
          return_obj.ytInitialData_rule = rules;
          return return_obj;
        }
        return return_obj;
      },
    };
  }

  function set_search_listen() {
    let count = 0;
    const interval_id = setInterval(() => {
      if (
        ![
          "yt_watch",
          "yt_home",
          "yt_search",
          "mobile_yt_search",
          "mobile_yt_home_searching",
          "mobile_yt_watch_searching",
          "yt_shorts",
          "yt_music_home",
          "yt_music_watch",
          "yt_watch_playlist",
          "other",
        ].includes(page_type)
      ) {
        clearInterval(interval_id);
        return;
      }
      count++;
      const search_selector = href.includes("https://m.youtube.com/")
        ? "input.searchbox-input.title"
        : href.includes("https://music.youtube.com/")
          ? "input.ytmusic-search-box"
          : "input.yt-searchbox-input";
      const search_input_node = $(search_selector);
      if (search_input_node) {
        clearInterval(interval_id);
        if (search_input_node.set_listener) return;

        search_input_node.set_listener = true;
        const oninput = function (event) {
          if (
            [
              display_error_keyword,
              open_config_keyword,
              reset_config_keyword,
              custom_panel_keyword,
            ].includes(this.value)
          ) {
            setTimeout(() => {
              search_input_node.blur();

              const closeSearchPanel = () => {
                const escapeEvent = new KeyboardEvent("keydown", {
                  key: "Escape",
                  code: "Escape",
                  keyCode: 27,
                  which: 27,
                  bubbles: true,
                  cancelable: true,
                });
                search_input_node.dispatchEvent(escapeEvent);
              };

              if (search_input_node.value === open_config_keyword) {
                search_input_node.value = "";
                search_input_node.dispatchEvent(
                  new Event("input", { bubbles: true }),
                );
                closeSearchPanel();
                display_config_win();
              }
              if (search_input_node.value === reset_config_keyword) {
                search_input_node.value = "";
                search_input_node.dispatchEvent(
                  new Event("input", { bubbles: true }),
                );
                closeSearchPanel();
                user_data_api.reset();
                return;
              }
              if (search_input_node.value === display_error_keyword) {
                search_input_node.value = "";
                search_input_node.dispatchEvent(
                  new Event("input", { bubbles: true }),
                );
                closeSearchPanel();
                let tips = `script ${flag_info.init} ${
                  isinint ? flag_info.success : flag_info.failed
                }`;
                if (error_messages.length === 0 && isinint)
                  tips += " " + flag_info.runing_normally;
                for (let key of Object.keys(inject_info)) {
                  if (!mobile_web && key === "ytInitialPlayerResponse")
                    continue;
                  if (
                    key === "ytInitialReelWatchSequenceResponse" &&
                    !["yt_shorts", "mobile_yt_shorts"].includes(page_type)
                  )
                    continue;
                  tips += `\n${key} ${flag_info.inject} ${
                    inject_info[key] ? flag_info.success : flag_info.failed
                  }`;
                }

                const tmp_user_data = JSON.parse(JSON.stringify(user_data));
                delete tmp_user_data.shorts_list;
                delete tmp_user_data.channel_infos;
                tips += `\n\n${flag_info.config_info}\n${JSON.stringify(
                  tmp_user_data,
                  null,
                  2,
                )}\n\n${
                  flag_info.page_info
                }\npage_type: ${page_type}\nhref: ${href}`;
                tips += `\n\nbrowser_info\n${JSON.stringify(
                  browser_info,
                  null,
                  2,
                )}`;
                const str_channel_id = "" + channel_id;
                tips += `\n\naccount_info\nchannel_id: ${
                  str_channel_id === "default" || str_channel_id.length <= 10
                    ? str_channel_id
                    : str_channel_id.slice(0, 5) +
                      "..." +
                      str_channel_id.slice(-5)
                }`;
                if (error_messages.length !== 0) {
                  tips += `\n\n${flag_info.exists_error}\n-----------${
                    flag_info.err_msg
                  }(${flag_info.ctoc})-----------------\n${error_messages.join(
                    "\n",
                  )}\n\n${flag_info.tips}`;
                }
                display_error_win(tips);
              }
              if (search_input_node.value === custom_panel_keyword) {
                search_input_node.value = "";
                search_input_node.dispatchEvent(
                  new Event("input", { bubbles: true }),
                );
                closeSearchPanel();
                display_hide_buttons_win();
              }
            }, 500);
          }
        };
        search_input_node.addEventListener("input", oninput);
      } else if (count > 50) {
        clearInterval(interval_id);
        log("Search box not found", -1);
      }
    }, 200);
  }

  function hide_create_button() {
    const labels = [
      "Create",
      "Create ",
      "Create a Short",
      "Create video",
      "Create post",
    ];

    const selectorParts = labels.map(
      (l) => `ytd-button-renderer.ytd-masthead button[aria-label="${l}"]`,
    );
    const selector = selectorParts.join(",");

    $$(selector).forEach((btn) => {
      const renderer = btn.closest("ytd-button-renderer.ytd-masthead") || btn;
      renderer.style.display = "none";
    });
  }

  function hide_explore_more_topics_section() {
    const sections = $$("#contents ytd-rich-section-renderer");
    if (!sections.length) return;

    for (const section of sections) {
      let titleSpan =
        section.querySelector(
          "yt-shelf-header-layout h2.yt-shelf-header-layout__title span.yt-core-attributed-string",
        ) ||
        section.querySelector(
          ".yt-shelf-header-layout__title span.yt-core-attributed-string",
        ) ||
        section.querySelector("h2 span.yt-core-attributed-string");

      const text = titleSpan?.textContent?.trim();
      if (!text) continue;

      const isExploreMoreTopics =
        text === "Explore more topics" ||
        text.toLowerCase().includes("explore more");

      if (isExploreMoreTopics) {
        section.style.display = "none";
        log('Hidden "Explore more topics" section', 0);
      }
    }
  }

  function hide_shorts_sections_if_disabled() {
    const shortsHidden =
      user_data.global_shorts_block === "on" ||
      user_data.open_recommend_shorts === "off";

    if (!shortsHidden) return;

    $$("#contents ytd-rich-section-renderer").forEach((section) => {
      const titleSpan = section.querySelector(
        ".yt-shelf-header-layout__title-row .yt-shelf-header-layout__title .yt-core-attributed-string",
      );
      const text = titleSpan?.textContent?.trim();
      if (!text) return;

      if (text === "Shorts") {
        section.style.display = "none";
        log('Hidden "Shorts" rich section', 0);
      }
    });

    $$(
      "#contents .yt-shelf-header-layout__title .yt-core-attributed-string",
    ).forEach((span) => {
      const txt = span.textContent.trim();
      if (txt === "Shorts") {
        const richSection = span.closest("ytd-rich-section-renderer");
        if (richSection) {
          richSection.style.display = "none";
          log('Hidden generic "Shorts" rich section', 0);
        }
      }
    });

    $$("grid-shelf-view-model").forEach((shelf) => {
      const titleSpan = shelf.querySelector(
        ".yt-shelf-header-layout__title .yt-core-attributed-string",
      );
      const txt = titleSpan?.textContent?.trim();
      if (txt === "Shorts") {
        shelf.style.display = "none";
        log('Hidden grid-shelf "Shorts" section', 0);
      }
    });
  }

  function hide_teaser_carousel(node) {
    if (user_data.watch_page_config?.hide_live_chat_replay !== "on") return;
    if (!node) node = $("#teaser-carousel");
    if (!node) return;
    node.style.display = "none";
    log("Hidden Live chat replay teaser (#teaser-carousel)", 0);
  }

  function simulate_swipeup(target, start, end) {
    function createAndDispatchTouchEvent(type, target, clientY) {
      const touches =
        (type !== "touchend" && [
          new Touch({
            identifier: 0,
            target: target,
            clientY: clientY,
          }),
        ]) ||
        [];
      let touchEvent = new TouchEvent(type, {
        touches: touches,
        bubbles: true,
        cancelable: true,
      });
      target.dispatchEvent(touchEvent);
    }
    createAndDispatchTouchEvent("touchstart", target, start);
    createAndDispatchTouchEvent("touchmove", target, end);
    createAndDispatchTouchEvent("touchend", target);
  }

  function getCookie(cookieName) {
    const name = cookieName + "=";
    let decodedCookie;
    try {
      decodedCookie = decodeURIComponent(document.cookie);
    } catch (error) {
      log("cookie decode error", error, -1);
      return null;
    }
    const cookieArray = decodedCookie.split(";");
    for (let i = 0; i < cookieArray.length; i++) {
      const cookie = cookieArray[i].trim();

      if (cookie.startsWith(name)) {
        return cookie.substring(name.length, cookie.length);
      }
    }
    return null;
  }

  function check_native(name, fun) {
    const fun_str = fun.toString();
    if (browser_info.name !== "Firefox") {
      return `function ${name}() { [native code] }` === fun_str;
    } else {
      return `function ${name}() {\n    [native code]\n}` === fun_str;
    }
  }

  function set_history_hook(window_obj) {
    const wrap = function (type) {
      const origin = window_obj.history[type];
      return function () {
        let rv;
        try {
          rv = origin.apply(this, arguments);
        } catch (error) {
          log("history hook error", error, 0);
          return;
        }
        let url = arguments[2] || location.href;
        url.startsWith("/") && (url = location.origin + url);
        !url.startsWith("http") && (url = location.origin + "/" + url);
        url_change(url);
        return rv;
      };
    };
    window_obj.history.pushState = wrap("pushState");
    window_obj.history.replaceState = wrap("replaceState");
  }

  function url_observer() {
    set_history_hook(unsafeWindow);
    unsafeWindow.addEventListener("popstate", function (event) {
      url_change(event);
    });
    unsafeWindow.addEventListener("hashchange", function (event) {
      url_change(event);
    });
  }

  function url_change(event = null) {
    let destination_url;
    if (typeof event === "object")
      destination_url = event?.destination?.url || "";
    else destination_url = event;

    if (destination_url?.startsWith?.("about:blank")) return;
    if (destination_url === href) return;
    href = destination_url || location.href;
    log("Page URL changed href -> " + href, 0);
    const tmp_page_type = get_page_type();
    if (tmp_page_type !== page_type) {
      page_type = tmp_page_type;
      config_api.config_init();
      set_search_listen();
    }
    on_page_change();
  }

  function get_page_type(url = href) {
    if (!url) return "other";
    url.startsWith("/") && (url = location.origin + url);
    const base_url = url.split("?")[0];
    let tmp_page_type;
    if (base_url.match("https://www.youtube.com/?$")) tmp_page_type = "yt_home";
    else if (base_url.match("https://m.youtube.com/?#?$"))
      tmp_page_type = "mobile_yt_home";
    else if (base_url.match("https://www.youtube.com/watch$"))
      tmp_page_type = "yt_watch";
    else if (base_url.match("https://m.youtube.com/watch$"))
      tmp_page_type = "mobile_yt_watch";
    else if (base_url.match("https://www.youtube.com/results$"))
      tmp_page_type = "yt_search";
    else if (base_url.match("https://m.youtube.com/results$"))
      tmp_page_type = "mobile_yt_search";
    else if (base_url.startsWith("https://www.youtube.com/shorts"))
      tmp_page_type = "yt_shorts";
    else if (base_url.startsWith("https://m.youtube.com/shorts"))
      tmp_page_type = "mobile_yt_shorts";
    else if (base_url.match("https://www.youtubekids.com/watch$"))
      tmp_page_type = "yt_kids_watch";
    else if (base_url.match("https://music.youtube.com/?$"))
      tmp_page_type = "yt_music_home";
    else if (base_url.match("https://music.youtube.com/watch$"))
      tmp_page_type = "yt_music_watch";
    else if (base_url.match("https://m.youtube.com/#searching$"))
      tmp_page_type = "mobile_yt_home_searching";
    else if (base_url.startsWith("https://www.youtube.com/playlist"))
      tmp_page_type = "yt_watch_playlist";
    else if (base_url.includes("channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ"))
      tmp_page_type = "yt_music_channel";
    else tmp_page_type = "other";
    if (tmp_page_type === "mobile_yt_watch" && href.endsWith("#searching"))
      tmp_page_type = "mobile_yt_watch_searching";
    return tmp_page_type;
  }

  function set_debugger() {
    while (!debugger_fun_name) {
      let tmp = crypto
        .randomUUID()
        .substring(0, Math.floor(Math.random() * 4) + 3)
        .replace(/-/g, "");
      tmp = tmp.match("[a-z].+")?.[0];
      if (tmp && !unsafeWindow[tmp]) {
        debugger_fun_name = tmp;
      }
    }
    log(`debugger_fun_name: ${debugger_fun_name}`, 0);
    const debugger_config_info = {
      ytInitialPlayerResponse: debugger_ytInitialPlayerResponse,
      ytInitialData: debugger_ytInitialData,
      ytInitialReelWatchSequenceResponse:
        debugger_ytInitialReelWatchSequenceResponse,
      music_initialData: debugger_music_initialData,
      inject_info: inject_info,
      info: [
        "ytInitialData_rule",
        "ytInitialPlayerResponse_rule",
        "is_account_init",
        "user_data",
        "mobile_web",
        "page_type",
        "tmp_debugger_value",
      ],
    };
    unsafeWindow[debugger_fun_name] = function (action = null) {
      const keys = Object.keys(debugger_config_info);
      if (!action && action !== 0) {
        debugger;
        return;
      }
      if (action === "ytInitialPlayerResponse")
        log("ytInitialPlayerResponse", debugger_ytInitialPlayerResponse, 0);
      if (action === "ytInitialData")
        log("ytInitialData", debugger_ytInitialData, 0);
      if (action === "inject_info") log("inject_info", inject_info, 0);
      if (action === "info") {
        if (limit_eval) {
          log("eval is restricted", 0);
        } else {
          for (let key of debugger_config_info["info"]) {
            log(key, eval(trustedScript(key)), 0);
          }
        }
        return;
      }
      if (action === "list") {
        keys.forEach(function (key, index) {
          log(index, key, 0);
        });
      }
      if (typeof action === "number") {
        if (action < keys.length) {
          unsafeWindow[debugger_fun_name](keys[action]);
        } else if (action >= keys.length) {
          keys.forEach(function (key) {
            unsafeWindow[debugger_fun_name](key);
          });
        }
      }
    };
  }

  function log() {
    const arguments_arr = [...arguments];
    const flag = arguments_arr.pop();
    if (flag === -1) {
      error_messages.push(arguments_arr.join(" "));
    }
    if (flag === 999) arguments_arr.unshift("-----test---test-----");
    if (flag !== 0 && flag !== 999) arguments_arr.push(getCodeLocation());
    if (flag === 0 || flag === 999) {
      const array_length = arguments_arr.length;
      const color = flag === 0 ? "orange" : "blue";
      const css_str = `color: ${color};font-size: 20px`;
      for (let i = 0; i < array_length; i++) {
        if (typeof arguments_arr[i] === "string") {
          arguments_arr[i] = "%c" + arguments_arr[i];
          i === array_length - 1
            ? arguments_arr.push(css_str)
            : arguments_arr.splice(i + 1, 0, css_str);
          break;
        }
      }
    }
    if ([-1, 0, 999].includes(flag) || open_debugger)
      flag === -1
        ? origin_console.error(...arguments_arr)
        : origin_console.log(...arguments_arr);
  }

  function getBrowserInfo() {
    const userAgent = navigator.userAgent;
    let browserName;
    let browserVersion;
    const isMobile =
      /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
        userAgent,
      );
    if (userAgent.indexOf("Firefox") > -1) {
      browserName = "Firefox";
      browserVersion = userAgent.match(/Firefox\/([0-9.]+)/)[1];
    } else if (
      userAgent.indexOf("OPR") > -1 ||
      userAgent.indexOf("Opera") > -1
    ) {
      browserName = "Opera";
      browserVersion = userAgent.match(/(OPR|Opera)\/([0-9.]+)/)[2];
    } else if (userAgent.indexOf("Edg") > -1) {
      browserName = "Edge";
      browserVersion = userAgent.match(/Edg\/([0-9.]+)/)[1];
    } else if (userAgent.indexOf("Chrome") > -1) {
      browserName = "Chrome";
      browserVersion = userAgent.match(/Chrome\/([0-9.]+)/)[1];
    } else if (userAgent.indexOf("Safari") > -1) {
      browserName = "Safari";
      browserVersion = userAgent.match(/Version\/([0-9.]+)/)[1];
    } else if (
      userAgent.indexOf("MSIE") > -1 ||
      userAgent.indexOf("rv:") > -1
    ) {
      browserName = "Internet Explorer";
      browserVersion = userAgent.match(/(MSIE |rv:)([0-9.]+)/)[2];
    } else {
      browserName = "Unknown";
      browserVersion = "N/A";
    }

    return {
      name: browserName,
      version: browserVersion,
      isMobile: isMobile,
    };
  }

  function getCodeLocation() {
    if (["Firefox"].includes(browser_info.name)) return "";
    const callstack = new Error().stack.split("\n");
    callstack.shift();
    while (callstack.length && callstack[0].includes("-extension://")) {
      callstack.shift();
    }
    if (!callstack.length) {
      return "";
    }
    return "\n" + callstack[0].trim();
  }

  /* ===== 2444 : Information Window panel ===== */

  function display_error_win(msg) {
    const css = `
  #yt-error-popup{
    z-index:999999999;
    position:fixed;
    top:50%;
    left:50%;
    transform:translate(-50%,-50%);
    padding:0;
    background-color:#ffffff;
    border:1px solid #3498db;
    border-radius:5px;
    box-shadow:0 0 10px rgba(0,0,0,0.3);
    width:360px;
    max-height:80vh;
    display:flex;
    flex-direction:column;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  }

  #yt-error-header{
    cursor:move;
    user-select:none;
    padding:4px 8px;
    padding-right:32px;
    background-color:#3498db;
    color:#ffffff;
    border-radius:4px 4px 0 0;
    font-weight:bold;
    font-size:13px;
    position:relative;
  }

  #yt-error-close,#yt-vbt-settings-btn{
    position:absolute;
    top:50%;
    transform:translateY(-50%);
    cursor:pointer;
    background-color:transparent;
    color:#ffffff;
    border:none;
    padding:0;
    width:20px;
    height:20px;
    border-radius:3px;
    font-size:14px;
    font-weight:bold;
    line-height:1;
    display:flex;
    align-items:center;
    justify-content:center;
    transition:background-color 0.2s ease;
  }

  #yt-vbt-settings-btn{
    right:32px;
    font-size:16px;
  }

  #yt-vbt-settings-btn:hover{
    background-color:rgba(52,152,219,0.9);
  }

  #yt-vbt-settings-btn:active{
    background-color:#2980b9;
  }

  #yt-error-close{
    right:8px;
  }

  #yt-error-close:hover{
    background-color:rgba(231,76,60,0.9);
  }

  #yt-error-close:active{
    background-color:#c0392b;
  }

  #yt-error-body{
    flex:1 1 auto;
    overflow-y:auto;
    padding:8px 10px 10px 10px;
    white-space:pre-wrap;
    font-size:12px;
    color:#000;
  }

  /* Settings backup sub-popup */
  #yt-vbt-backup-popup{
    z-index:1000000000;
    position:fixed;
    top:50%;
    left:50%;
    transform:translate(-50%,-50%);
    padding:0;
    background-color:#ffffff;
    border:1px solid #3498db;
    border-radius:5px;
    box-shadow:0 0 15px rgba(0,0,0,0.4);
    width:280px;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  }

  #yt-vbt-backup-header{
    cursor:move;
    user-select:none;
    padding:6px 10px;
    padding-right:32px;
    background-color:#3498db;
    color:#ffffff;
    border-radius:4px 4px 0 0;
    font-weight:bold;
    font-size:13px;
    position:relative;
  }

  #yt-vbt-backup-close{
    position:absolute;
    top:50%;
    right:8px;
    transform:translateY(-50%);
    cursor:pointer;
    background-color:transparent;
    color:#ffffff;
    border:none;
    padding:0;
    width:20px;
    height:20px;
    border-radius:3px;
    font-size:16px;
    font-weight:bold;
    line-height:1;
    display:flex;
    align-items:center;
    justify-content:center;
    transition:background-color 0.2s ease;
  }

  #yt-vbt-backup-close:hover{
    background-color:rgba(231,76,60,0.9);
  }

  #yt-vbt-backup-body{
    padding:14px 14px 16px 14px;
    display:flex;
    flex-direction:column;
    gap:10px;
  }

  .yt-vbt-backup-btn{
    display:flex;
    align-items:center;
    gap:8px;
    padding:8px 12px;
    border:none;
    border-radius:5px;
    cursor:pointer;
    font-size:13px;
    font-weight:600;
    width:100%;
    background-color:#3498db;
    color:#ffffff;
    transition:background-color 0.2s ease;
  }

  .yt-vbt-backup-btn:hover{
    background-color:#2980b9;
  }

  .yt-vbt-backup-btn:active{
    background-color:#2471a3;
  }

  #yt-vbt-backup-note{
    font-size:11px;
    color:#888;
    text-align:center;
    margin-top:2px;
  }
  `;
    if (!unsafeWindow.document.getElementById("yt-error-style")) {
      const style = unsafeWindow.document.createElement("style");
      style.id = "yt-error-style";
      style.textContent = css;
      unsafeWindow.document.head.appendChild(style);
    }

    const old = unsafeWindow.document.getElementById("yt-error-popup");
    if (old) old.remove();

    const popup = unsafeWindow.document.createElement("div");
    popup.id = "yt-error-popup";

    const header = unsafeWindow.document.createElement("div");
    header.id = "yt-error-header";
    header.textContent = "Information (message)";

    const settingsBtn = unsafeWindow.document.createElement("button");
    settingsBtn.id = "yt-vbt-settings-btn";
    settingsBtn.innerHTML = "⚙";
    settingsBtn.title = "Export / Import settings";
    header.appendChild(settingsBtn);

    const closeBtn = unsafeWindow.document.createElement("button");
    closeBtn.id = "yt-error-close";
    closeBtn.innerHTML = "X";
    closeBtn.title = "Close";
    header.appendChild(closeBtn);

    const body = unsafeWindow.document.createElement("div");
    body.id = "yt-error-body";
    body.textContent = msg;

    popup.append(header, body);
    unsafeWindow.document.body.appendChild(popup);

    function close() {
      popup.remove();
      const bp = unsafeWindow.document.getElementById("yt-vbt-backup-popup");
      if (bp) bp.remove();
    }

    closeBtn.addEventListener("click", close);

    settingsBtn.addEventListener("click", () => {
      // Toggle: close if already open
      const existing = unsafeWindow.document.getElementById("yt-vbt-backup-popup");
      if (existing) { existing.remove(); return; }

      const bp = unsafeWindow.document.createElement("div");
      bp.id = "yt-vbt-backup-popup";

      const bh = unsafeWindow.document.createElement("div");
      bh.id = "yt-vbt-backup-header";
      bh.textContent = "Settings Backup";

      const bc = unsafeWindow.document.createElement("button");
      bc.id = "yt-vbt-backup-close";
      bc.innerHTML = "X";
      bc.title = "Close";
      bh.appendChild(bc);

      const bb = unsafeWindow.document.createElement("div");
      bb.id = "yt-vbt-backup-body";

      const exportBtn = unsafeWindow.document.createElement("button");
      exportBtn.className = "yt-vbt-backup-btn";
      exportBtn.innerHTML = "⬇ Export settings";
      exportBtn.addEventListener("click", () => {
        try {
          const exportData = JSON.parse(JSON.stringify(user_data));
          delete exportData.shorts_list;
          delete exportData.channel_infos;
          delete exportData.login;
          const blob = new Blob(
            [JSON.stringify(exportData, null, 2)],
            { type: "application/json" }
          );
          const url = URL.createObjectURL(blob);
          const a = unsafeWindow.document.createElement("a");
          a.href = url;
          a.download = "vBlockTube-settings.vbt";
          a.click();
          URL.revokeObjectURL(url);
        } catch (e) {
          alert("Export failed: " + e.message);
        }
      });

      const importBtn = unsafeWindow.document.createElement("button");
      importBtn.className = "yt-vbt-backup-btn";
      importBtn.innerHTML = "⬆ Import Settings";
      importBtn.addEventListener("click", () => {
        const fileInput = unsafeWindow.document.createElement("input");
        fileInput.type = "file";
        fileInput.accept = ".vbt";
        fileInput.style.display = "none";
        unsafeWindow.document.body.appendChild(fileInput);
        fileInput.addEventListener("change", () => {
          const file = fileInput.files[0];
          if (!file) return;
          const reader = new FileReader();
          reader.onload = (e) => {
            try {
              const imported = JSON.parse(e.target.result);
              if (typeof imported !== "object" || imported === null) {
                throw new Error("Invalid settings file.");
              }
              const preserved = {
                shorts_list: user_data.shorts_list,
                channel_infos: user_data.channel_infos,
                login: user_data.login,
                language: user_data.language,
              };
              Object.assign(user_data, imported, preserved);
              user_data_api.set();
              alert("Settings imported successfully. The page will now reload.");
              unsafeWindow.location.reload();
            } catch (err) {
              alert("Import failed: " + err.message);
            } finally {
              fileInput.remove();
            }
          };
          reader.readAsText(file);
        });
        fileInput.click();
      });

      const note = unsafeWindow.document.createElement("div");
      note.id = "yt-vbt-backup-note";

      bb.append(exportBtn, importBtn, note);
      bp.append(bh, bb);
      unsafeWindow.document.body.appendChild(bp);

      bc.addEventListener("click", () => bp.remove());
      make_popup_draggable(bp, bh);
    });

    make_popup_draggable(popup, header);
  }

  /* =============== MAIN 2333 CONFIG PANEL =============== */

  function display_config_win() {
    const css_str = `
  .popup{
    z-index:999999999;
    position:fixed;
    top:50%;
    left:50%;
    transform:translate(-50%,-50%);
    padding:0;
    background-color:#ffffff;
    border:1px solid #3498db;
    border-radius:5px;
    box-shadow:0 0 10px rgba(0,0,0,0.3);
    width:260px;
    max-height:80vh;
    display:flex;
    flex-direction:column;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  }

  .popup-header{
    cursor:move;
    user-select:none;
    padding:4px 8px;
    padding-right:32px;
    background-color:#3498db;
    color:#ffffff;
    border-radius:4px 4px 0 0;
    font-weight:bold;
    font-size:13px;
    text-align:left;
    position:relative;
  }

  .popup-close-button{
    position:absolute;
    top:50%;
    right:8px;
    transform:translateY(-50%);
    cursor:pointer;
    background-color:transparent;
    color:#ffffff;
    border:none;
    padding:0;
    width:20px;
    height:20px;
    border-radius:3px;
    font-size:16px;
    font-weight:bold;
    line-height:1;
    display:flex;
    align-items:center;
    justify-content:center;
    transition:background-color 0.2s ease;
  }

  .popup-close-button:hover{
    background-color:rgba(231,76,60,0.9);
  }

  .popup-close-button:active{
    background-color:#c0392b;
  }

  .popup-body{
    flex:1 1 auto;
    overflow-y:auto;
    padding:6px 8px 8px 8px;
  }

  .btn{
    cursor:pointer;
    background-color:#3498db;
    color:#ffffff;
    border:none;
    padding:5px 10px;
    margin:0 auto;
    border-radius:5px;
    display:block;
    margin-top:10px;
  }

  .recommend-title{
    user-select:none;
    font-weight:bold;
    font-size:13px;
    background-color:#f3f6fb;
    color:#333333;
    border:none;
    padding:5px 8px;
    border-radius:4px;
    width:auto;
    text-align:start;
    margin-bottom:4px;
  }

  .select-group{
    cursor:pointer;
    padding:4px 0 6px 0;
    list-style-type:none;
    margin:0;
    padding-left:0;
    user-select:none;
  }

  .item-group{
    list-style-type:none;
    margin:0;
    padding-left:0;
  }

  .popup h1{
    margin:4px 0;
  }

  label{
    font-size:13px;
  }
  `;
    const style = unsafeWindow.document.createElement("style");
    style.textContent = css_str;
    $("body").appendChild(style);

    let win_config;
    const home_watch_config = {
      recommend_btn: [
        {
          id: "open_recommend_shorts",
          title: "btn_recommend_shorts",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
            {
              tag: "btn_lable_subscribed",
              value: "subscribed",
              tips: "recommend_subscribed_lable_tips",
              condition: {
                login_status: true,
              },
            },
          ],
        },
        {
          id: "open_recommend_liveroom",
          title: "btn_recommend_liveroom",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
            {
              tag: "btn_lable_subscribed",
              value: "subscribed",
              tips: "recommend_subscribed_lable_tips",
              condition: {
                login_status: true,
              },
            },
          ],
        },
        {
          id: "open_recommend_movie",
          title: "btn_recommend_movie",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "open_recommend_popular",
          title: "btn_recommend_popular",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "open_recommend_playables",
          title: "btn_recommend_game",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "restore_related_sidebar_layout",
          title: "Restore Related Sidebar Layout",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
      ],
    };
    const shorts_config = {
      recommend_btn: [
        {
          id: "add_shorts_upload_date",
          title: "title_add_shorts_upload_date",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "shorts_change_author_name",
          title: "title_shorts_change_author_name",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "short_buy_super_thanks",
          title: "short_buy_super_thanks",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "shorts_disable_loop_play",
          title: "bt_shorts_disable_loop_play_title",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "shorts_auto_scroll",
          title: "btn_shorts_auto_scroll_title",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "shorts_add_video_progress",
          title: "btn_shorts_add_video_progress_title",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "shorts_dbclick_like",
          title: "btn_shorts_dbclick_like_title",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
      ],
    };

    const common_config = {
      recommend_btn: [
        {
          id: "sponsorblock",
          title: "btn_sponsorblock_title",
          tips: "btn_sponsorblock_tips",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "dark_mode",
          title: "Dark Mode",
          items: [
            {
              tag: "Auto",
              value: "auto",
            },
            {
              tag: "On",
              value: "on",
            },
            {
              tag: "Off",
              value: "off",
            },
          ],
        },
        {
          id: "global_shorts_block",
          title: "Block all Shorts",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "disable_saturated_hover",
          title: "Disable Saturated Hover",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "disable_play_on_hover",
          title: "Disable Play on Hover",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
      ],
    };

    const music_config = {
      recommend_btn: [
        {
          id: "sponsorblock",
          title: "btn_sponsorblock_title",
          tips: "btn_sponsorblock_tips",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
        {
          id: "duplicate_song_prevention",
          title: "btn_duplicate_song_prevention_title",
          tips: "btn_duplicate_song_prevention_tips",
          items: [
            {
              tag: "btn_lable_open",
              value: "on",
            },
            {
              tag: "btn_lable_close",
              value: "off",
            },
          ],
        },
      ],
    };

    if (
      ["mobile_yt_home_searching", "mobile_yt_watch_searching"].includes(
        page_type,
      )
    ) {
      home_watch_config.recommend_btn.push({
        split_line: true,
        title: "shorts_recommend_split_tag",
      });
      home_watch_config.recommend_btn.push(...shorts_config.recommend_btn);
    }
    [
      "yt_home",
      "yt_watch",
      "yt_search",
      "mobile_yt_search",
      "mobile_yt_watch_searching",
      "mobile_yt_home_searching",
      "yt_watch_playlist",
      "other",
    ].includes(page_type) && (win_config = home_watch_config);
    ["yt_shorts"].includes(page_type) && (win_config = shorts_config);

    // YouTube Music config panel config
    if (["yt_music_home", "yt_music_watch"].includes(page_type)) {
      win_config = music_config;
    } else {
      win_config &&
        win_config.recommend_btn.push(...common_config.recommend_btn);
    }

    if (!win_config) return;
    const popup_node = unsafeWindow.document.getElementById("xxx_popup");
    if (popup_node) {
      popup_node.remove_popup_listener("rm");
    }

    const popup = unsafeWindow.document.createElement("div");
    popup.id = "xxx_popup";
    popup.className = "popup";

    const header = unsafeWindow.document.createElement("div");
    header.className = "popup-header";
    header.textContent = flag_info.config_info || "Script Settings";

    const closeButton = unsafeWindow.document.createElement("button");
    closeButton.className = "popup-close-button";
    closeButton.innerHTML = "×";
    closeButton.title = "Close";
    header.appendChild(closeButton);

    const body = unsafeWindow.document.createElement("div");
    body.className = "popup-body";

    const item_groups = [];
    const item_group = unsafeWindow.document.createElement("ul");
    item_group.className = "item-group";
    win_config.recommend_btn.forEach((recommend_item_info) => {
      if (recommend_item_info.split_line) {
        let p = unsafeWindow.document.createElement("h1");
        p.style.fontSize = "large";
        p.style.textAlign = "center";
        p.style.color = "red";
        p.style.padding = "20px 20px";
        p.style.fontWeight = "bold";
        p.innerText =
          flag_info[recommend_item_info.title] || recommend_item_info.title;
        item_groups.push(p);
        return;
      }
      const recommend_id = recommend_item_info.id;
      const recommend_title =
        flag_info[recommend_item_info.title] || recommend_item_info.title;
      const recommend_tips =
        recommend_item_info.tips && flag_info[recommend_item_info.tips];
      const select_item_infos = recommend_item_info.items || [];
      const select_items = [];
      const item = unsafeWindow.document.createElement("li");
      const select_group = unsafeWindow.document.createElement("ul");
      select_group.className = "select-group";
      select_group.id = recommend_id;
      select_item_infos.forEach((select_item_info) => {
        const tag = flag_info[select_item_info.tag] || select_item_info.tag;
        const value = select_item_info.value;
        const tips = select_item_info.tips && flag_info[select_item_info.tips];
        const condition = select_item_info.condition;
        const select_item = unsafeWindow.document.createElement("li");
        const input = unsafeWindow.document.createElement("input");
        input.type = "radio";
        input.name = recommend_id + "_option";
        input.id = recommend_id + "_" + value;
        input.value = value;
        if (condition && condition.login_status) {
          if (condition.login_status !== user_data.login) {
            input.disabled = true;
          }
        }
        if (user_data[recommend_id] === value) {
          input.checked = true;
        }
        input.addEventListener("click", () => {
          handle_recommend_radio(input);
        });
        const label = unsafeWindow.document.createElement("label");
        label.htmlFor = input.id;
        label.innerText = tag;
        tips && (label.title = tips);
        select_item.append(input, label);
        select_items.push(select_item);
      });
      const recommend_title_div = unsafeWindow.document.createElement("div");
      recommend_title_div.className = "recommend-title";
      recommend_title_div.innerText = recommend_title;
      recommend_tips && (recommend_title_div.title = recommend_tips);
      select_group.append(...select_items);
      item.append(recommend_title_div, select_group);
      item_groups.push(item);
    });
    item_group.append(...item_groups);

    body.appendChild(item_group);
    popup.append(header, body);
    unsafeWindow.document.body.append(popup);

    function remove_popup_hander(event) {
      if (
        (event && event.target && !popup.contains(event.target)) ||
        (event && event.target === closeButton) ||
        event === "rm"
      ) {
        popup.remove();
        unsafeWindow.document.removeEventListener("click", remove_popup_hander);
        if (
          ["mobile_yt_watch_searching", "mobile_yt_home_searching"].includes(
            page_type,
          )
        ) {
          history.back();
        }
      }
    }

    popup.remove_popup_listener = remove_popup_hander;
    unsafeWindow.document.addEventListener("click", remove_popup_hander);
    closeButton.addEventListener("click", remove_popup_hander);

    make_popup_draggable(popup, header, "pos_2333");

    return;
  }

  function make_popup_draggable(popup, handle, posKey) {
    let isDown = false;
    let offsetX = 0;
    let offsetY = 0;

    if (posKey) {
      const saved = user_data.popup_positions?.[posKey];
      if (saved) {
        popup.style.transform = "none";
        popup.style.top = saved.top;
        popup.style.left = saved.left;
      }
    }

    const cleanup = () => {
      unsafeWindow.document.removeEventListener("mousemove", onMouseMove);
      unsafeWindow.document.removeEventListener("mouseup", onMouseUp);
    };

    const onMouseMove = (e) => {
      if (!unsafeWindow.document.contains(popup)) { cleanup(); return; }
      if (!isDown) return;
      const x = e.clientX - offsetX;
      const y = e.clientY - offsetY;
      popup.style.left = x + "px";
      popup.style.top = y + "px";
    };

    const onMouseUp = () => {
      if (!unsafeWindow.document.contains(popup)) { cleanup(); return; }
      if (!isDown) return;
      isDown = false;
      if (posKey) {
        if (!user_data.popup_positions) user_data.popup_positions = {};
        user_data.popup_positions[posKey] = {
          top: popup.style.top,
          left: popup.style.left,
        };
        user_data_api.set();
      }
    };

    handle.addEventListener("mousedown", (e) => {
      if (e.button !== 0) return;
      isDown = true;

      const rect = popup.getBoundingClientRect();
      popup.style.transform = "none";
      popup.style.top = rect.top + "px";
      popup.style.left = rect.left + "px";

      offsetX = e.clientX - rect.left;
      offsetY = e.clientY - rect.top;

      e.preventDefault();
    });

    unsafeWindow.document.addEventListener("mousemove", onMouseMove);
    unsafeWindow.document.addEventListener("mouseup", onMouseUp);
  }

  function handle_recommend_radio(input_obj) {
    const setting_id = input_obj.parentNode.parentNode.id;
    user_data[setting_id] = input_obj.value;
    user_data_api.set();

    // Apply dark mode if setting changed
    if (setting_id === "dark_mode") {
      darkModeSystem.apply();
    }

    config_api.config_init(user_data.language);
  }

  function init_disable_saturated_hover() {
    const styleId = "no-saturated-hover-style";

    const removeStyle = () => {
      const el = unsafeWindow.document.getElementById(styleId);
      if (el) el.remove();
    };

    if (user_data.disable_saturated_hover !== "on") {
      removeStyle();
      return;
    }

    const detectDark = () => {
      const html = unsafeWindow.document.documentElement;
      if (html.hasAttribute("dark") || html.classList.contains("dark-theme")) return true;
      if (html.hasAttribute("light") || html.classList.contains("light-theme")) return false;
      try {
        const bg = (getComputedStyle(html).getPropertyValue("--yt-spec-base-background") || "").trim();
        if (bg.startsWith("rgb")) {
          const nums = bg.match(/\d+/g);
          if (nums && nums.length >= 3) {
            return ((+nums[0] + +nums[1] + +nums[2]) / 3) < 60;
          }
        }
      } catch (e) {}
      return false;
    };

    const buildCss = (d) => (`
html {
  --ytc-base-background:${d ? "#0f0f0f" : "#fff"};
  --ytc-additive-background:${d ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)"};
  --ytc-text-primary:${d ? "#f1f1f1" : "#0f0f0f"};
  --ytc-text-secondary:${d ? "#aaa" : "#606060"};
  --yt-spec-base-background:var(--yt-spec-base-background,var(--ytc-base-background));
  --yt-spec-additive-background:var(--yt-spec-additive-background,var(--ytc-additive-background));
  --yt-spec-text-primary:var(--yt-spec-text-primary,var(--ytc-text-primary));
  --yt-spec-text-secondary:var(--yt-spec-text-secondary,var(--ytc-text-secondary));
  --yt-active-playlist-panel-background-color:var(--yt-spec-additive-background);
  --yt-lightsource-primary-title-color:var(--ytc-text-primary);
  --yt-lightsource-secondary-title-color:var(--ytc-text-secondary);
  --iron-icon-fill-color:var(--yt-lightsource-primary-title-color);
}

.yt-spec-touch-feedback-shape__hover-effect,
.yt-spec-touch-feedback-shape__stroke,
.yt-spec-touch-feedback-shape__fill {
  display:none !important;
  opacity:0 !important;
  pointer-events:none !important;
}

ytd-rich-item-renderer.ytd-rich-item-renderer-highlight {
  background:transparent !important;
  box-shadow:none !important;
  --yt-spec-outline:transparent !important;
}

ytd-rich-grid-renderer #video-title,
.yt-lockup-metadata-view-model__title,
.yt-lockup-metadata-view-model__title a {
  color:var(--yt-spec-text-primary,var(--ytc-text-primary)) !important;
}

.yt-lockup-metadata-view-model__metadata,
.yt-lockup-metadata-view-model__metadata span,
#metadata-line span,
.yt-content-metadata-view-model__metadata-text,
.yt-content-metadata-view-model__metadata-text span,
.yt-content-metadata-view-model__delimiter {
  color:var(--yt-spec-text-secondary,var(--ytc-text-secondary)) !important;
}

ytd-watch-metadata .yt-core-attributed-string--link-inherit-color:not(:has(a)),
ytd-watch-metadata #description,
ytd-video-secondary-info-renderer #description,
ytd-watch-info-text,
#metadata.ytd-watch-info-text,
#metadata-line.ytd-video-primary-info-renderer span,
#snippet-text,
#snippet-text *,
#attributed-snippet-text,
#attributed-snippet-text * {
  color:var(--yt-spec-text-primary,var(--ytc-text-primary)) !important;
}

#snippet-text :not(a):hover,
#attributed-snippet-text :not(a):hover,
ytd-watch-info-text :not(a):hover {
  color:var(--yt-spec-text-primary,var(--ytc-text-primary)) !important;
  filter:none !important;
  opacity:1 !important;
}

.yt-core-attributed-string--highlight-text-decorator>a.yt-core-attributed-string__link--call-to-action-color,
.yt-core-attributed-string--link-inherit-color .yt-core-attributed-string--highlight-text-decorator>a.yt-core-attributed-string__link--call-to-action-color {
  color:var(--yt-spec-text-primary,var(--ytc-text-primary)) !important;
}

ytd-watch-metadata :not(.yt-core-attributed-string--highlight-text-decorator)>.yt-core-attributed-string__link--call-to-action-color,
#snippet-text :not(.yt-core-attributed-string--highlight-text-decorator)>.yt-core-attributed-string__link--call-to-action-color,
#attributed-snippet-text :not(.yt-core-attributed-string--highlight-text-decorator)>.yt-core-attributed-string__link--call-to-action-color {
  color:var(--yt-spec-call-to-action,#3ea6ff) !important;
}

ytd-watch-metadata #owner .yt-core-attributed-string__link--call-to-action-color {
  color:var(--yt-spec-text-primary,var(--ytc-text-primary)) !important;
}

ytd-watch-metadata,.ytd-watch-metadata {
  --yt-saturated-base-background:var(--ytc-base-background);
  --yt-saturated-raised-background:var(--yt-spec-additive-background,var(--ytc-additive-background));
  --yt-saturated-additive-background:var(--yt-spec-additive-background,var(--ytc-additive-background));
  --yt-saturated-text-primary:var(--yt-spec-text-primary,var(--ytc-text-primary));
  --yt-saturated-text-secondary:var(--yt-spec-text-secondary,var(--ytc-text-secondary));
  --yt-saturated-overlay-background:var(--yt-spec-additive-background,var(--ytc-additive-background));
  --yt-spec-overlay-background:var(--yt-spec-additive-background,var(--ytc-additive-background));
  --yt-spec-static-overlay-background-light:var(--yt-spec-additive-background,var(--ytc-additive-background));
  --yt-active-playlist-panel-background-color:var(--yt-spec-additive-background);
  --yt-lightsource-primary-title-color:var(--ytc-text-primary);
  --yt-lightsource-secondary-title-color:var(--ytc-text-secondary);
  --iron-icon-fill-color:var(--yt-lightsource-primary-title-color);
}

.yt-core-attributed-string--highlight-text-decorator {
  background-color:var(--yt-spec-static-overlay-background-light,PLACEHOLDER_COLOR) !important;
  border-radius:8px !important;
  padding-bottom:1px !important;
}

ytd-masthead[is-watch-page][dark]:not([theater]):not([fullscreen]) #background.ytd-masthead,
ytd-masthead[is-shorts-page][dark] #background.ytd-masthead,
#background.ytd-masthead {
  opacity:1 !important;
  background:var(--yt-spec-base-background,var(--ytc-base-background)) !important;
}
`).trim().replace("PLACEHOLDER_COLOR", d ? "rgba(255,255,255,0.102)" : "rgba(0,0,0,0.051)");

    const CSS_CACHE = { dark: buildCss(true), light: buildCss(false) };

    const apply = () => {
      const isDark = detectDark();
      let styleEl = unsafeWindow.document.getElementById(styleId);
      if (!styleEl) {
        styleEl = unsafeWindow.document.createElement("style");
        styleEl.id = styleId;
        (unsafeWindow.document.head || unsafeWindow.document.documentElement).appendChild(styleEl);
      }
      const newCss = isDark ? CSS_CACHE.dark : CSS_CACHE.light;
      if (styleEl.textContent !== newCss) styleEl.textContent = newCss;
    };

    apply();

    if (!unsafeWindow.__yt_saturated_hover_init) {
      unsafeWindow.__yt_saturated_hover_init = true;
      unsafeWindow.addEventListener("yt-navigate-finish", apply, { passive: true });
      unsafeWindow.addEventListener("yt-dark-mode-toggled", apply, { passive: true });
    }
  }


  function init_disable_play_on_hover() {
    const styleId = "disable-play-on-hover-style";
    const removeStyle = () => {
      const existing = unsafeWindow.document.getElementById(styleId);
      if (existing) existing.remove();
    };

    if (user_data.disable_play_on_hover !== "on") {
      removeStyle();
      return;
    }

    const css = `
  ytd-thumbnail[is-preview-loading] ytd-thumbnail-overlay-toggle-button-renderer.ytd-thumbnail,
  ytd-thumbnail[is-preview-loading] ytd-thumbnail-overlay-time-status-renderer.ytd-thumbnail,
  ytd-thumbnail[is-preview-loading] ytd-thumbnail-overlay-endorsement-renderer.ytd-thumbnail,
  ytd-thumbnail[is-preview-loading] ytd-thumbnail-overlay-hover-text-renderer.ytd-thumbnail,
  ytd-thumbnail[is-preview-loading] ytd-thumbnail-overlay-button-renderer.ytd-thumbnail,
  ytd-thumbnail[now-playing] ytd-thumbnail-overlay-time-status-renderer.ytd-thumbnail,
  ytd-thumbnail-overlay-loading-preview-renderer[is-preview-loading],
  ytd-grid-video-renderer a#thumbnail div#mouseover-overlay,
  ytd-rich-item-renderer a#thumbnail div#mouseover-overlay,
  ytd-thumbnail-overlay-loading-preview-renderer,
  ytd-moving-thumbnail-renderer img#thumbnail,
  .ytAnimatedThumbnailOverlayViewModelHost,
  animated-thumbnail-overlay-view-model,
  ytd-moving-thumbnail-renderer yt-icon,
  ytd-moving-thumbnail-renderer span,
  ytd-moving-thumbnail-renderer img,
  ytd-moving-thumbnail-renderer,
  #mouseover-overlay,
  ytd-video-preview,
  div#video-preview,
  #video-preview,
  #preview {
    display: none !important;
  }
  `;

    let styleEl = unsafeWindow.document.getElementById(styleId);
    if (!styleEl) {
      styleEl = unsafeWindow.document.createElement("style");
      styleEl.id = styleId;
      unsafeWindow.document.head.appendChild(styleEl);
    }
    styleEl.textContent = css;
  }

  function init_disable_end_cards() {
    const styleId = "disable-end-cards-style";
    const removeStyle = () => {
      const existing = unsafeWindow.document.getElementById(styleId);
      if (existing) existing.remove();
    };

    if (user_data.hide_end_cards !== "on") {
      removeStyle();
      return;
    }

    const css = `
  .ytp-endscreen-container,
  [data-a11y-skip-to-endscreen-button],
  ytd-video-secondary-info-renderer .yt-chip-cloud-chip-renderer,
  .ytp-ce-playlist,
  .ytp-ce-element {
    display: none !important;
  }
  `;

    let styleEl = unsafeWindow.document.getElementById(styleId);
    if (!styleEl) {
      styleEl = unsafeWindow.document.createElement("style");
      styleEl.id = styleId;
      unsafeWindow.document.head.appendChild(styleEl);
    }
    styleEl.textContent = css;
  }

  function init_interruptions_remover() {
    const removeInterruptionsPopup = () => {
      const toasts =
        unsafeWindow.document.querySelectorAll("tp-yt-paper-toast");
      toasts.forEach((toast) => {
        const textEl = toast.querySelector("#text");
        if (
          textEl &&
          textEl.textContent.includes("Experiencing interruptions?")
        ) {
          toast.remove();
        }
      });
    };

    try {
      const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          if (mutation.addedNodes.length) {
            removeInterruptionsPopup();
          }
        });
      });

      observer.observe(unsafeWindow.document.body, {
        childList: true,
        subtree: true,
        attributes: false,
        characterData: false,
      });
    } catch (e) {}
  }

  async function init_miniplayer_button() {
    if (
      document.querySelector(
        "#cpfyt-miniplayer-button, .ytp-chrome-bottom .ytp-miniplayer-button",
      )
    )
      return;

    let $sizeButton = null;
    const maxAttempts = 50;
    let attempts = 0;

    while (!$sizeButton && attempts < maxAttempts) {
      $sizeButton = document.querySelector(
        ".ytp-chrome-bottom .ytp-size-button",
      );
      if ($sizeButton) break;
      await new Promise((r) => setTimeout(r, 100));
      attempts++;
    }

    if (!$sizeButton) return;

    const supportsAnchorPositioning =
      "anchorName" in document.documentElement.style;
    const style = $sizeButton.parentElement.classList.contains(
      "ytp-right-controls-right",
    )
      ? "new"
      : "old";

    const buttonHTML = `<button id="cpfyt-miniplayer-button" class="ytp-button" aria-keyshortcuts="i" ${!supportsAnchorPositioning ? `title="Miniplayer (i)"` : ""}>
        ${
          style == "new"
            ? `
          <svg fill="none" height="24" viewBox="0 0 24 24" width="24">
            <path d="M21.20 3.01C21.66 3.05 22.08 3.26 22.41 3.58C22.73 3.91 22.94 4.33 22.98 4.79L23 5V19C23.00 19.49 22.81 19.97 22.48 20.34C22.15 20.70 21.69 20.93 21.20 20.99L21 21H3L2.79 20.99C2.30 20.93 1.84 20.70 1.51 20.34C1.18 19.97 .99 19.49 1 19V13H3V19H21V5H11V3H21L21.20 3.01ZM1.29 3.29C1.10 3.48 1.00 3.73 1.00 4C1.00 4.26 1.10 4.51 1.29 4.70L5.58 9H3C2.73 9 2.48 9.10 2.29 9.29C2.10 9.48 2 9.73 2 10C2 10.26 2.10 10.51 2.29 10.70C2.48 10.89 2.73 11 3 11H9V5C9 4.73 8.89 4.48 8.70 4.29C8.51 4.10 8.26 4 8 4C7.73 4 7.48 4.10 7.29 4.29C7.10 4.48 7 4.73 7 5V7.58L2.70 3.29C2.51 3.10 2.26 3.00 2 3.00C1.73 3.00 1.48 3.10 1.29 3.29ZM19.10 11.00L19 11H12L11.89 11.00C11.66 11.02 11.45 11.13 11.29 11.29C11.13 11.45 11.02 11.66 11.00 11.89L11 12V17C10.99 17.24 11.09 17.48 11.25 17.67C11.42 17.85 11.65 17.96 11.89 17.99L12 18H19L19.10 17.99C19.34 17.96 19.57 17.85 19.74 17.67C19.90 17.48 20.00 17.24 20 17V12L19.99 11.89C19.97 11.66 19.87 11.45 19.70 11.29C19.54 11.13 19.33 11.02 19.10 11.00ZM13 16V13H18V16H13Z" fill="white"></path>
          </svg>
        `
            : `
          <svg height="100%" version="1.1" viewBox="0 0 36 36" width="100%">
            <use xlink:href="#cpfyt-id-1" class="ytp-svg-shadow"></use>
            <path id="cpfyt-id-1" d="M25,17 L17,17 L17,23 L25,23 L25,17 L25,17 Z M29,25 L29,10.98 C29,9.88 28.1,9 27,9 L9,9 C7.9,9 7,9.88 7,10.98 L7,25 C7,26.1 7.9,27 9,27 L27,27 C28.1,27 29,26.1 29,25 L29,25 Z M27,25.02 L9,25.02 L9,10.97 L27,10.97 L27,25.02 L27,25.02 Z" fill="#fff" fill-rule="evenodd"></path>
          </svg>
        `
        }
      </button>${
        supportsAnchorPositioning
          ? `<div class="ytp-tooltip ytp-bottom">
        <div class="ytp-tooltip-text-wrapper" aria-hidden="true">
          <div class="ytp-tooltip-bottom-text${style == "old" ? " ytp-tooltip-text-no-title" : ""}">
            <span class="ytp-tooltip-text">Miniplayer${style == "old" ? " (i)" : ""}</span>
            ${style == "new" ? '<div class="ytp-tooltip-keyboard-shortcut">I</div>' : ""}
          </div>
        </div>
      </div>`
          : ""
      }`;

    $sizeButton.insertAdjacentHTML("beforebegin", buttonHTML);

    const $button = document.querySelector("#cpfyt-miniplayer-button");

    $button.style.display = "inline-block";

    if (supportsAnchorPositioning) {
      $button.style.anchorName = "--cpfyt-miniplayer-anchor";
    }

    if (!supportsAnchorPositioning) {
      const $tooltip = $button.nextElementSibling;
      if ($tooltip && $tooltip.classList.contains("ytp-tooltip")) {
        $button.addEventListener("mouseenter", () => {
          $tooltip.style.display = "block";
        });
        $button.addEventListener("mouseleave", () => {
          $tooltip.style.display = "none";
        });
      }
    }

    $button.addEventListener("click", (e) => {
      e.preventDefault();
      e.stopPropagation();
      document.dispatchEvent(
        new KeyboardEvent("keydown", {
          bubbles: true,
          cancelable: true,
          code: "KeyI",
          key: "i",
          keyCode: 73,
          which: 73,
        }),
      );
    });
  }

  function display_update_win() {
    function btn_click() {
      const btn = this;
      if (btn.id === "go_btn") {
        location.href = script_url;
      }
      container.remove();
    }
    const css_str =
      "#update_tips_win { z-index:9999999999; display: flex; position: fixed; bottom: 20px; right: 20px; padding: 10px 20px; background-color: #fff; border: 1px solid #ccc; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); border-radius: 10px; } .btn { margin: 0 10px; display: inline-block; padding: 5px 10px; background-color: #3498db; color: #fff; border: none; border-radius: 5px; cursor: pointer; transition: background-color 0.3s ease; } .btn:hover { background-color: #2980b9; }";
    const style = unsafeWindow.document.createElement("style");
    style.innerText = css_str;
    $("body").appendChild(style);
    const container = unsafeWindow.document.createElement("div");
    container.id = "update_tips_win";
    const span = unsafeWindow.document.createElement("span");
    span.textContent = GM_info.script.name + " has an update!";
    container.appendChild(span);
    const go_btn = unsafeWindow.document.createElement("button");
    go_btn.textContent = "GO";
    go_btn.id = "go_btn";
    go_btn.className = "btn";
    go_btn.onclick = btn_click;
    container.appendChild(go_btn);
    const no_btn = unsafeWindow.document.createElement("button");
    no_btn.textContent = "NO";
    no_btn.className = "btn";
    no_btn.id = "no_btn";
    no_btn.onclick = btn_click;
    container.appendChild(no_btn);
    $("body").appendChild(container);
  }

  function check_update() {
    const script_handler = GM_info.scriptHandler;
    if (["Via"].includes(script_handler)) return;
    const last_check_time = GM_getValue("last_check_time", 0);
    if (Date.now() - last_check_time < 1000 * 60 * 60 * 24) return;
    GM_xmlhttpRequest({
      method: "GET",
      url: script_url,
      onload: function (response) {
        const onlineScript = response.responseText;
        const onlineMeta = onlineScript.match(/@version\s+([^\s]+)/i);
        const onlineVersion = onlineMeta ? onlineMeta[1] : "";
        if (onlineVersion > GM_info.script.version) {
          display_update_win();
        }
      },
    });
    GM_setValue("last_check_time", Date.now());
  }

  function obj_process_filter(path_info, json_obj) {
    if (
      !["yt_home", "yt_watch", "mobile_yt_home", "mobile_yt_watch"].includes(
        page_type,
      )
    )
      return false;
    if (!user_data.login || user_data.channel_infos.ids.length === 0)
      return false;

    if (
      user_data.open_recommend_shorts === "subscribed" &&
      path_info.condition_value === "YOUTUBE_SHORTS_BRAND_24"
    ) {
      if (path_info.express.includes("YOUTUBE_SHORTS_BRAND_24")) {
        let video_list_path;
        video_list_path =
          path_info.conform_value_path.split('["icon"]')[0] +
          (page_type === "yt_home" ? '["contents"]' : '["items"]');
        const video_list =
          data_process.string_to_value(json_obj, video_list_path) || [];
        shorts_fun.node_parse(video_list);
      }
    }

    if (
      user_data.open_recommend_liveroom === "subscribed" &&
      ["UPCOMING", "LIVE", "BADGE_STYLE_TYPE_LIVE_NOW"].includes(
        path_info.condition_value,
      )
    ) {
      if (path_info.express.includes("UPCOMING")) {
        try {
          const match = JSON.stringify(
            data_process.string_to_value(json_obj, path_info.deal_path),
          ).match(/"browseId"\:"(.*?)"/);
          let id;
          if (match && match.length > 1) id = match[1];
          if (!id) {
            log("Failed to get id\n" + JSON.stringify(path_info), -1);
          }
          if (user_data.channel_infos.ids.includes(id)) {
            const index = user_data.channel_infos.ids.indexOf(id);
            const name = user_data.channel_infos.names[index];
            log(
              "Do not filter " +
                name +
                (path_info.condition_value === "UPCOMING"
                  ? " upcoming Live"
                  : " ongoing Live"),
              "shorts",
            );
            return true;
          }
          let msg = `Filtering ${id} ${
            path_info.condition_value === "UPCOMING"
              ? " upcoming Live"
              : " ongoing Live"
          }`;
          log(msg, "shorts");
        } catch (error) {
          log(error, -1);
        }
      }
    }
    return false;
  }

  function get_shorts_fun() {
    class ShortsFun {
      constructor() {
        this.parsing = false;
        this.shorts_list = [];
      }
      node_parse(video_list) {
        !user_data.shorts_list && (user_data.shorts_list = []);
        let video_id, title, views_lable, thumbnail_url;
        let count = 0;
        for (let video_info of video_list) {
          count++;
          if (page_type === "yt_home") {
            video_id =
              video_info.richItemRenderer.content.reelItemRenderer.videoId;
            title =
              video_info.richItemRenderer.content.reelItemRenderer.headline
                .simpleText;
            views_lable =
              video_info.richItemRenderer.content.reelItemRenderer.viewCountText
                .simpleText;
            thumbnail_url =
              video_info.richItemRenderer.content.reelItemRenderer.thumbnail
                .thumbnails[0].url;
          }
          if (page_type === "yt_watch") {
            video_id = video_info.reelItemRenderer.videoId;
            title = video_info.reelItemRenderer.headline.simpleText;
            views_lable = video_info.reelItemRenderer.viewCountText.simpleText;
            thumbnail_url =
              video_info.reelItemRenderer.thumbnail.thumbnails[0].url;
          }
          if (["mobile_yt_home", "mobile_yt_watch"].includes(page_type)) {
            video_id = video_info.shortsLockupViewModel.entityId.replace(
              "shorts-shelf-item-",
              "",
            );
            title =
              video_info.shortsLockupViewModel.overlayMetadata.primaryText
                .content;
            views_lable =
              video_info.shortsLockupViewModel.overlayMetadata.secondaryText
                .content;
            thumbnail_url =
              video_info.shortsLockupViewModel.thumbnail.sources[0].url;
          }
          this.shorts_list.push({
            id: video_id,
            title: title,
            views_lable: views_lable,
            thumbnail_url: thumbnail_url,
          });
          if (!this.parsing) {
            this.parsing = true;
            setTimeout(() => {
              this.parse_shorts_list();
            }, shorts_parse_delay);
          }
        }
      }
      get_shorts_section() {
        if (!user_data.shorts_list || !user_data.shorts_list.length) return;
        let root, item_path;
        const items = [];
        if (page_type == "yt_home") {
          root = {
            richSectionRenderer: {
              content: {
                richShelfRenderer: {
                  title: {
                    runs: [
                      {
                        text: "Shorts",
                      },
                    ],
                  },
                  contents: [],
                  trackingParams: "CNMEEN-DAyITCOGA_NHuz4UDFWdqTAgdfF4E-Q==",
                  menu: {
                    menuRenderer: {
                      trackingParams:
                        "CNMEEN-DAyITCOGA_NHuz4UDFWdqTAgdfF4E-Q==",
                      topLevelButtons: [
                        {
                          buttonRenderer: {
                            style: "STYLE_OPACITY",
                            size: "SIZE_DEFAULT",
                            isDisabled: false,
                            serviceEndpoint: {
                              clickTrackingParams:
                                "CNYEEKqJCRgMIhMI4YD80e7PhQMVZ2pMCB18XgT5",
                              commandMetadata: {
                                webCommandMetadata: {
                                  sendPost: true,
                                  apiUrl: "/youtubei/v1/feedback",
                                },
                              },
                              feedbackEndpoint: {
                                feedbackToken:
                                  "AB9zfpIcTXNyA3lbF_28icb4umRJ5AveSSTqmF7T9gE8k-Sw7HrOTLE5wzA2TScqfTByCI-cR9nPuVMSWAgbNuuaruVBYx2-2dGAzujQTL8KGMOyCFM_wmGhkLTSdUBQzsFQRHEibpg_",
                                uiActions: {
                                  hideEnclosingContainer: true,
                                },
                                actions: [
                                  {
                                    clickTrackingParams:
                                      "CNYEEKqJCRgMIhMI4YD80e7PhQMVZ2pMCB18XgT5",
                                    replaceEnclosingAction: {
                                      item: {
                                        notificationMultiActionRenderer: {
                                          responseText: {
                                            runs: [
                                              {
                                                text: "Shelf will be hidden for ",
                                              },
                                              {
                                                text: "30",
                                              },
                                              {
                                                text: " days",
                                              },
                                            ],
                                          },
                                          buttons: [
                                            {
                                              buttonRenderer: {
                                                style: "STYLE_BLUE_TEXT",
                                                text: {
                                                  simpleText: "Undo",
                                                },
                                                serviceEndpoint: {
                                                  clickTrackingParams:
                                                    "CNgEEPBbGAAiEwjhgPzR7s-FAxVnakwIHXxeBPk=",
                                                  commandMetadata: {
                                                    webCommandMetadata: {
                                                      sendPost: true,
                                                      apiUrl:
                                                        "/youtubei/v1/feedback",
                                                    },
                                                  },
                                                  undoFeedbackEndpoint: {
                                                    undoToken:
                                                      "AB9zfpLpAillN1hH9cyfSbyPRWwAhTOJo6mUTu-ony4HASc0KgCEy0ifaIrDUdJJEk4OXiPC43EMPZBEK8WGiIqeci4r97TGpabAUk84dEh7tHzF7-rsziFBGZjY92Jyk3YujrF2_wxC",
                                                    actions: [
                                                      {
                                                        clickTrackingParams:
                                                          "CNgEEPBbGAAiEwjhgPzR7s-FAxVnakwIHXxeBPk=",
                                                        undoFeedbackAction: {
                                                          hack: true,
                                                        },
                                                      },
                                                    ],
                                                  },
                                                },
                                                trackingParams:
                                                  "CNgEEPBbGAAiEwjhgPzR7s-FAxVnakwIHXxeBPk=",
                                              },
                                            },
                                          ],
                                          trackingParams:
                                            "CNcEEKW8ASITCOGA_NHuz4UDFWdqTAgdfF4E-Q==",
                                        },
                                      },
                                    },
                                  },
                                ],
                              },
                            },
                            icon: {
                              iconType: "DISMISSAL",
                            },
                            tooltip: "Not interested",
                            trackingParams:
                              "CNYEEKqJCRgMIhMI4YD80e7PhQMVZ2pMCB18XgT5",
                            accessibilityData: {
                              accessibilityData: {
                                label: "Not interested",
                              },
                            },
                          },
                        },
                      ],
                    },
                  },
                  showMoreButton: {
                    buttonRenderer: {
                      style: "STYLE_OPACITY",
                      size: "SIZE_DEFAULT",
                      text: {
                        runs: [
                          {
                            text: "Show more",
                          },
                        ],
                      },
                      icon: {
                        iconType: "EXPAND",
                      },
                      accessibility: {
                        label: "Show more",
                      },
                      trackingParams:
                        "CNUEEJnjCyITCOGA_NHuz4UDFWdqTAgdfF4E-Q==",
                    },
                  },
                  isExpanded: false,
                  icon: {
                    iconType: "YOUTUBE_SHORTS_BRAND_24",
                  },
                  isTopDividerHidden: false,
                  isBottomDividerHidden: false,
                  showLessButton: {
                    buttonRenderer: {
                      style: "STYLE_OPACITY",
                      size: "SIZE_DEFAULT",
                      text: {
                        runs: [
                          {
                            text: "Show less",
                          },
                        ],
                      },
                      icon: {
                        iconType: "COLLAPSE",
                      },
                      accessibility: {
                        label: "Show less",
                      },
                      trackingParams: "CNQEEPBbIhMI4YD80e7PhQMVZ2pMCB18XgT5",
                    },
                  },
                },
              },
              trackingParams: "CNIEEOOXBRgEIhMI4YD80e7PhQMVZ2pMCB18XgT5",
              fullBleed: false,
            },
          };
          item_path =
            "root.richSectionRenderer.content.richShelfRenderer.contents";
        }
        if (["mobile_yt_watch", "yt_watch"].includes(page_type)) {
          root = {
            reelShelfRenderer: {
              title: {
                runs: [
                  {
                    text: "Shorts",
                  },
                ],
              },
              items: [],
              trackingParams: "CM4CEN-DAxgEIhMInKOvhY3QhQMVGcCXCB04HQR6",
              icon: {
                iconType: "YOUTUBE_SHORTS_BRAND_24",
              },
            },
          };
          item_path = "root.reelShelfRenderer.items";
        }
        if (page_type == "mobile_yt_home") {
          root = {
            richSectionRenderer: {
              content: {
                reelShelfRenderer: {
                  title: {
                    runs: [
                      {
                        text: "Shorts",
                      },
                    ],
                  },
                  button: {
                    menuRenderer: {
                      trackingParams: "CHYQ34MDIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
                      topLevelButtons: [
                        {
                          buttonRenderer: {
                            style: "STYLE_DEFAULT",
                            size: "SIZE_DEFAULT",
                            isDisabled: false,
                            serviceEndpoint: {
                              clickTrackingParams:
                                "CLMBEKqJCRgPIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
                              commandMetadata: {
                                webCommandMetadata: {
                                  sendPost: true,
                                  apiUrl: "/youtubei/v1/feedback",
                                },
                              },
                              feedbackEndpoint: {
                                feedbackToken:
                                  "AB9zfpJSnrbvskPWkpziyGduKV-4gTxm30-eNNYDobzecpLq84dL6HwCxdX_zbvm_OmxSKdlsngHEE1CF7JKYGiyDVYV_Q7p9ihGCzOYcnqKcAJfNnSp-U-njcnKLgCWu_USr-2prW3x",
                                uiActions: {
                                  hideEnclosingContainer: true,
                                },
                                actions: [
                                  {
                                    clickTrackingParams:
                                      "CLMBEKqJCRgPIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
                                    replaceEnclosingAction: {
                                      item: {
                                        notificationMultiActionRenderer: {
                                          responseText: {
                                            runs: [
                                              {
                                                text: "Shelf will be hidden for ",
                                              },
                                              {
                                                text: "30",
                                              },
                                              {
                                                text: " days",
                                              },
                                            ],
                                          },
                                          buttons: [
                                            {
                                              buttonRenderer: {
                                                style: "STYLE_MONO_TONAL",
                                                text: {
                                                  runs: [
                                                    {
                                                      text: "Undo",
                                                    },
                                                  ],
                                                },
                                                serviceEndpoint: {
                                                  clickTrackingParams:
                                                    "CLUBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
                                                  commandMetadata: {
                                                    webCommandMetadata: {
                                                      sendPost: true,
                                                      apiUrl:
                                                        "/youtubei/v1/feedback",
                                                    },
                                                  },
                                                  undoFeedbackEndpoint: {
                                                    undoToken:
                                                      "AB9zfpK-nY3vxgYDkvJSkuFdbeBltD0r4XdLzoFqxz6OPnmJrroOAxKfUuDny8kPjB9yyWzwEerOZqe90BakCPEJXycRSrH8sZAdnlWpEs0n0lx6qOFERE6o5jkK3mgbcVCM-Al38oGV",
                                                    actions: [
                                                      {
                                                        clickTrackingParams:
                                                          "CLUBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
                                                        undoFeedbackAction: {
                                                          hack: true,
                                                        },
                                                      },
                                                    ],
                                                  },
                                                },
                                                trackingParams:
                                                  "CLUBEPBbGAAiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
                                              },
                                            },
                                          ],
                                          trackingParams:
                                            "CLQBEKW8ASITCKnqgMqO0IUDFc95TAgdpggNCQ==",
                                        },
                                      },
                                    },
                                  },
                                ],
                              },
                            },
                            icon: {
                              iconType: "DISMISSAL",
                            },
                            tooltip: "Not interested",
                            trackingParams:
                              "CLMBEKqJCRgPIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
                            accessibilityData: {
                              accessibilityData: {
                                label: "Not interested",
                              },
                            },
                          },
                        },
                      ],
                    },
                  },
                  items: [],
                  trackingParams: "CHYQ34MDIhMIqeqAyo7QhQMVz3lMCB2mCA0J",
                  icon: {
                    iconType: "YOUTUBE_SHORTS_BRAND_24",
                  },
                },
              },
              trackingParams: "CHUQ45cFGAEiEwip6oDKjtCFAxXPeUwIHaYIDQk=",
              fullBleed: false,
            },
          };
          item_path =
            "root.richSectionRenderer.content.reelShelfRenderer.items";
        }
        let shorts;
        while ((shorts = user_data.shorts_list.pop())) {
          const id = shorts["id"];
          const title = shorts["title"];
          const ago_str = shorts["ago_str"];
          const author = shorts["author_name"];
          const views_lable =
            shorts["views_lable"] +
            (author ? " · " + author : "") +
            (ago_str ? " · " + ago_str : "");
          const thumbnail_url = shorts["thumbnail_url"];
          let tmp_item;
          if (["yt_home", "yt_watch"].includes(page_type)) {
            tmp_item = {
              reelItemRenderer: {
                videoId: id,
                headline: {
                  simpleText: title,
                },
                thumbnail: {
                  thumbnails: [
                    {
                      url: thumbnail_url,
                      width: 405,
                      height: 720,
                    },
                  ],
                  isOriginalAspectRatio: true,
                },
                viewCountText: {
                  accessibility: {
                    accessibilityData: {
                      label: views_lable,
                    },
                  },
                  simpleText: views_lable,
                },
                navigationEndpoint: {
                  clickTrackingParams:
                    "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6mgEFCCUQ-B0=",
                  commandMetadata: {
                    webCommandMetadata: {
                      url: "/shorts/" + id,
                      webPageType: "WEB_PAGE_TYPE_SHORTS",
                      rootVe: 37414,
                    },
                  },
                  reelWatchEndpoint: {
                    videoId: id,
                    playerParams:
                      "8AEBoAMCyAMluAQGogYVAdXZ-jvMfGWnXiNDPh0oiMSTJMUn",
                    thumbnail: {
                      thumbnails: [
                        {
                          url: "https://i.ytimg.com/vi/" + id + "/frame0.jpg",
                          width: 1080,
                          height: 1920,
                        },
                      ],
                      isOriginalAspectRatio: true,
                    },
                    overlay: {
                      reelPlayerOverlayRenderer: {
                        style: "REEL_PLAYER_OVERLAY_STYLE_SHORTS",
                        trackingParams:
                          "CO4CELC1BCITCJyjr4WN0IUDFRnAlwgdOB0Eeg==",
                        reelPlayerNavigationModel:
                          "REEL_PLAYER_NAVIGATION_MODEL_UNSPECIFIED",
                      },
                    },
                    params: "CAYwAg%3D%3D",
                    sequenceProvider: "REEL_WATCH_SEQUENCE_PROVIDER_RPC",
                    sequenceParams: "CgtLRmRCbnpnSjJZWSoCGAZQGWgA",
                    loggingContext: {
                      vssLoggingContext: {
                        serializedContextData: "CgIIDA%3D%3D",
                      },
                      qoeLoggingContext: {
                        serializedContextData: "CgIIDA%3D%3D",
                      },
                    },
                    ustreamerConfig:
                      "CAwSHDFIakVXUytucVRyTENNWlgzMXdDZmYwamZQQ0U=",
                  },
                },
                menu: {
                  menuRenderer: {
                    items: [
                      {
                        menuServiceItemRenderer: {
                          text: {
                            runs: [
                              {
                                text: "Report",
                              },
                            ],
                          },
                          icon: {
                            iconType: "FLAG",
                          },
                          serviceEndpoint: {
                            clickTrackingParams:
                              "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                            commandMetadata: {
                              webCommandMetadata: {
                                sendPost: true,
                                apiUrl: "/youtubei/v1/flag/get_form",
                              },
                            },
                            getReportFormEndpoint: {
                              params: "EgtLRmRCbnpnSjJZWUABWABwAXgB2AEA6AEA",
                            },
                          },
                          trackingParams:
                            "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                        },
                      },
                      {
                        menuServiceItemRenderer: {
                          text: {
                            runs: [
                              {
                                text: "Not interested",
                              },
                            ],
                          },
                          icon: {
                            iconType: "NOT_INTERESTED",
                          },
                          serviceEndpoint: {
                            clickTrackingParams:
                              "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                            commandMetadata: {
                              webCommandMetadata: {
                                sendPost: true,
                                apiUrl: "/youtubei/v1/feedback",
                              },
                            },
                            feedbackEndpoint: {
                              feedbackToken:
                                "AB9zfpIBjY8nLioWtHjvUvMvrLXfhPMooShdpv91xgNNrZuxibAl6QyPeYMe7faEHcrSUm-TIqvLe2ThmYQpNRUy9rPbV1k3jjrvqqc5cOLBvnV8oN0Kbrq3-K9IjJXYitJPyOzJU0uy",
                              actions: [
                                {
                                  clickTrackingParams:
                                    "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                                  replaceEnclosingAction: {
                                    item: {
                                      notificationMultiActionRenderer: {
                                        responseText: {
                                          runs: [
                                            {
                                              text: "Video removed",
                                            },
                                          ],
                                        },
                                        buttons: [
                                          {
                                            buttonRenderer: {
                                              style: "STYLE_BLUE_TEXT",
                                              text: {
                                                runs: [
                                                  {
                                                    text: "Undo",
                                                  },
                                                ],
                                              },
                                              serviceEndpoint: {
                                                clickTrackingParams:
                                                  "CO0CEPBbGAAiEwico6-FjdCFAxUZwJcIHTgdBHo=",
                                                commandMetadata: {
                                                  webCommandMetadata: {
                                                    sendPost: true,
                                                    apiUrl:
                                                      "/youtubei/v1/feedback",
                                                  },
                                                },
                                                undoFeedbackEndpoint: {
                                                  undoToken:
                                                    "AB9zfpK74nsMbZ4OfNgKTgA9g0w3Q8o72jdm384D3y82OAuy2KgvTUOAn-iII915ZC_7aqAxTK-XNir21X_T3WQEeAzdy4hCZ6o0f12hfdHW8xI1js1WB_CEn3EW27P9_1vu5dw2kDeW",
                                                  actions: [
                                                    {
                                                      clickTrackingParams:
                                                        "CO0CEPBbGAAiEwico6-FjdCFAxUZwJcIHTgdBHo=",
                                                      undoFeedbackAction: {
                                                        hack: true,
                                                      },
                                                    },
                                                  ],
                                                },
                                              },
                                              trackingParams:
                                                "CO0CEPBbGAAiEwico6-FjdCFAxUZwJcIHTgdBHo=",
                                            },
                                          },
                                        ],
                                        trackingParams:
                                          "COwCEKW8ASITCJyjr4WN0IUDFRnAlwgdOB0Eeg==",
                                      },
                                    },
                                  },
                                },
                              ],
                            },
                          },
                          trackingParams:
                            "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                          accessibility: {
                            accessibilityData: {
                              label: "Not interested",
                            },
                          },
                        },
                      },
                      {
                        menuNavigationItemRenderer: {
                          text: {
                            runs: [
                              {
                                text: "Send feedback",
                              },
                            ],
                          },
                          icon: {
                            iconType: "FEEDBACK",
                          },
                          navigationEndpoint: {
                            clickTrackingParams:
                              "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                            commandMetadata: {
                              webCommandMetadata: {
                                ignoreNavigation: true,
                              },
                            },
                            userFeedbackEndpoint: {
                              additionalDatas: [
                                {
                                  userFeedbackEndpointProductSpecificValueData:
                                    {
                                      key: "video_id",
                                      value: id,
                                    },
                                },
                                {
                                  userFeedbackEndpointProductSpecificValueData:
                                    {
                                      key: "lockup",
                                      value: "shelf",
                                    },
                                },
                              ],
                            },
                          },
                          trackingParams:
                            "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                          accessibility: {
                            accessibilityData: {
                              label: "Send feedback",
                            },
                          },
                        },
                      },
                    ],
                    trackingParams: "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                    accessibility: {
                      accessibilityData: {
                        label: "More actions",
                      },
                    },
                  },
                },
                trackingParams:
                  "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6QIazp8Dzs9CrKA==",
                accessibility: {
                  accessibilityData: {
                    label: title + " - play Short",
                  },
                },
                style: "REEL_ITEM_STYLE_AVATAR_CIRCLE",
                dismissalInfo: {
                  feedbackToken:
                    "AB9zfpLIJd1aRU9JzdOjpgeJBW2QvHH79sx6dM6ZCDEzyc5qrISZBSpNRe5lerckNHwQ10BOwEQhlquLlHP-nkuA4VSSCXX0XgMJHBnKWBxlIXkQ1pLIUjd6cQKhrCUioDfix7xn5Ecj",
                },
                videoType: "REEL_VIDEO_TYPE_VIDEO",
                loggingDirectives: {
                  trackingParams: "COsCEIf2BBgAIhMInKOvhY3QhQMVGcCXCB04HQR6",
                  visibility: {
                    types: "12",
                  },
                  enableDisplayloggerExperiment: true,
                },
              },
            };
          }
          if (page_type == "yt_home") {
            tmp_item = {
              richItemRenderer: {
                content: tmp_item,
                trackingParams: "CJsFEJmNBRgAIhMI4YD80e7PhQMVZ2pMCB18XgT5",
              },
            };
          }
          if (["mobile_yt_home", "mobile_yt_watch"].includes(page_type)) {
            tmp_item = {
              shortsLockupViewModel: {
                entityId: "shorts-shelf-item-" + id,
                accessibilityText: title + ", " + views_lable + " - play Short",
                thumbnail: {
                  sources: [
                    {
                      url: thumbnail_url,
                      width: 405,
                      height: 720,
                    },
                  ],
                },
                onTap: {
                  innertubeCommand: {
                    clickTrackingParams:
                      "CK8BEIf2BBgAIhMIqeqAyo7QhQMVz3lMCB2mCA0JWg9GRXdoYXRfdG9fdG9wYXRjaJoBBQgkEI4e",
                    commandMetadata: {
                      webCommandMetadata: {
                        url: "/shorts/" + id,
                        webPageType: "WEB_PAGE_TYPE_SHORTS",
                        rootVe: 37414,
                      },
                    },
                    reelWatchEndpoint: {
                      videoId: id,
                    },
                  },
                },
                overlayMetadata: {
                  primaryText: {
                    content: title,
                  },
                  secondaryText: {
                    content: views_lable,
                  },
                },
              },
            };
          }
          items.push(tmp_item);
        }
        if (item_path) {
          eval(trustedScript(item_path + " = items"));
          user_data_api.set();
          return root;
        }
        return {};
      }
      get_shorts_info(video_id) {
        return new Promise((resolve, reject) => {
          let basic_url, author_id_reg, author_name_reg, ago_reg;
          if (page_type.startsWith("mobile")) {
            basic_url = "https://m.youtube.com/shorts/";
            author_id_reg = /"channelId":"(.*?)"/;
            author_name_reg = /"ownerChannelName":"(.*?)"/;
            ago_reg = /timestampText.*?:\\x22(.*?)\\x22\\x7d/;
          } else {
            basic_url = "https://www.youtube.com/shorts/";
            author_id_reg = /"browseId":"([a-zA-Z0-9\-_]+)","canonicalBaseUrl"/;
            author_name_reg = /"channel":\{"simpleText":"(.*?)"/;
            ago_reg = /"timestampText":{"simpleText":"(.*?)"}/;
          }
          const url = basic_url + video_id;
          const xhr = new XMLHttpRequest();
          xhr.open("GET", url);
          xhr.setRequestHeader(
            "accept",
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
          );
          let author_id = "";
          let author_name = "";
          let ago_str = "";
          xhr.onload = function () {
            if (xhr.status === 200) {
              let match;
              const result = xhr.responseText;
              match = result.match(author_id_reg);
              if (match && match.length > 1) author_id = match[1];
              match = result.match(author_name_reg);
              if (match && match.length > 1) author_name = match[1];
              match = result.match(ago_reg);
              if (match && match.length > 1) ago_str = match[1];
              resolve({
                id: video_id,
                author_id: author_id,
                author_name: author_name,
                ago_str: ago_str,
              });
            } else {
              reject(xhr.responseText);
            }
          };
          xhr.onerror = function () {
            reject(new Error("XHR request failed"));
          };
          xhr.send();
        });
      }
      parse_shorts_list() {
        if (!this.shorts_list.length) return;
        const { id, title, views_lable, thumbnail_url } =
          this.shorts_list.pop();
        this.get_shorts_info(id)
          .then((author_info) => {
            const { author_id, author_name, ago_str } = author_info;
            if (author_id && user_data.channel_infos.ids.includes(author_id)) {
              if (
                user_data.shorts_list.some((value) => {
                  return value.id === id;
                })
              ) {
                log(
                  "Already exists from " + author_name + ": " + title,
                  "shorts",
                );
              } else {
                log(
                  "Not filtering " + author_name + "'s short: " + title,
                  "shorts",
                );
                const shorts_info = {
                  id: id,
                  title: title,
                  author_id: author_id,
                  author_name: author_name,
                  views_lable: views_lable,
                  from: page_type,
                  thumbnail_url: thumbnail_url,
                  ago_str: ago_str,
                };
                user_data.shorts_list.push(shorts_info);
                user_data_api.set();
              }
            } else {
              log("Filtering " + author_name + "'s short: " + title, "shorts");
            }
          })
          .finally(() => {
            if (this.shorts_list.length > 0)
              setTimeout(() => {
                this.parse_shorts_list();
              }, shorts_parse_delay);
            else this.parsing = false;
          });
      }
      check_shorts_exist() {
        const short_id = href.split("/").pop();
        for (let i = 0; i < user_data.shorts_list.length; i++) {
          if (user_data.shorts_list[i].id === short_id) {
            user_data.shorts_list.splice(i, 1);
            user_data_api.set();
            return;
          }
        }
      }
      get_interval_tag(upload_date_str) {
        if (!upload_date_str) return "";
        const uploadDate = new Date(upload_date_str);
        const currentDate = new Date();
        const timeDifference = Math.abs(currentDate - uploadDate);
        const secondsDifference = timeDifference / 1000;
        const minutesDifference = secondsDifference / 60;
        const hoursDifference = minutesDifference / 60;
        const daysDifference = hoursDifference / 24;
        const weeksDifference = daysDifference / 7;
        const monthsDifference = weeksDifference / 4.345;
        const yearsDifference = monthsDifference / 12;
        if (secondsDifference < 60) {
          return `${Math.floor(secondsDifference)} seconds ago`;
        } else if (minutesDifference < 60) {
          return `${Math.floor(minutesDifference)} minutes ago`;
        } else if (hoursDifference < 24) {
          return `${Math.floor(hoursDifference)} hours ago`;
        } else if (daysDifference < 7) {
          return `${Math.floor(daysDifference)} days ago`;
        } else if (weeksDifference < 4.345) {
          return `${Math.floor(weeksDifference)} weeks ago`;
        } else if (monthsDifference < 12) {
          return `${Math.floor(monthsDifference)} months ago`;
        } else {
          return `${Math.floor(yearsDifference)} years ago`;
        }
      }
    }
    return new ShortsFun();
  }

  function get_yt_api() {
    return {
      get_subscribe_data: function (retry = 0) {
        const headers = {
          authority: "www.youtube.com",
          accept:
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        };
        const url = "https://www.youtube.com/feed/channels";
        const requestConfig = {
          method: "GET",
          headers: headers,
          url: url,
        };
        const save_this = this;
        GM_xmlhttpRequest({
          ...requestConfig,
          onload: function (response) {
            const tmp_channel_names = [];
            const tmp_channel_ids = [];
            const regex = /var ytInitialData \= (.*?);\<\/script\>/;
            try {
              const match = response.responseText.match(regex);
              const ytInitialData_obj = JSON.parse(match[1]);
              const items =
                ytInitialData_obj.contents.twoColumnBrowseResultsRenderer
                  .tabs[0].tabRenderer.content.sectionListRenderer.contents[0]
                  .itemSectionRenderer.contents[0].shelfRenderer.content
                  .expandedShelfContentsRenderer.items;
              for (let item of items) {
                const channel_name = item.channelRenderer.title.simpleText;
                const match_channel_id = item.channelRenderer.channelId;
                tmp_channel_ids.push(match_channel_id);
                tmp_channel_names.push(channel_name);
              }
              if (tmp_channel_ids.length > 0) {
                user_data.channel_infos.ids = tmp_channel_ids;
                user_data.channel_infos.names = tmp_channel_names;
                user_data_api.set();
              }
              log(
                "Fetched subscription list successfully: " +
                  user_data.channel_infos.ids.length +
                  " channels",
                0,
              );
            } catch (error) {
              if (retry < 3) {
                setTimeout(() => {
                  save_this.get_subscribe_data(retry + 1);
                }, 1000);
              }
              log("Failed to fetch subscription list\n", error, -1);
            }
          },
          onerror: function (error) {
            if (retry < 3) {
              setTimeout(() => {
                save_this.get_subscribe_data(retry + 1);
              }, 1000);
            }
            log("Failed to fetch subscription list\n", error, -1);
          },
        });
      },
      get_authorization: function () {
        function Vja() {
          function a() {
            e[0] = 1732584193;
            e[1] = 4023233417;
            e[2] = 2562383102;
            e[3] = 271733878;
            e[4] = 3285377520;
            u = q = 0;
          }
          function b(x) {
            for (var y = l, C = 0; 64 > C; C += 4)
              y[C / 4] =
                (x[C] << 24) | (x[C + 1] << 16) | (x[C + 2] << 8) | x[C + 3];
            for (C = 16; 80 > C; C++)
              ((x = y[C - 3] ^ y[C - 8] ^ y[C - 14] ^ y[C - 16]),
                (y[C] = ((x << 1) | (x >>> 31)) & 4294967295));
            x = e[0];
            var E = e[1],
              H = e[2],
              R = e[3],
              T = e[4];
            for (C = 0; 80 > C; C++) {
              if (40 > C) {
                if (20 > C) {
                  var X = R ^ (E & (H ^ R));
                  var la = 1518500249;
                } else ((X = E ^ H ^ R), (la = 1859775393));
              } else
                60 > C
                  ? ((X = (E & H) | (R & (E | H))), (la = 2400959708))
                  : ((X = E ^ H ^ R), (la = 3395469782));
              X =
                ((((x << 5) | (x >>> 27)) & 4294967295) + X + T + la + y[C]) &
                4294967295;
              T = R;
              R = H;
              H = ((E << 30) | (E >>> 2)) & 4294967295;
              E = x;
              x = X;
            }
            e[0] = (e[0] + x) & 4294967295;
            e[1] = (e[1] + E) & 4294967295;
            e[2] = (e[2] + H) & 4294967295;
            e[3] = (e[3] + R) & 4294967295;
            e[4] = (e[4] + T) & 4294967295;
            u = q = 0;
          }
          function c(x, y) {
            if ("string" === typeof x) {
              x = unescape(encodeURIComponent(x));
              for (var C = [], E = 0, H = x.length; E < H; ++E)
                C.push(x.charCodeAt(E));
              x = C;
            }
            y || (y = x.length);
            C = 0;
            if (0 == q)
              for (; C + 64 < y; )
                (b(x.slice(C, C + 64)), (C += 64), (u += 64));
            for (; C < y; )
              if (((h[q++] = x[C++]), u++, 64 == q))
                for (q = 0, b(h); C + 64 < y; )
                  (b(x.slice(C, C + 64)), (C += 64), (u += 64));
          }
          function d() {
            var x = [],
              y = 8 * u;
            56 > q ? c(m, 56 - q) : c(m, 64 - (q - 56));
            for (var C = 63; 56 <= C; C--) ((h[C] = y & 255), (y >>>= 8));
            b(h);
            for (C = y = 0; 5 > C; C++)
              for (var E = 24; 0 <= E; E -= 8) x[y++] = (e[C] >> E) & 255;
            return x;
          }
          for (var e = [], h = [], l = [], m = [128], p = 1; 64 > p; ++p)
            m[p] = 0;
          var q, u;
          a();
          return {
            reset: a,
            update: c,
            digest: d,
            digestString: function () {
              for (var x = d(), y = "", C = 0; C < x.length; C++)
                y +=
                  "0123456789ABCDEF".charAt(Math.floor(x[C] / 16)) +
                  "0123456789ABCDEF".charAt(x[C] % 16);
              return y;
            },
          };
        }
        const sapisid_cookie =
          getCookie("SAPISID") ||
          getCookie("APISID") ||
          getCookie("__Secure-3PAPISID");
        if (sapisid_cookie) {
          const timestamp = Math.floor(Date.now() / 1000);
          const b = Vja();
          b.update(
            timestamp + " " + sapisid_cookie + " https://www.youtube.com",
          );
          const hash_value = b.digestString().toLowerCase();
          return "SAPISIDHASH " + timestamp + "_" + hash_value;
        }
        return "";
      },
      get_channel_id: function (retry = 0) {
        const authorization = this.get_authorization();
        if (!authorization) {
          log("Failed to get authorization", 0);
          return;
        }
        const url = "https://www.youtube.com/youtubei/v1/account/account_menu";
        const params = {
          prettyPrint: "false",
        };
        const data = {
          context: {
            client: {
              clientName: "WEB",
              clientVersion: "2.20240308.00.00",
            },
          },
        };
        const jsonData = JSON.stringify(data);
        const headers = {
          authorization: authorization,
          "content-type": "application/json",
          origin: "https://www.youtube.com",
          referer: "https0://www.youtube.com/",
        };
        const requestConfig = {
          method: "POST",
          headers: headers,
          data: jsonData,
          url: url + "?" + new URLSearchParams(params),
        };

        GM_xmlhttpRequest({
          ...requestConfig,
          onload: function (response) {
            const match = response.responseText.match(/"browseId"\:"(.*?)"/);
            if (match && match.length > 1) {
              const tmp_id = match[1];
              if (tmp_id && tmp_id != channel_id) {
                channel_id = tmp_id;
                user_data = user_data_api.get();
                GM_setValue("last_channel_id", channel_id);
              }
              log("Successfully obtained channel_id " + channel_id, 0);
            } else {
              if (retry < 3) {
                setTimeout(() => {
                  yt_api.get_channel_id(retry + 1);
                }, 500);
              } else {
                log(
                  "Failed to get channel_id",
                  response,
                  response.responseText,
                  -1,
                );
              }
            }
          },
          onerror: function (error) {
            if (retry < 3) {
              setTimeout(() => {
                yt_api.get_channel_id(retry + 1);
              }, 500);
              yt_api.get_channel_id(retry + 1);
            } else {
              log("Failed to get channel_id", error, 0);
            }
          },
        });
      },
    };
  }

  function get_user_data_api() {
    return {
      get() {
        const default_user_data = {
          open_recommend_shorts: "off",
          open_recommend_movie: "off",
          open_recommend_popular: "off",
          open_recommend_liveroom: "on",
          open_recommend_playables: "off",
          add_shorts_upload_date: "on",
          shorts_change_author_name: "on",
          short_buy_super_thanks: "off",
          shorts_auto_scroll: "off",
          shorts_add_video_progress: "onTap",
          shorts_dbclick_like: "off",
          shorts_disable_loop_play: "on",
          sponsorblock: "on",
          duplicate_song_prevention: "off",
          disable_play_on_hover: "off",
          default_quality: "hd1080",
          default_speed: "1",
          restore_related_sidebar_layout: "on",
          language: "en",
          channel_infos: {
            ids: [],
            names: [],
          },
          shorts_list: [],
          watch_page_config: {
            shop_banner: "off",
            hide_live_chat: "on",
            hide_live_chat_replay: "on",
          },
          global_shorts_block: "on",
          hide_share_button: "off",
          hide_thanks_button: "off",
          hide_clip_button: "off",
          hide_more_actions_button: "off",
          hide_save_button: "off",
          hide_subscribe_button: "off",
          hide_like_bar: "off",
          hide_join_button: "off",
          hide_ask_button: "off",
          hide_download_button: "off",
          hide_end_cards: "off",
          hide_fullscreen_controls: "off",
          hide_ai_summary: "off",
          hide_microphone_icon: "off",
          hide_paid_promotion: "off",
          disable_saturated_hover: "off",
          old_player_ui: "off",
          restore_red_progress_bar: "on",
          search_thumbnail_small: "on",
          dark_mode: "auto",
          popup_positions: {},
          sb_categories: {
            sponsor: "on",
            intro: "on",
            outro: "on",
            selfpromo: "on",
            interaction: "on",
            music_offtopic: "on",
          },
          login: false,
        };
        let diff = false;
        user_data_listener.set();
        let tmp_user_data = GM_getValue(channel_id);
        if (!tmp_user_data) {
          tmp_user_data = default_user_data;
          diff = true;
        }
        for (let key in default_user_data) {
          if (!(key in tmp_user_data)) {
            diff = true;
            tmp_user_data[key] = default_user_data[key];
          }
        }
        const tmp_login = channel_id !== "default";
        if (tmp_user_data.login !== tmp_login) {
          diff = true;
          tmp_user_data.login = tmp_login;
        }
        (diff || this.update(tmp_user_data)) &&
          GM_setValue(channel_id, tmp_user_data);
        return tmp_user_data;
      },
      set() {
        return GM_setValue(channel_id, user_data);
      },
      reset() {
        if (!confirm(flag_info.del_config_confirm_tips)) return;
        const keys = GM_listValues();
        for (let key of keys) {
          GM_deleteValue(key);
        }
        unsafeWindow.document.location.reload();
      },
      update(tmp_user_data) {
        let diff = false;
        const last_version = GM_getValue("last_version", -1);
        if (last_version === -1 && !tmp_user_data.open_recommend_shorts) {
          tmp_user_data.open_recommend_shorts = GM_getValue(
            "open_recommend_shorts",
            "on",
          );
          tmp_user_data.open_recommend_movie = GM_getValue(
            "open_recommend_movie",
            "on",
          );
          tmp_user_data.open_recommend_popular = GM_getValue(
            "open_recommend_popular",
            "on",
          );
          tmp_user_data.open_recommend_liveroom = GM_getValue(
            "open_recommend_liveroom",
            "on",
          );
          diff = true;
        }
        if (typeof tmp_user_data.open_recommend_shorts === "boolean") {
          tmp_user_data.open_recommend_shorts =
            tmp_user_data.open_recommend_shorts ? "on" : "off";
          tmp_user_data.open_recommend_movie =
            tmp_user_data.open_recommend_movie ? "on" : "off";
          tmp_user_data.open_recommend_popular =
            tmp_user_data.open_recommend_popular ? "on" : "off";
          tmp_user_data.open_recommend_liveroom =
            tmp_user_data.open_recommend_liveroom ? "on" : "off";
          diff = true;
        }
        last_version !== GM_info.script.version &&
          GM_setValue("last_version", GM_info.script.version);

        if (!tmp_user_data.watch_page_config) {
          tmp_user_data.watch_page_config = {
            shop_banner: "off",
            hide_live_chat: "off",
            hide_live_chat_replay: "off",
          };
          diff = true;
        } else {
          if (tmp_user_data.watch_page_config.hide_live_chat === undefined) {
            tmp_user_data.watch_page_config.hide_live_chat = "off";
            diff = true;
          }
          if (
            tmp_user_data.watch_page_config.hide_live_chat_replay === undefined
          ) {
            tmp_user_data.watch_page_config.hide_live_chat_replay = "off";
            diff = true;
          }
        }

        const newHideKeys = [
          "hide_share_button",
          "hide_thanks_button",
          "hide_clip_button",
          "hide_more_actions_button",
          "hide_save_button",
          "hide_subscribe_button",
          "hide_like_bar",
          "hide_join_button",
          "hide_ask_button",
          "hide_download_button",
          "global_shorts_block",
          "dark_mode",
          "old_player_ui",
        ];
        for (const key of newHideKeys) {
          if (key === "dark_mode" && tmp_user_data[key] === undefined) {
            tmp_user_data[key] = "auto";
            diff = true;
          } else if (key !== "dark_mode" && tmp_user_data[key] === undefined) {
            tmp_user_data[key] = "off";
            diff = true;
          }
        }

        if (!tmp_user_data.popup_positions) {
          tmp_user_data.popup_positions = {};
          diff = true;
        }

        if (!tmp_user_data.sb_categories) {
          tmp_user_data.sb_categories = {
            sponsor: "on",
            intro: "on",
            outro: "on",
            selfpromo: "on",
            interaction: "on",
            music_offtopic: "on",
          };
          diff = true;
        } else {
          const defaults = ["sponsor", "intro", "outro", "selfpromo", "interaction", "music_offtopic"];
          for (const cat of defaults) {
            if (tmp_user_data.sb_categories[cat] === undefined) {
              tmp_user_data.sb_categories[cat] = "on";
              diff = true;
            }
          }
        }

        return diff;
      },
    };
  }

  function get_data_process() {
    class DATA_PROCESS {
      constructor() {
        this.limit_eval = false;
        this.obj_filter;
        this.obj_storage = {};
      }
      condition_split_and_tag = "&&";
      condition_split_or_tag = "||";
      value_split_and_tag = "&";
      value_split_or_tag = "|";

      storage_obj(key, obj) {
        this.obj_storage[key] = obj;
      }

      set_obj_filter(obj_filter) {
        if (typeof obj_filter !== "function") return;
        this.obj_filter = function () {
          try {
            obj_filter.apply(this, arguments);
          } catch (error) {
            log("obj_filter error", error, -1);
            return false;
          }
        };
      }

      text_process(data, values, mode, traverse_all) {
        if (!values) return data;
        const origin_data = data;
        try {
          mode = mode || "cover";
          if (mode === "reg") {
            for (let value of values) {
              const patten_express = value.split(SPLIT_TAG)[0];
              const replace_value = value.split(SPLIT_TAG)[1];
              const patten = new RegExp(patten_express, "g");
              data = data.replace(patten, replace_value);
            }
          }
          if (mode === "cover") {
            data = values[0];
          }
          if (mode === "insert") {
            traverse_all = traverse_all || false;
            let json_data;
            try {
              json_data = JSON.parse(data);
            } catch (error) {
              log("text_process JSON parse error", -1);
              return data;
            }
            this.obj_process(json_data, values, traverse_all);
            data = JSON.stringify(json_data);
          }
        } catch (error) {
          log("text_process error", error, -1);
          data = origin_data;
        }
        return data;
      }

      get_relative_path(basic_path, relative_path) {
        if (relative_path === "/") return basic_path;
        let real_path;
        if (relative_path.startsWith("/.")) {
          real_path = basic_path + relative_path.slice(1);
        }
        if (relative_path.startsWith(".")) {
          const reg = /[\.\[]/g;
          const positions = [];
          let match;
          while ((match = reg.exec(basic_path)) !== null) {
            positions.push(match.index);
          }
          if (positions.length === 0) {
            return basic_path;
          }
          const pointer_match = relative_path.match(/^\.+/);
          const split_index =
            positions[positions.length - pointer_match[0].length];
          const relative_attribute = relative_path.slice(
            pointer_match[0].length,
          );
          real_path =
            basic_path.slice(0, split_index) +
            (relative_attribute
              ? (relative_attribute.startsWith("[") ? "" : ".") +
                relative_attribute
              : "");
        }
        return this.convertPathToBracketNotation(real_path);
      }

      value_parse(parse_value, path_info = null, json_obj = null) {
        const formula_match = parse_value.match(/\{.*?\}/g);
        if (formula_match) {
          for (let express_ of formula_match) {
            const express = express_.slice(1, -1);
            if (!express) continue;
            parse_value = parse_value.replace(
              express_,
              this.value_parse(express, path_info, json_obj),
            );
          }
        }
        const json_math = parse_value.match(/^json\((.*)\)$/);
        if (json_math) return JSON.parse(json_math[1]);
        const obj_match = parse_value.match(/^obj\((.*)\)$/);
        if (obj_match) return this.string_to_value(unsafeWindow, obj_match[1]);
        const storage_obj_match = parse_value.match(/^sobj\((.*)\)$/);
        if (storage_obj_match)
          return this.string_to_value(this.obj_storage, storage_obj_match[1]);
        const number_match = parse_value.match(/^num\((.*)\)$/);
        if (number_match) return Number(number_match[1]);
        const method_match = parse_value.match(/^method\((.*)\)$/);
        if (method_match) {
          if (this.limit_eval) {
            const method_info = method_match[1].match(/(.*?)\((.*)\)$/);
            const method_name = method_info[1];
            const method_args_string = method_info[2];
            const method_args = method_args_string.split(",");
            const args = [];
            for (let arg of method_args) {
              args.push(this.value_parse(arg, path_info, json_obj));
            }
            return unsafeWindow[method_name](...args);
          }
          return eval(trustedScript(method_match[1]));
        }
        const deal_obj_match = parse_value.match(/^dealObj\((.*)\)$/);
        if (deal_obj_match) {
          const path_msg = deal_obj_match[1];
          return this.string_to_value(
            json_obj.this.get_relative_path(path_info.deal_path, path_msg),
          );
        }
        const path_obj_match = parse_value.match(/^pathObj\((.*)\)$/);
        if (path_obj_match) {
          const path_msg = path_obj_match[1];
          return this.string_to_value(
            json_obj,
            this.get_relative_path(path_info.path, path_msg),
          );
        }
        const abs_obj_match = parse_value.match(/^absObj\((.*)\)$/);
        if (abs_obj_match) {
          const abs_path = abs_obj_match[1];
          return this.string_to_value(json_obj, abs_path);
        }
        const string_match = parse_value.match(/^["'](.*)["']$/);
        if (string_match) return string_match[1];
        if (parse_value === "undefined") return undefined;
        if (parse_value === "null") return null;
        return parse_value;
      }

      string_to_value(obj, path) {
        try {
          if (!this.limit_eval) {
            return eval(trustedScript(path.replace("json_obj", "obj")));
          }
          let tmp_obj = obj;
          let matches = path.match(/\[(.*?)\]/g);
          if (matches) {
            matches.map((match) => {
              if (match.includes('["')) {
                tmp_obj = Reflect.get(tmp_obj, match.replace(/\["|"\]/g, ""));
              } else {
                tmp_obj = Reflect.get(
                  tmp_obj,
                  Number(match.replace(/\[|\]/g, "")),
                );
              }
            });
            return tmp_obj;
          }
          matches = path.split(".");
          if (matches) {
            matches.splice(0, 1);
            matches.map((match) => {
              tmp_obj = Reflect.get(tmp_obj, match);
            });
            return tmp_obj;
          }
        } catch (error) {
          return null;
        }
      }

      get_lastPath_and_key(path) {
        let last_path, last_key;
        let matches = path.match(/\[(.*?)\]/g);
        if (matches && matches.length > 0) {
          const tmp = matches[matches.length - 1];
          if (tmp.includes('["')) {
            last_key = tmp.replace(/\["|"\]/g, "");
          } else {
            last_key = Number(tmp.replace(/\[|\]/g, ""));
          }
          last_path = path.substring(0, path.lastIndexOf(tmp));
        }
        if (!matches) {
          matches = path.split(".");
          if (matches && matches.length > 0) {
            last_key = matches[matches.length - 1];
            last_path = path.replace("." + last_key, "");
          }
        }
        return [last_path, last_key];
      }

      convertPathToBracketNotation(path) {
        if (!path) return "";
        return path.replace(/\.[\d\w\-\_\$@]+/g, function (match) {
          return '["' + match.slice(1) + '"]';
        });
      }

      paths_sort(paths_arr, key_name = null, reverse = false) {
        if (!Array.isArray(paths_arr)) {
          throw new Error("paths_arr must be an array");
        }
        if (paths_arr.length === 0) return;
        let tmp_paths_arr = paths_arr;
        if (!key_name) {
          key_name = "path";
          if (typeof paths_arr[0] !== "string")
            throw new Error("paths_arr must be a string array");
          tmp_paths_arr = [];
          paths_arr.forEach((path) => {
            tmp_paths_arr.push({
              path: path,
            });
          });
        }
        const reverse_factor = reverse ? -1 : 1;
        tmp_paths_arr.sort((a, b) => {
          function get_sort_key(obj) {
            if (!obj.sort_keys) {
              const reg = /\["?(.*?)"?\]/g;
              let matches = [];
              let match;
              while ((match = reg.exec(obj[key_name]))) {
                if (!match[0].startsWith('["')) {
                  if (isNaN(match[1]))
                    throw new Error("array index must be a number");
                  match[1] = parseInt(match[1]);
                }
                matches.push(match[1]);
              }
              obj.sort_keys = matches;
            }
          }
          if (a[key_name] === b[key_name]) return 0;
          get_sort_key(a);
          get_sort_key(b);
          const a_sort_keys = a.sort_keys;
          const b_sort_keys = b.sort_keys;
          if (a_sort_keys.length !== b_sort_keys.length) {
            return (b_sort_keys.length - a_sort_keys.length) * reverse_factor;
          }
          for (let i = 0; i < a_sort_keys.length; i++) {
            if (a_sort_keys[i] !== b_sort_keys[i]) {
              return (
                (b_sort_keys[i] > a_sort_keys[i] ? 1 : -1) * reverse_factor
              );
            }
          }
          return 0;
        });
        if (paths_arr !== tmp_paths_arr) {
          paths_arr.length = 0;
          tmp_paths_arr.forEach((path_info) => {
            paths_arr.push(path_info.path);
          });
        }
      }

      obj_process(json_obj, express_list, traverse_all = false) {
        if (typeof json_obj !== "object") {
          log("obj_process target is not an object", express_list, -1);
          return;
        }
        if (typeof express_list === "function") {
          try {
            express_list = express_list(json_obj);
            if (
              !express_list ||
              (Array.isArray(express_list) && express_list.length === 0)
            )
              return;
          } catch (error) {
            log("obj_process express_list function execution error", error, -1);
            return;
          }
        }
        const data_this = this;
        const abs_path_info_list = [];
        const relative_path_info_list = [];
        const relative_path_list = [];
        const relative_short_path_list = [];
        if (!json_obj || !express_list) return;
        const is_array_obj = Array.isArray(json_obj);
        try {
          express_list.forEach((express) => {
            if (!express) return;
            let reg;
            const express_type = typeof express;
            let matches;
            let conditions;
            reg =
              /^(abs:)?(.*?)(=\-|~=|=\+|=)(\(?([^ ][\s\S]*?)\)?)?( ([\s\S]*))?$/;
            if (express_type === "string") {
              matches = express.match(reg);
            } else {
              matches = express.value.match(reg);
              conditions = express.conditions;
            }
            const abs = matches[1];
            let path = matches[2];
            const operator = matches[3];
            let value = matches[4];
            const condition = matches[7];
            const path_extral_match = path.match(/\/\..*$|\.+$|\.\(.*$/);
            let path_extral;
            if (path_extral_match) {
              path_extral = path_extral_match[0];
              path = path.replace(path_extral, "");
            }
            let value_mode;
            if (express_type === "string") {
              const mode_match = value?.match(/^\((.*)\)$/);
              if (mode_match) {
                const mode_info = mode_match[1].split(",");
                value = mode_info[1];
                const mode = mode_info[0];
                mode_info.shift();
                mode_info.shift();
                value_mode = {
                  mode: mode,
                  params: mode_info,
                };
              }
              if (condition) {
                const tmp_conditions = condition
                  ? condition.split(this.condition_split_and_tag)
                  : [];
                conditions = {};
                for (let index = 0; index < tmp_conditions.length; index++) {
                  conditions["value" + index] = tmp_conditions[index].split(
                    this.condition_split_or_tag,
                  );
                }
              }
            }
            matches = path.match(/\[([\*\d\-,]*)\]$/);
            let array_index;
            if (matches) {
              path = path.replace(/\[([\*\d\-,]*)\]$/, "");
              array_index = matches[1];
            }
            if (abs) {
              add_data_to_abs_path({
                path: `json_obj${is_array_obj ? "" : "."}` + path,
                express: express,
                relative_path: path,
                operator: operator,
                value: value,
                condition: conditions,
                array_index: array_index,
                path_extral: path_extral,
                value_mode: value_mode,
              });
            } else {
              relative_path_list.push(path);
              const tmp_short_path = path.split(".").pop();
              relative_short_path_list.push(tmp_short_path);
              relative_path_info_list.push({
                express: express,
                path: path,
                operator: operator,
                value: value,
                value_mode: value_mode,
                conditions: conditions,
                array_index: array_index,
                path_extral: path_extral,
              });
            }
          });
          if (relative_path_list.length > 0) {
            const dec_list = [];
            const dec_index_list = [];
            obj_property_traverse(
              json_obj,
              "",
              {
                short_keys: relative_short_path_list,
                real_keys: relative_path_list,
              },
              dec_list,
              dec_index_list,
              traverse_all,
            );
            for (let i = 0; i < dec_index_list.length; i++) {
              const real_index = dec_index_list[i];
              const real_path_info = relative_path_info_list[real_index];
              const tmp_path = "json_obj" + dec_list[i];
              add_data_to_abs_path({
                path: tmp_path,
                express: real_path_info.express,
                relative_path: real_path_info.path,
                operator: real_path_info.operator,
                value: real_path_info.value,
                condition: real_path_info.conditions,
                array_index: real_path_info.array_index,
                path_extral: real_path_info.path_extral,
                value_mode: real_path_info.value_mode,
              });
            }
          }
          try {
            this.paths_sort(abs_path_info_list, "deal_path");
          } catch (error) {
            abs_path_info_list.sort((a, b) => (a < b ? 1 : -1));
          }
          for (let path_info of abs_path_info_list) {
            if (!this.obj_conditional(path_info, json_obj)) continue;
            if (this.obj_filter && this.obj_filter(path_info, json_obj))
              continue;
            obj_modify(json_obj, path_info);
          }
        } catch (error) {
          log("obj_process processing failed", error, -1);
        }

        function add_data_to_abs_path(params) {
          let {
            path,
            express,
            relative_path,
            operator,
            value,
            condition,
            array_index,
            path_extral,
            value_mode,
          } = params;
          let tmp;
          path = data_this.convertPathToBracketNotation(path);
          if (array_index === undefined) {
            tmp = {};
            path = path;
            tmp.path = path;
            tmp.relative_path = relative_path;
            tmp.operator = operator;
            tmp.value = value;
            tmp.value_mode = value_mode;
            tmp.condition = condition;
            tmp.path_extral = path_extral;
            tmp.express = express;
            add_path(tmp);
            return;
          }
          let array_index_list = [];
          if (array_index === "*") {
            let array_length;
            try {
              array_length =
                data_this.string_to_value(json_obj, path)?.length || 0;
              if (!array_length) return;
            } catch (error) {
              log(
                "obj_process failed to get array length --->" + path,
                error,
                -1,
              );
              return;
            }
            array_index_list = Array.from(
              { length: array_length },
              (_, i) => i,
            );
          } else if (array_index.includes(",")) {
            let is_error = false;
            array_index_list = array_index.split(",").map((item) => {
              if (is_error) return;
              if (isNaN(item)) {
                is_error = true;
                return;
              }
              return Number(item);
            });
            if (is_error) {
              return log(
                "obj_process array index format error --->" + path,
                -1,
              );
            }
          } else if (array_index.includes("-")) {
            const index_arr = array_index.split("-");
            if (index_arr.length !== 2)
              return log(
                "obj_process array index format error --->" + path,
                -1,
              );
            const start = Number(index_arr[0]);
            const end = Number(index_arr[1]);
            if (isNaN(start) || isNaN(end)) {
              return log(
                "obj_process array index format error --->" + path,
                -1,
              );
            }
            array_index_list = Array.from(
              { length: end - start + 1 },
              (_, i) => start + i,
            );
          } else if (!isNaN(array_index)) {
            array_index_list = [array_index];
          } else {
            return log("obj_process array index format error --->" + path, -1);
          }
          for (
            let tmp_index = array_index_list.length - 1;
            tmp_index >= 0;
            tmp_index--
          ) {
            tmp = {};
            tmp.path = path + "[" + array_index_list[tmp_index] + "]";
            tmp.operator = operator;
            tmp.value = value;
            tmp.value_mode = value_mode;
            tmp.condition = condition;
            tmp.path_extral = path_extral;
            tmp.relative_path = relative_path;
            tmp.express = express;
            add_path(tmp);
          }
          function add_path(path_info) {
            path_info.deal_path = path_extral
              ? data_this.get_relative_path(path, path_extral)
              : path_info.path;
            abs_path_info_list.push(path_info);
          }
        }

        function obj_property_traverse(
          obj,
          cur_path,
          dec_infos,
          dec_list,
          dec_index_list,
          traverse_all = false,
        ) {
          if (Array.isArray(obj)) {
            obj.forEach((tmp_obj, index) => {
              const tmp_path = cur_path + "[" + index + "]";
              if (!tmp_obj || typeof tmp_obj !== "object") return;
              obj_property_traverse(
                tmp_obj,
                tmp_path,
                dec_infos,
                dec_list,
                dec_index_list,
                traverse_all,
              );
            });
            return;
          }
          Object.keys(obj).forEach((key) => {
            const tmp_path = cur_path + "." + key;
            let deal = false;
            for (let i = 0; i < dec_infos["short_keys"].length; i++) {
              if (dec_infos["short_keys"][i] === key) {
                const len = dec_infos["real_keys"][i].length;
                if (
                  tmp_path.slice(tmp_path.length - len) ===
                  dec_infos["real_keys"][i]
                ) {
                  dec_list.push(tmp_path);
                  dec_index_list.push(i);
                  if (!deal && traverse_all && typeof obj[key] === "object") {
                    obj_property_traverse(
                      obj[key],
                      tmp_path,
                      dec_infos,
                      dec_list,
                      dec_index_list,
                      traverse_all,
                    );
                  }
                  deal = true;
                }
              }
            }
            const value = obj[key];
            if (deal || !value || typeof value !== "object") return;
            obj_property_traverse(
              value,
              tmp_path,
              dec_infos,
              dec_list,
              dec_index_list,
              traverse_all,
            );
          });
        }

        function obj_modify(json_obj, path_info) {
          const path = path_info["deal_path"];
          const operator = path_info["operator"];
          let value = path_info["value"];
          const [last_path, last_key] = data_this.get_lastPath_and_key(path);
          const last_obj = data_this.string_to_value(json_obj, last_path);
          if (!last_obj) {
            debugger;
            return log(
              "obj_modify failed, object not found --->" + path_info,
              -1,
            );
          }
          if (operator === "=-") {
            const is_array = typeof last_key === "number";
            if (is_array) last_obj.splice(last_key, 1);
            else delete last_obj[last_key];
            log("Based on: " + path_info.express, "obj_process");
            log("Deleted property -->" + path, "obj_process");
            return;
          }
          if (operator === "=") {
            value = data_this.value_parse(value, path_info, json_obj);
            last_obj[last_key] = value;
            log("Based on: " + path_info.express, "obj_process");
            log("Modified property -->" + path, "obj_process");
          }
          const dec_obj = last_obj[last_key];
          if (!dec_obj) {
            return log(
              "obj_modify failed, object not found --->" + path_info,
              -1,
            );
          }
          if (operator === "=+") {
            value = data_this.value_parse(value, path_info, json_obj);
            if (dec_obj === null || dec_obj === undefined)
              throw new Error("dec_obj is null");
            let type_ = typeof dec_obj;
            if (Array.isArray(dec_obj)) type_ = "array";
            if (type_ === "array") {
              const mode_info = path_info.value_mode;
              if (mode_info) {
                try {
                  mode_info.mode === "arr_insert" &&
                    last_obj[last_key].splice(
                      Number(mode_info.params[0]),
                      0,
                      value,
                    );
                } catch (error) {
                  log(error, -1);
                }
              } else {
                last_obj[last_key].push(value);
              }
            }
            if (type_ === "string" || type_ === "number")
              last_obj[last_key] = last_obj[last_key] + value;
            log("Based on: " + path_info.express, "obj_process");
            log("Modified property -->" + path, "obj_process");
          }
          if (operator === "~=") {
            const search_value = value.split(SPLIT_TAG)[0];
            const replace_value = value.split(SPLIT_TAG)[1];
            last_obj[last_key] = dec_obj.replace(
              new RegExp(search_value, "g"),
              replace_value,
            );
            log("Based on: " + path_info.express, "obj_process");
            log("Modified property -->" + path, "obj_process");
          }
        }
      }

      path_process(json_obj, path) {
        if (path.includes("[-")) {
          const match = path.match(/\[(-\d+)\]/);
          const index = parseInt(match[1]);
          const dec_obj_path = path.slice(0, match.index);
          const array_length = this.string_to_value(
            json_obj,
            dec_obj_path + '["length"]',
          );
          if (!array_length) return path;
          const real_index = array_length + index;
          path = path.replace(`[${index}`, `[${real_index}`);
          return this.path_process(json_obj, path);
        }
        return path;
      }

      value_conditional(value, condition_express) {
        const reg =
          /(\$text|\$value|\$exist|\$notexist)?((>=|<=|>|<|!~=|!=|~=|=))?(.*)/;
        const match = condition_express.match(reg);
        const condition_type = match[1] || "$text";
        const condition_operator = match[2];
        const condition_test_value = match[4];
        const operator_reg = /(>=|<=|>|<|!~=|!=|~=|=)?(.*)$/;
        if (condition_type === "$value") {
          if (![">=", "<=", ">", "<", "="].includes(condition_operator))
            return false;
          const split_tag =
            (condition_test_value.includes(this.value_split_or_tag) &&
              this.value_split_or_tag) ||
            this.value_split_and_tag;
          const condition_test_value_arr =
            condition_test_value.split(split_tag);
          let result;
          for (let test_value of condition_test_value_arr) {
            const operator_match = test_value.match(operator_reg);
            const operator =
              (operator_match && operator_match[1]) || condition_operator;
            test_value = operator_match && operator_match[2];
            if (isNaN(test_value)) {
              if (split_tag === this.value_split_and_tag) return false;
              else continue;
            }
            test_value = parseInt(test_value);
            if (operator === "=") result = test_value === value;
            if (operator === ">=") result = value >= test_value;
            if (operator === "<=") result = value <= test_value;
            if (operator === ">") result = value > test_value;
            if (operator === "<") result = value < test_value;
            if (!result) {
              if (split_tag === this.value_split_and_tag) return false;
              else continue;
            }
            return true;
          }
        }
        if (condition_type === "$exist") {
          return value !== undefined && value !== null;
        }
        if (condition_type === "$notexist") {
          return value === undefined || value === null;
        }
        if (condition_type === "$text") {
          let split_tag;
          let condition_test_value_arr;
          if (["!~=", "~="].includes(condition_operator)) {
            split_tag = this.value_split_and_tag;
            condition_test_value_arr = [condition_test_value];
          } else {
            split_tag =
              (condition_test_value.includes(this.value_split_or_tag) &&
                this.value_split_or_tag) ||
              this.value_split_and_tag;
            condition_test_value_arr = condition_test_value.split(split_tag);
          }
          let result;
          if (typeof value === "object") value = JSON.stringify(value);
          for (let test_value of condition_test_value_arr) {
            const operator_match = test_value.match(operator_reg);
            const operator =
              (operator_match && operator_match[1]) || condition_operator;
            test_value = (operator_match && operator_match[2]) || test_value;
            if (operator === "!=") result = test_value !== value;
            if (operator === "=") result = test_value === value;
            if (operator === "~=") result = new RegExp(test_value).test(value);
            if (operator === "!~=")
              result = !new RegExp(test_value).test(value);
            if (operator === ">=") result = value.length >= test_value.length;
            if (operator === ">") result = value.length > test_value.length;
            if (operator === "<=") result = value.length <= test_value.length;
            if (operator === ">") result = value.length > test_value.length;
            if (!result) {
              if (split_tag === this.value_split_and_tag) return false;
              else continue;
            }
            return true;
          }
        }
        return false;
      }

      obj_conditional(express_info, json_obj) {
        if (!express_info["condition"]) return true;
        const condition_infos = express_info["condition"];
        for (let condition_list of Object.values(condition_infos)) {
          let result = false;
          for (let condition of condition_list) {
            const reg = /^([a-zA-Z_0-9\/\-\.@\[\]]*)?(.*)/;
            const match = condition.match(reg);
            let condition_path = match[1];
            let mod;
            if (condition_path) {
              if (condition_path.startsWith("/")) {
                mod = "child";
              } else if (condition_path.startsWith(".")) {
                mod = "parent";
              } else if (condition_path.startsWith("@")) {
                mod = "global";
              } else {
                mod = "other";
              }
            } else {
              condition_path = express_info.path;
            }
            const conditional_express = match[2];
            if (["child", "parent"].includes(mod)) {
              condition_path = this.get_relative_path(
                express_info.path,
                condition_path,
              );
            }
            if (mod === "other") {
              condition_path = this.get_relative_path(
                "json_obj",
                "/." + condition_path,
              );
            }
            if (mod === "global") {
              condition_path = condition_path.replace(
                "@",
                this.limit_eval ? "unsafeWindow." : "",
              );
            }
            let condition_value;
            try {
              condition_path = this.path_process(json_obj, condition_path);
              condition_value = this.string_to_value(
                mod === "global" ? unsafeWindow : json_obj,
                condition_path,
              );
            } catch (error) {
              continue;
            }
            result = this.value_conditional(
              condition_value,
              conditional_express,
            );
            if (result) {
              express_info.condition_value = condition_value;
              express_info.conform_value_path = condition_path;
              log(
                "Condition satisfied -->",
                condition,
                typeof condition_value === "object"
                  ? "[object Object]"
                  : condition_value,
                "obj_process",
              );
              break;
            }
          }
          if (!result) return false;
        }
        return true;
      }
    }
    return new DATA_PROCESS();
  }

  /* ============ SponsorBlock integration ============ */

  const SB_API = "https://api.sponsor.ajay.app/api/skipSegments";
  const SB_SKIP_CATEGORIES = [
    "sponsor",
    "intro",
    "outro",
    "selfpromo",
    "interaction",
    "music_offtopic",
  ];
  const SB_SKIP_PADDING = 0.35;

  const sb_segmentCache = new Map();
  let sb_currentVideoId = null;
  let sb_currentVideoEl = null;
  let sb_eventListenersAttached = false;

  function sb_log(...args) {
    if (!open_debugger) return;
    log("[SmartSponsorBlock]", ...args, 0);
  }

  function sb_getVideoId() {
    try {
      const url = new URL(location.href);
      const v = url.searchParams.get("v");
      if (v) return v;
      const shorts = location.pathname.match(/\/shorts\/([^/?#]+)/);
      if (shorts) return shorts[1];
      const meta = unsafeWindow.document.querySelector(
        'meta[itemprop="videoId"]',
      );
      if (meta) return meta.content;
    } catch (e) {
      sb_log("getVideoId error", e);
    }
    return null;
  }

  function sb_getVideoElement() {
    return unsafeWindow.document.querySelector("video");
  }

  const SB_CATEGORY_COLORS = {
    sponsor: "#00d400",
    intro: "#00ffff",
    outro: "#0202ed",
    interaction: "#cc00ff",
    music_offtopic: "#ff9900",
    selfpromo: "#ffff00",
    exclusive: "#008a5c",
    filler: "#7300FF",
  };

  function sb_clearProgressBarMarkers() {
    const progressBar =
      unsafeWindow.document.querySelector(".ytp-progress-bar");
    if (!progressBar) return;

    const existing = progressBar.querySelectorAll(".sb-marker");
    existing.forEach((el) => el.remove());

    sb_log("Cleared SponsorBlock markers");
  }

  function sb_renderProgressBarMarkers(segments, videoDuration) {
    if (!segments || segments.length === 0) return;
    if (!videoDuration || videoDuration === 0) return;

    const progressBar =
      unsafeWindow.document.querySelector(".ytp-progress-bar");
    if (!progressBar) return;

    const existing = progressBar.querySelectorAll(".sb-marker");
    existing.forEach((el) => el.remove());

    let markerContainer = progressBar.querySelector(".sb-markers-container");
    if (!markerContainer) {
      markerContainer = unsafeWindow.document.createElement("div");
      markerContainer.className = "sb-markers-container";
      markerContainer.style.position = "relative";
      markerContainer.style.width = "100%";
      markerContainer.style.height = "100%";
      markerContainer.style.pointerEvents = "none";
      progressBar.style.position = "relative";
      progressBar.appendChild(markerContainer);
    }

    if (!unsafeWindow.document.querySelector("#sb-marker-styles")) {
      const style = unsafeWindow.document.createElement("style");
      style.id = "sb-marker-styles";
      style.textContent = `
          .sb-marker {
            position: absolute;
            height: 100%;
            opacity: 0.8;
            pointer-events: auto;
            transition: opacity 0.2s;
          }
          .sb-marker:hover {
            opacity: 1;
          }
        `;
      unsafeWindow.document.head.appendChild(style);
    }

    for (const segment of segments) {
      const color = SB_CATEGORY_COLORS[segment.category] || "#cccccc";
      const startPercent = (segment.start / videoDuration) * 100;
      const endPercent = (segment.end / videoDuration) * 100;
      const widthPercent = endPercent - startPercent;

      const marker = unsafeWindow.document.createElement("div");
      marker.className = "sb-marker";
      marker.style.left = startPercent + "%";
      marker.style.width = widthPercent + "%";
      marker.style.backgroundColor = color;
      marker.title = `${segment.category} (${segment.start.toFixed(0)}s - ${segment.end.toFixed(0)}s)`;

      markerContainer.appendChild(marker);
    }

    sb_log("Rendered", segments.length, "SB progress bar markers");
  }

  function sb_getActiveCategories() {
    const cats = user_data.sb_categories || {};
    return SB_SKIP_CATEGORIES.filter((c) => cats[c] !== "off");
  }

  function sb_fetchSegments(videoId, callback) {
    if (!videoId) {
      callback([]);
      return;
    }

    const activeCategories = sb_getActiveCategories();
    if (activeCategories.length === 0) {
      callback([]);
      return;
    }

    const cacheKey = videoId + "|" + activeCategories.join(",");

    if (sb_segmentCache.has(cacheKey)) {
      callback(sb_segmentCache.get(cacheKey));
      return;
    }

    const url =
      `${SB_API}?videoID=${encodeURIComponent(videoId)}` +
      `&categories=${encodeURIComponent(JSON.stringify(activeCategories))}`;

    GM_xmlhttpRequest({
      method: "GET",
      url,
      headers: { Accept: "application/json" },
      onload: (res) => {
        try {
          const data = JSON.parse(res.responseText);
          const segments = Array.isArray(data)
            ? data
                .map((d) => ({
                  start: d.segment[0],
                  end: d.segment[1],
                  category: d.category,
                }))
                .filter((s) => s.end > s.start)
                .sort((a, b) => a.start - b.start)
            : [];

          const merged = [];
          for (const s of segments) {
            const last = merged[merged.length - 1];
            if (!last || s.start > last.end) {
              merged.push({ ...s });
            } else {
              last.end = Math.max(last.end, s.end);
            }
          }

          sb_segmentCache.set(cacheKey, merged);
          sb_log("Loaded segments for", videoId, merged);
          callback(merged);
        } catch (e) {
          sb_log("Failed to parse SponsorBlock response", e);
          callback([]);
        }
      },
      onerror: () => {
        sb_log("GM_xmlhttpRequest error");
        callback([]);
      },
      ontimeout: () => {
        sb_log("GM_xmlhttpRequest timeout");
        callback([]);
      },
    });
  }

  function sb_attachSkipper(videoId) {
    const video = sb_getVideoElement();
    if (!video || !videoId) return;

    if (
      video === sb_currentVideoEl &&
      videoId === sb_currentVideoId &&
      sb_eventListenersAttached
    ) {
      sb_log("Skipper already attached to", videoId);
      return;
    }

    sb_log("Attaching skipper to video", videoId);
    sb_currentVideoEl = video;
    sb_currentVideoId = videoId;
    sb_eventListenersAttached = false;

    sb_clearProgressBarMarkers();

    sb_fetchSegments(videoId, (segments) => {
      if (!segments.length) {
        sb_log("No sponsor segments for", videoId);
        return;
      }

      let nextIndex = 0;

      if (video.duration && !isNaN(video.duration)) {
        sb_renderProgressBarMarkers(segments, video.duration);
      } else {
        const durationWatcher = () => {
          if (video.duration && !isNaN(video.duration)) {
            sb_renderProgressBarMarkers(segments, video.duration);
            video.removeEventListener("durationchange", durationWatcher);
          }
        };
        observerManager.addListener(video, "durationchange", durationWatcher);
      }

      const skipIfNeeded = () => {
        const t = video.currentTime;
        if (!user_data.default_speed) return;
        while (nextIndex < segments.length) {
          const seg = segments[nextIndex];
          if (t >= seg.start && t < seg.end) {
            video.currentTime = +(seg.end + SB_SKIP_PADDING).toFixed(2);
            sb_log(`Skipped sponsor: ${seg.start} → ${seg.end}`);
            nextIndex++;
          } else if (t < seg.start) {
            break;
          } else {
            nextIndex++;
          }
        }
      };

      const onSeeking = () => {
        nextIndex = segments.findIndex((s) => video.currentTime < s.end);
        if (nextIndex === -1) nextIndex = segments.length;

        const inside = segments.find(
          (s) => video.currentTime >= s.start && video.currentTime < s.end,
        );

        if (inside) {
          video.currentTime = +(inside.end + SB_SKIP_PADDING).toFixed(2);
          sb_log("Seeked into sponsor, skipped");
          nextIndex = segments.indexOf(inside) + 1;
        }
      };

      observerManager.addListener(video, "timeupdate", skipIfNeeded);
      observerManager.addListener(video, "seeking", onSeeking);
      sb_eventListenersAttached = true;

      const watcher = unsafeWindow.setInterval(() => {
        if (
          !unsafeWindow.document.contains(video) ||
          sb_getVideoElement() !== video
        ) {
          video.removeEventListener("timeupdate", skipIfNeeded);
          video.removeEventListener("seeking", onSeeking);
          unsafeWindow.clearInterval(watcher);
          sb_currentVideoEl = null;
          sb_currentVideoId = null;
          sb_eventListenersAttached = false;
          sb_log("Detached skipper from old video element");
        }
      }, 1500);
    });
  }

  function init_sponsorblock() {
    if (user_data.sponsorblock === "off") {
      return;
    }

    if (unsafeWindow.__yt_sb_initialized) {
      return;
    }
    unsafeWindow.__yt_sb_initialized = true;

    function handleNavigation() {
      if (user_data.sponsorblock === "off") return;

      if (
        ![
          "yt_watch",
          "mobile_yt_watch",
          "yt_shorts",
          "mobile_yt_shorts",
          "yt_music_watch",
        ].includes(page_type)
      ) {
        return;
      }

      const videoId = sb_getVideoId();
      if (!videoId) {
        sb_log("No videoId found for current page_type", page_type);
        return;
      }

      unsafeWindow.setTimeout(() => {
        sb_attachSkipper(videoId);
      }, 800);
    }

    unsafeWindow.document.addEventListener(
      "yt-navigate-finish",
      handleNavigation,
    );
    unsafeWindow.document.addEventListener(
      "yt-page-data-updated",
      handleNavigation,
    );

    handleNavigation();

    sb_log("SponsorBlock navigation listeners attached");
  }

  function init_duplicate_song_prevention() {
    if (user_data.duplicate_song_prevention !== "on") {
      return;
    }
    if (!["yt_music_home", "yt_music_watch"].includes(page_type)) {
      return;
    }

    // Guard: check if already initialized to avoid duplicate listeners/observers
    if (unsafeWindow.__yt_dsp_initialized) {
      sb_log("Duplicate song prevention already initialized, skipping");
      return;
    }
    unsafeWindow.__yt_dsp_initialized = true;

    let lastPlayedSong = null;
    let lastPlayedArtist = null;
    const playHistory = [];
    const maxHistoryLength = 50;

    let pendingCheck = null;
    let lastProcessedTitle = null;

    function getSongInfo() {
      const titleElement = document.querySelector(
        "yt-formatted-string.style-scope.ytmusic-player-bar",
      );
      const artistElement = document.querySelector(
        "span.subtitle.style-scope.ytmusic-player-bar a",
      );

      const title = titleElement?.textContent?.trim() || null;
      const artist = artistElement?.textContent?.trim() || null;

      return { title, artist };
    }

    function shouldSkipSong(title, artist) {
      if (!title) return false;

      if (title === lastPlayedSong) {
        sb_log(`Duplicate song detected: ${title}`);
        return true;
      }

      if (artist && artist === lastPlayedArtist) {
        sb_log(`Duplicate artist detected (consecutive): ${artist}`);
        return true;
      }

      return false;
    }

    function updateHistory(title, artist) {
      playHistory.push({ title, artist });
      if (playHistory.length > maxHistoryLength) {
        playHistory.shift();
      }
      lastPlayedSong = title;
      lastPlayedArtist = artist;
      lastProcessedTitle = title;
    }

    function handleSongChange() {
      if (pendingCheck) {
        clearTimeout(pendingCheck);
      }

      pendingCheck = setTimeout(() => {
        const { title, artist } = getSongInfo();

        if (!title || title === lastProcessedTitle) {
          pendingCheck = null;
          return;
        }

        if (shouldSkipSong(title, artist)) {
          const nextBtn =
            document.querySelector("button[aria-label='Next']") ||
            document.querySelector("button.yt-spec-button-shape-next");

          if (nextBtn) {
            nextBtn.click();
            sb_log(`Skipped duplicate: ${title}`);
          }
        } else {
          updateHistory(title, artist);
        }

        pendingCheck = null;
      }, 500);
    }

    const navFinishListener = handleSongChange;
    const pageDataListener = handleSongChange;

    unsafeWindow.document.addEventListener(
      "yt-navigate-finish",
      navFinishListener,
    );
    unsafeWindow.document.addEventListener(
      "yt-page-data-updated",
      pageDataListener,
    );

    const observer = new MutationObserver(handleSongChange);

    const playerElement = document.querySelector("ytmusic-player-bar");
    if (playerElement) {
      observer.observe(playerElement, {
        subtree: true,
        characterData: true,
      });
    }

    unsafeWindow.__yt_dsp_cleanup = () => {
      unsafeWindow.document.removeEventListener(
        "yt-navigate-finish",
        navFinishListener,
      );
      unsafeWindow.document.removeEventListener(
        "yt-page-data-updated",
        pageDataListener,
      );
      observer.disconnect();
      if (pendingCheck) {
        clearTimeout(pendingCheck);
      }
      unsafeWindow.__yt_dsp_initialized = false;
      sb_log("Duplicate song prevention cleaned up");
    };

    sb_log("Duplicate song prevention initialized");
  }

  /* ====== CLEANUP DUPLICATE SONG PREVENTION ====== */

  function cleanup_duplicate_song_prevention() {
    if (typeof unsafeWindow.__yt_dsp_cleanup === "function") {
      unsafeWindow.__yt_dsp_cleanup();
    }
  }

  /* ====== HIDE CREATE BUTTON ====== */

  function init_create_button_observer() {
    const header =
      unsafeWindow.document.querySelector("ytd-masthead") ||
      unsafeWindow.document.querySelector(
        "ytm-app > ytm-mobile-topbar-renderer",
      );
    if (!header) return;

    const observer = new MutationObserver(() => {
      hide_create_button();
    });

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

    hide_create_button();

    unsafeWindow.__yt_create_button_observer = observer;
  }

  /* ====== GLOBAL SHORTS BLOCKER ====== */

  function init_global_shorts_blocker() {
    const processedElements = new WeakSet();

    function hideElement(element) {
      if (!element || processedElements.has(element)) return;
      element.style.display = "none";
      processedElements.add(element);
    }

    function blockShorts() {
      if (user_data.global_shorts_block !== "on") {
        hide_shorts_sections_if_disabled();
        return;
      }

      // Hide reel shelf renderers (Shorts shelves on home/subscriptions)
      document.querySelectorAll("ytd-reel-shelf-renderer").forEach((element) => {
        hideElement(element);
      });

      // Hide Shorts sidebar entry in the guide menu
      document.querySelectorAll('ytd-guide-entry-renderer').forEach((entry) => {
        const link = entry.querySelector('a[title="Shorts"]');
        if (link) {
          hideElement(entry);
        }
      });

      // Hide Shorts in compact sidebar (mini guide)
      document.querySelectorAll('ytd-mini-guide-entry-renderer').forEach((entry) => {
        const link = entry.querySelector('a[title="Shorts"]');
        if (link) {
          hideElement(entry);
        }
      });

      // Hide Shorts chips in filter bar
      document.querySelectorAll("yt-chip-cloud-chip-renderer").forEach((element) => {
        const chipText = element.querySelector(".ytChipShapeChip");
        if (chipText?.textContent.trim() === "Shorts") {
          hideElement(element);
        }
      });

      // Hide individual Shorts items in search/home/subscriptions
      document.querySelectorAll('a[href^="/shorts/"]:not([data-shorts-processed])').forEach((link) => {
        link.setAttribute("data-shorts-processed", "true");

        // Hide video renderer (for search results)
        const videoRenderer = link.closest("ytd-video-renderer");
        if (videoRenderer) {
          hideElement(videoRenderer);
        }

        // Hide grid video renderer (for grid view)
        const gridRenderer = link.closest("ytd-grid-video-renderer");
        if (gridRenderer) {
          hideElement(gridRenderer);
        }

        // Hide rich item renderer (for home/subscriptions)
        const richItem = link.closest("ytd-rich-item-renderer");
        if (richItem) {
          hideElement(richItem);
        }

        // Hide compact video renderer
        const compactRenderer = link.closest("ytd-compact-video-renderer");
        if (compactRenderer) {
          hideElement(compactRenderer);
        }
      });

      // Hide mobile Shorts lockup view models
      document.querySelectorAll("ytm-shorts-lockup-view-model-v2, ytm-shorts-lockup-view-model").forEach((element) => {
        hideElement(element);
      });

      // Hide grid shelf items containing Shorts
      document.querySelectorAll(".ytGridShelfViewModelGridShelfItem:not([data-shorts-processed])").forEach((item) => {
        if (item.querySelector('ytm-shorts-lockup-view-model-v2, ytm-shorts-lockup-view-model, a[href^="/shorts/"]')) {
          hideElement(item);
        }
        item.setAttribute("data-shorts-processed", "true");
      });

      // Hide grid-shelf-view-model containing Shorts
      document.querySelectorAll("grid-shelf-view-model:not([data-shorts-processed])").forEach((shelf) => {
        shelf.setAttribute("data-shorts-processed", "true");
        const hasShorts =
          shelf.querySelector("ytm-shorts-lockup-view-model-v2, ytm-shorts-lockup-view-model") ||
          shelf.querySelector('.shelf-header-layout-wiz__title span[role="text"]')?.textContent?.includes("Shorts");
        if (hasShorts) {
          hideElement(shelf);
        }
      });

      // Hide Shorts tab on channel pages and subscriptions using tab-title attribute
      document.querySelectorAll('yt-tab-shape[tab-title="Shorts"]').forEach((tab) => {
        hideElement(tab);
      });

      // Fallback for Shorts tabs without tab-title attribute
      document.querySelectorAll('yt-tab-shape:not([data-shorts-processed])').forEach((tab) => {
        tab.setAttribute("data-shorts-processed", "true");
        const tabText = tab.querySelector('.yt-tab-shape__tab');
        if (tabText?.textContent?.trim() === "Shorts") {
          hideElement(tab);
        }
      });

      // Hide Shorts sections with "Shorts" title in rich sections
      document.querySelectorAll("ytd-rich-section-renderer:not([data-shorts-processed])").forEach((section) => {
        section.setAttribute("data-shorts-processed", "true");
        const titleSpan = section.querySelector(
          ".yt-shelf-header-layout__title .yt-core-attributed-string, .yt-shelf-header-layout__title-row .yt-core-attributed-string"
        );
        const text = titleSpan?.textContent?.trim();
        if (text === "Shorts") {
          hideElement(section);
        }
      });

      // Hide ytd-rich-shelf-renderer with Shorts content
      document.querySelectorAll("ytd-rich-shelf-renderer:not([data-shorts-processed])").forEach((shelf) => {
        shelf.setAttribute("data-shorts-processed", "true");
        const title = shelf.querySelector("#title-container h2, .ytd-rich-shelf-renderer #title");
        if (title?.textContent?.trim() === "Shorts") {
          hideElement(shelf);
        }
      });

      hide_shorts_sections_if_disabled();
    }

    const observer = new MutationObserver(function (mutations) {
      let shouldProcess = false;
      for (const mutation of mutations) {
        if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
          shouldProcess = true;
          break;
        }
      }
      if (shouldProcess) {
        blockShorts();
      }
    });

    blockShorts();

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

    unsafeWindow.__yt_global_shorts_blocker = {
      reRun: blockShorts,
    };
  }


  /* ====== 2666 WATCH PAGE ELEMENTS HIDER PANEL ====== */

  function apply_hide_buttons_css() {
    const rules = [];

    if (user_data.hide_ask_button === "on") {
      rules.push('button[aria-label="Ask"] { display: none !important; }');
    }

    if (user_data.hide_download_button === "on") {
      rules.push('button[aria-label="Download"] { display: none !important; }');
    }

    if (user_data.hide_share_button === "on") {
      rules.push(
        "#actions #menu ytd-menu-renderer > yt-button-view-model { display: none !important; }",
      );
      rules.push('button[aria-label="Share"] { display: none !important; }');
    }

    if (user_data.hide_thanks_button === "on") {
      rules.push('button[aria-label="Thanks"] { display: none !important; }');
    }

    if (user_data.hide_clip_button === "on") {
      rules.push('button[aria-label="Clip"] { display: none !important; }');
    }

    if (user_data.hide_more_actions_button === "on") {
      rules.push(
        "#actions #menu ytd-menu-renderer yt-button-shape#button-shape { display: none !important; }",
      );
      rules.push(
        '#actions #menu ytd-menu-renderer button[aria-label="More actions"] { display: none !important; }',
      );
    }

    if (user_data.hide_save_button === "on") {
      rules.push(
        'button[aria-label="Save to playlist"] { display: none !important; }',
      );
    }

    if (user_data.hide_subscribe_button === "on") {
      rules.push("#subscribe-button { display: none !important; }");
    }

    if (user_data.hide_like_bar === "on") {
      rules.push(
        ".ytSegmentedLikeDislikeButtonViewModelSegmentedButtonsWrapper { display: none !important; }",
      );
    }

    if (user_data.hide_join_button === "on") {
      rules.push("#sponsor-button { display: none !important; }");
    }

    if (user_data.hide_fullscreen_controls === "on") {
      rules.push(
        "#movie_player .ytp-overlay-top-right { display: none !important; }",
      );
      rules.push(
        "#movie_player .ytp-fullscreen-quick-actions { display: none !important; }",
      );
      rules.push(
        "player-fullscreen-action-menu .action-menu-engagement-buttons-wrapper { display: none !important; }",
      );
    }

    if (user_data.hide_ai_summary === "on") {
      rules.push(
        "#expandable-metadata:has(path[d*='gemini' i]) { display: none !important; }",
      );
      rules.push(
        "#video-summary.ytd-structured-description-content-renderer { display: none !important; }",
      );
      rules.push(
        "ytm-expandable-metadata-renderer:has(path[d*='gemini' i]) { display: none !important; }",
      );
    }

    if (user_data.hide_microphone_icon === "on") {
      rules.push(
        'button[aria-label*="Search with your voice" i] { display: none !important; }',
      );
      rules.push(
        'button[aria-label*="Voice search" i] { display: none !important; }',
      );
      rules.push(
        '.ytd-topbar-logo-button-renderer button[aria-label*="mic" i] { display: none !important; }',
      );
      rules.push(
        "ytm-topbar-search-input-renderer .search-microphone { display: none !important; }",
      );
    }

    if (user_data.hide_paid_promotion === "on") {
      rules.push(
        ".ytp-paid-content-overlay { display: none !important; }",
      );
      rules.push(
        ".ytp-paid-content-overlay-link { display: none !important; }",
      );
      rules.push(
        ".YtmPaidContentOverlayHost { display: none !important; }",
      );
      rules.push(
        "ytm-paid-content-overlay-renderer { display: none !important; }",
      );
      rules.push(
        '[class*="paid-content-overlay"] { display: none !important; }',
      );
    }

    rules.push(`
        #cpfyt-miniplayer-button {
          display: inline-block !important;
          anchor-name: --cpfyt-miniplayer-anchor;
        }
        #cpfyt-miniplayer-button + .ytp-tooltip {
          display: block !important;
          position: fixed !important;
          position-anchor: --cpfyt-miniplayer-anchor;
          bottom: anchor(top);
          left: anchor(center);
          translate: -50% -14px;
          opacity: 0;
          pointer-events: none;
          transition: opacity 0.2s ease;
        }
        .ytp-delhi-modern #cpfyt-miniplayer-button + .ytp-tooltip {
          translate: -50% -22px;
        }
        #cpfyt-miniplayer-button:hover + .ytp-tooltip {
          opacity: 1;
        }
        #cpfyt-miniplayer-button + .ytp-tooltip .ytp-tooltip-text {
          font-size: 13px;
          white-space: pre;
        }
      `);

    let css = rules.join("\n");
    let styleEl = unsafeWindow.document.getElementById("yt-hide-buttons-style");
    if (!styleEl) {
      styleEl = unsafeWindow.document.createElement("style");
      styleEl.id = "yt-hide-buttons-style";
      unsafeWindow.document.head.appendChild(styleEl);
    }
    styleEl.textContent = css;
  }

  function init_disable_ambient_mode() {
    const MAX_RETRIES = 10;
    const WAIT_MS = 500;

    const waitForElement = (selector) => {
      let timeout = MAX_RETRIES;

      return new Promise((resolve, reject) => {
        const interval = setInterval(() => {
          const el = selector();
          if (el) {
            clearInterval(interval);
            resolve(el);
          }
          if (timeout-- <= 0) {
            clearInterval(interval);
            reject("timeout");
          }
        }, WAIT_MS);
      });
    };

    const runScript = () => {
      waitForElement(() =>
        unsafeWindow.document.querySelector(
          "[data-tooltip-target-id=ytp-settings-button]",
        ),
      )
        .then((cog) => {
          cog.click();
          cog.click();

          const getAmbientMode = () =>
            Array.from(
              unsafeWindow.document.getElementsByClassName("ytp-menuitem"),
            ).find((e) => e.innerText.toLowerCase().includes("ambient mode"));

          waitForElement(getAmbientMode)
            .then((el) => {
              if (el.ariaChecked === "true") {
                el.click();
              }
            })
            .catch((e) => {});
        })
        .catch((e) => {
          log("Couldn't find settings cog", 0);
        });
    };

    if (
      unsafeWindow.location.href.includes("youtube.com/watch") ||
      unsafeWindow.location.href.includes("youtube.com/shorts")
    ) {
      runScript();
    }
  }

  function display_hide_buttons_win() {
    const existing = unsafeWindow.document.getElementById(
      "yt-hide-buttons-popup",
    );
    if (existing) existing.remove();

    const css = `
  #yt-hide-buttons-popup{
    z-index:999999999;
    position:fixed;
    top:50%;
    left:50%;
    transform:translate(-50%,-50%);
    padding:0;
    background-color:#ffffff;
    border:1px solid #3498db;
    border-radius:5px;
    box-shadow:0 0 10px rgba(0,0,0,0.3);
    width:260px;
    max-height:80vh;
    display:flex;
    flex-direction:column;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  }

  #yt-hide-buttons-header{
    cursor:move;
    user-select:none;
    padding:4px 8px;
    padding-right:32px;
    background-color:#3498db;
    color:#ffffff;
    border-radius:4px 4px 0 0;
    font-weight:bold;
    font-size:13px;
    position:relative;
  }

  #yt-hide-buttons-close{
    position:absolute;
    top:50%;
    right:8px;
    transform:translateY(-50%);
    cursor:pointer;
    background-color:transparent;
    color:#ffffff;
    border:none;
    padding:0;
    width:20px;
    height:20px;
    border-radius:3px;
    font-size:16px;
    font-weight:bold;
    line-height:1;
    display:flex;
    align-items:center;
    justify-content:center;
    transition:background-color 0.2s ease;
  }

  #yt-hide-buttons-close:hover{
    background-color:rgba(231,76,60,0.9);
  }

  #yt-hide-buttons-close:active{
    background-color:#c0392b;
  }

  #yt-hide-buttons-body{
    flex:1 1 auto;
    overflow-y:auto;
    padding:6px 8px 8px 8px;
  }

  .yt-hb-row{
    display:flex;
    align-items:center;
    gap:6px;
    margin:2px 0;
  }

  .yt-hb-row label{
    font-size:13px;
  }

  .yt-hb-row.disabled label{
    color:#999;
  }

  .yt-hb-select-row{
    display:flex;
    align-items:center;
    gap:6px;
    margin:6px 0 4px 0;
  }

  .yt-hb-select-row label{
    font-size:13px;
    min-width:120px;
  }

  .yt-hb-select-row select{
    flex:1 1 auto;
    padding:4px 6px;
    font-size:13px;
  }

  .yt-hb-section{
    border:1px solid #e0e0e0;
    border-radius:6px;
    margin:8px 0;
    overflow:hidden;
  }

  .yt-hb-section-header{
    display:flex;
    align-items:center;
    gap:8px;
    padding:8px 10px;
    background:#f7f7f7;
    cursor:pointer;
    user-select:none;
    font-weight:600;
    font-size:13px;
  }

  .yt-hb-caret{
    transition:transform 0.2s ease;
    display:inline-block;
    font-size:12px;
  }

  .yt-hb-section-body{
    padding:6px 8px 8px 8px;
  }

  .yt-hb-section-body.collapsed{
    display:none;
  }
  `;
    const style = unsafeWindow.document.createElement("style");
    style.textContent = css;
    unsafeWindow.document.head.appendChild(style);

    const popup = unsafeWindow.document.createElement("div");
    popup.id = "yt-hide-buttons-popup";

    const header = unsafeWindow.document.createElement("div");
    header.id = "yt-hide-buttons-header";
    header.textContent = "Watch Page Tweaks";

    const closeBtn = unsafeWindow.document.createElement("button");
    closeBtn.id = "yt-hide-buttons-close";
    closeBtn.innerHTML = "×";
    closeBtn.title = "Close";
    header.appendChild(closeBtn);

    const body = unsafeWindow.document.createElement("div");
    body.id = "yt-hide-buttons-body";

    function selectRow(id, labelText, options) {
      const div = unsafeWindow.document.createElement("div");
      div.className = "yt-hb-select-row";
      const label = unsafeWindow.document.createElement("label");
      label.htmlFor = id;
      label.textContent = labelText;
      const select = unsafeWindow.document.createElement("select");
      select.id = id;
      options.forEach(({ text, value }) => {
        const opt = unsafeWindow.document.createElement("option");
        opt.value = value;
        opt.textContent = text;
        select.appendChild(opt);
      });
      div.append(label, select);
      return { div, select };
    }

    function row(id, labelText) {
      const div = unsafeWindow.document.createElement("div");
      div.className = "yt-hb-row";
      const input = unsafeWindow.document.createElement("input");
      input.type = "checkbox";
      input.id = id;
      const label = unsafeWindow.document.createElement("label");
      label.htmlFor = id;
      label.textContent = labelText;
      div.append(input, label);
      return { div, input };
    }

    const actionRows = [
      row("hb_ask", "Ask (Gemini)"),
      row("hb_download", "Download"),
      row("hb_share", "Share"),
      row("hb_thanks", "Thanks"),
      row("hb_clip", "Clip"),
      row("hb_save", "Save to playlist"),
      row("hb_more", "More actions"),
      row("hb_subscribe", "Subscribe+Bell"),
      row("hb_likebar", "Like/Dislike bar"),
      row("hb_join", "Join"),
    ];

    const otherRows = [
      row("hb_endcards", "End cards (overlay)"),
      row("hb_livechat_replay", "Live chat replay teaser"),
      row("hb_fullscreen_controls", "Hide fullscreen controls"),
      row("hb_ai_summary", "Hide AI summaries"),
      row("hb_microphone", "Hide microphone icon"),
      row("hb_restore_red_progress_bar", "Restore red progress bar"),
      row("hb_paid_promotion", "Hide paid promotion overlay"),
      row("hb_old_player_ui", "Old Player UI (reload required)"),
    ];

    const rows = [...actionRows, ...otherRows];

    const qualityRow = selectRow("sel_quality", "Default quality", [
      { text: "Off", value: "off" },
      { text: "4K", value: "hd2160" },
      { text: "1440p", value: "hd1440" },
      { text: "1080p", value: "hd1080" },
      { text: "720p", value: "hd720" },
      { text: "480p", value: "large" },
      { text: "360p", value: "medium" },
      { text: "240p", value: "small" },
      { text: "144p", value: "tiny" },
    ]);

    const speedRow = selectRow("sel_speed", "Default speed", [
      { text: "0.25x", value: "0.25" },
      { text: "0.5x", value: "0.5" },
      { text: "0.75x", value: "0.75" },
      { text: "1x", value: "1" },
      { text: "1.25x", value: "1.25" },
      { text: "1.5x", value: "1.5" },
      { text: "1.75x", value: "1.75" },
    ]);

    body.append(qualityRow.div, speedRow.div);

    const buttonsSection = unsafeWindow.document.createElement("div");
    buttonsSection.className = "yt-hb-section";

    const sectionHeader = unsafeWindow.document.createElement("div");
    sectionHeader.className = "yt-hb-section-header";
    const caret = unsafeWindow.document.createElement("span");
    caret.className = "yt-hb-caret";
    caret.textContent = "▾";
    const sectionTitle = unsafeWindow.document.createElement("span");
    sectionTitle.textContent = "Action bar buttons";
    sectionHeader.append(caret, sectionTitle);

    const sectionBody = unsafeWindow.document.createElement("div");
    sectionBody.className = "yt-hb-section-body collapsed";
    caret.style.transform = "rotate(-90deg)";

    for (const { div } of actionRows) {
      sectionBody.appendChild(div);
    }

    sectionHeader.addEventListener("click", () => {
      const collapsed = sectionBody.classList.toggle("collapsed");
      caret.style.transform = collapsed ? "rotate(-90deg)" : "rotate(0deg)";
    });

    buttonsSection.append(sectionHeader, sectionBody);
    body.append(buttonsSection);

    // SponsorBlock categories
    const SB_CATEGORY_LABELS = {
      sponsor:       "Sponsor",
      intro:         "Intro / Intermission",
      outro:         "Outro / Credits",
      selfpromo:     "Self-promotion",
      interaction:   "Interaction reminder",
      music_offtopic:"Music: non-music section",
    };
    const SB_CATEGORY_DOTS = {
      sponsor:       "#00d400",
      intro:         "#00ffff",
      outro:         "#0202ed",
      selfpromo:     "#ffff00",
      interaction:   "#cc00ff",
      music_offtopic:"#ff9900",
    };

    const sbSection = unsafeWindow.document.createElement("div");
    sbSection.className = "yt-hb-section";

    const sbSectionHeader = unsafeWindow.document.createElement("div");
    sbSectionHeader.className = "yt-hb-section-header";
    const sbCaret = unsafeWindow.document.createElement("span");
    sbCaret.className = "yt-hb-caret";
    sbCaret.textContent = "▾";
    const sbSectionTitle = unsafeWindow.document.createElement("span");
    sbSectionTitle.textContent = "SponsorBlock categories";
    sbSectionHeader.append(sbCaret, sbSectionTitle);

    const sbSectionBody = unsafeWindow.document.createElement("div");
    sbSectionBody.className = "yt-hb-section-body collapsed";
    sbCaret.style.transform = "rotate(-90deg)";

    sbSectionHeader.addEventListener("click", () => {
      const collapsed = sbSectionBody.classList.toggle("collapsed");
      sbCaret.style.transform = collapsed ? "rotate(-90deg)" : "rotate(0deg)";
    });

    const sbCheckboxes = {};
    for (const [catId, catLabel] of Object.entries(SB_CATEGORY_LABELS)) {
      const rowDiv = unsafeWindow.document.createElement("div");
      rowDiv.className = "yt-hb-row";

      const input = unsafeWindow.document.createElement("input");
      input.type = "checkbox";
      input.id = "sb_cat_" + catId;
      input.checked = (user_data.sb_categories?.[catId] ?? "on") === "on";

      const label = unsafeWindow.document.createElement("label");
      label.htmlFor = input.id;

      // colored dot
      const dot = unsafeWindow.document.createElement("span");
      dot.style.cssText = `display:inline-block;width:10px;height:10px;border-radius:50%;background:${SB_CATEGORY_DOTS[catId]};margin-right:5px;flex-shrink:0;`;

      label.append(dot, catLabel);
      rowDiv.append(input, label);
      sbSectionBody.appendChild(rowDiv);
      sbCheckboxes[catId] = input;

      input.addEventListener("change", () => {
        if (!user_data.sb_categories) user_data.sb_categories = {};
        user_data.sb_categories[catId] = input.checked ? "on" : "off";
        user_data_api.set();
        sb_segmentCache.clear();
      });
    }

    sbSection.append(sbSectionHeader, sbSectionBody);
    body.appendChild(sbSection);

    // Disable SB categories section if SponsorBlock is off
    const sbEnabled = user_data.sponsorblock === "on";
    if (!sbEnabled) {
      sbSectionHeader.style.opacity = "0.45";
      sbSectionHeader.style.pointerEvents = "none";
      sbSectionHeader.title = "Enable SponsorBlock first to configure categories";
      Object.values(sbCheckboxes).forEach((inp) => {
        inp.disabled = true;
        inp.parentElement.style.opacity = "0.45";
      });
    }

    for (const { div } of otherRows) {
      body.appendChild(div);
    }

    popup.append(header, body);
    unsafeWindow.document.body.appendChild(popup);

    const map = [
      ["hb_ask", "hide_ask_button"],
      ["hb_download", "hide_download_button"],
      ["hb_share", "hide_share_button"],
      ["hb_thanks", "hide_thanks_button"],
      ["hb_clip", "hide_clip_button"],
      ["hb_more", "hide_more_actions_button"],
      ["hb_save", "hide_save_button"],
      ["hb_subscribe", "hide_subscribe_button"],
      ["hb_likebar", "hide_like_bar"],
      ["hb_join", "hide_join_button"],
      ["hb_endcards", "hide_end_cards"],
      ["hb_fullscreen_controls", "hide_fullscreen_controls"],
      ["hb_ai_summary", "hide_ai_summary"],
      ["hb_microphone", "hide_microphone_icon"],
      ["hb_restore_red_progress_bar", "restore_red_progress_bar"],
      ["hb_paid_promotion", "hide_paid_promotion"],
      ["hb_old_player_ui", "old_player_ui"],
    ];

    const checkboxById = {};
    for (const { input } of rows) {
      checkboxById[input.id] = input;
    }

    qualityRow.select.value = user_data.default_quality || "off";
    speedRow.select.value = user_data.default_speed || "1";

    for (const [checkboxId, key] of map) {
      const input = checkboxById[checkboxId];
      input.checked = user_data[key] === "on";
    }

    // Live chat replay section
    if (user_data.watch_page_config?.hide_live_chat_replay === "on") {
      checkboxById["hb_livechat_replay"].checked = true;
    }

    function applyQualityPreference() {
      try {
        if (!user_data.default_quality || user_data.default_quality === "off")
          return;
        const player = document.querySelector(".html5-video-player");
        const video = document.querySelector("video");
        if (!player || !video) return;
        const levels = player.getAvailableQualityLevels?.();
        if (!levels || levels.length === 0) return;
        const targetQuality = user_data.default_quality;
        const startIndex = QUALITY_ORDER.indexOf(targetQuality);
        const candidates =
          startIndex >= 0 ? QUALITY_ORDER.slice(startIndex) : QUALITY_ORDER;
        const chosen =
          candidates.find((q) => levels.includes(q)) ||
          levels[levels.length - 1];
        if (chosen && player.getPlaybackQualityLabel?.() !== chosen) {
          player.setPlaybackQualityRange?.(chosen, chosen);
        }
      } catch (e) {}
    }

    function applySpeedPreference() {
      try {
        if (!user_data.default_speed) return;
        const video = document.querySelector("video");
        if (!video) return;
        const targetSpeed = parseFloat(user_data.default_speed);
        if (!isFinite(targetSpeed)) return;
        video.playbackRate = targetSpeed;
      } catch (e) {}
    }

    qualityRow.select.addEventListener("change", () => {
      user_data.default_quality = qualityRow.select.value;
      user_data_api.set();
      applyQualityPreference();
    });

    speedRow.select.addEventListener("change", () => {
      user_data.default_speed = speedRow.select.value;
      user_data_api.set();
      applySpeedPreference();
    });

    for (const [checkboxId, key] of map) {
      const input = checkboxById[checkboxId];
      input.addEventListener("change", () => {
        user_data[key] = input.checked ? "on" : "off";
        user_data_api.set();
        apply_hide_buttons_css();
        if (key === "old_player_ui") {
          if (confirm("Old Player UI requires a page reload to take effect. Reload now?")) {
            unsafeWindow.location.reload();
          }
        }
      });
    }

    checkboxById["hb_livechat_replay"].addEventListener("change", () => {
      user_data.watch_page_config.hide_live_chat_replay = checkboxById[
        "hb_livechat_replay"
      ].checked
        ? "on"
        : "off";
      user_data_api.set();
      if (user_data.watch_page_config.hide_live_chat_replay === "on") {
        hide_teaser_carousel();
      } else {
        const n = unsafeWindow.document.querySelector("#teaser-carousel");
        if (n) n.style.display = "";
      }
    });

    function close() {
      popup.remove();
    }
    closeBtn.addEventListener("click", close);
    make_popup_draggable(popup, header, "pos_2666");

  }
})();