Greasy Fork

Greasy Fork is available in English.

chzzk Ad Block Auto Quality Change

치지직 광고 차단 감지 스크립트를 우회합니다.

当前为 2025-08-22 提交的版本,查看 最新版本

// ==UserScript==
// @name         chzzk Ad Block Auto Quality Change
// @version      1.0.2
// @match        https://chzzk.naver.com/*
// @description  치지직 광고 차단 감지 스크립트를 우회합니다.
// @run-at       document-start
// @grant        none
// @author       k22pr
// @namespace    k22pr/chzzk-ad-block-auto-quality-change
// @license MIT
// ==/UserScript==

var qualityInterval = null;
(function () {
  const SLIDER_SEL = "div.pzp-pc-progress-slider";
  const BTN_SEL = "button.pzp-pc-setting-button";
  const VIDEO_SEL = "video.webplayer-internal-video";
  const QUALITY_SEL = "li.pzp-ui-setting-quality-item";
  let isHighQuality = false;

  let qualityButtonList = document.querySelectorAll(
    `${SLIDER_SEL} > ${BTN_SEL}`
  );
  let videoEl = document.querySelector(VIDEO_SEL);

  async function hoverThenClickOnce() {
    const enterEvent = new KeyboardEvent("keydown", {
      bubbles: true,
      cancelable: true,
      key: "Enter",
      code: "Enter",
      keyCode: 13,
      which: 13,
    });
    const qualityOption = document.querySelector(QUALITY_SEL);
    console.log(qualityOption);
    if (!qualityOption) return false;
    qualityOption.dispatchEvent(enterEvent);
  }

  // SPA URL Change
  (function () {
    clearInterval(qualityInterval);
    qualityInterval = null;
    const fireLoc = () =>
      setTimeout(() => {
        done = false;
        setQualityInterval();
        startQualityInterval();
        forceStopInterval();
      }, 0);

    const _ps = history.pushState,
      _rs = history.replaceState;
    history.pushState = function () {
      console.log("pushState");
      const r = _ps.apply(this, arguments);
      fireLoc();
      return r;
    };
    history.replaceState = function () {
      console.log("replaceState");
      const r = _rs.apply(this, arguments);
      fireLoc();
      return r;
    };
    window.addEventListener("popstate", fireLoc);
  })();

  // test: Alt+H
  window.addEventListener("keydown", (e) => {
    if (e.altKey && e.key.toLowerCase() === "h") {
      done = false;
      hoverThenClickOnce();
    }
  });

  function setQualityInterval() {
    qualityInterval = setInterval(() => {
      const hasLive = location.pathname.includes("/live/");
      qualityButtonList = document.querySelectorAll(QUALITY_SEL);
      videoEl = document.querySelector(VIDEO_SEL);
      if (qualityButtonList.length != 0) {
        isHighQuality = qualityButtonList[0].classList.contains(
          "pzp-ui-setting-pane-item--checked"
        );
      }
      console.log("setQualityInterval", videoEl, hasLive, isHighQuality);

      if (!hasLive) {
        clearInterval(qualityInterval);
        qualityInterval = null;
      }

      if (videoEl && hasLive && !isHighQuality) {
        hoverThenClickOnce();
      }

      if (isHighQuality && videoEl.paused === true) {
        videoEl.play();
      }

      if (isHighQuality && videoEl.paused !== false) {
        clearInterval(qualityInterval);
        qualityInterval = null;
      }
    }, 100);
  }

  function startQualityInterval() {
    const hasLive = location.pathname.includes("/live/");
    qualityButtonList = document.querySelectorAll(QUALITY_SEL);
    videoEl = document.querySelector(VIDEO_SEL);
    console.log("startQualityInterval", videoEl, hasLive, isHighQuality);
    if (qualityButtonList.length != 0) {
      isHighQuality = qualityButtonList[0].classList.contains(
        "pzp-ui-setting-pane-item--checked"
      );
    }

    if (videoEl && hasLive && !isHighQuality) {
      setQualityInterval();
    }
  }

  function forceStopInterval() {
    setTimeout(() => {
      clearInterval(qualityInterval);
      qualityInterval = null;
    }, 3000);
  }

  setQualityInterval();
  startQualityInterval();
  forceStopInterval();
})();

(function () {
  const MSG = "광고 차단 프로그램을 사용 중이신가요?";

  const isPopupContents = (el) =>
    el instanceof Element &&
    (el.className + "").includes("popup_contents__") &&
    el.textContent &&
    el.textContent.includes(MSG);

  const removePopup = (el) => {
    const container =
      el.closest(
        '[role="dialog"], [class*="popup"], [class*="modal"], [class*="layer"]'
      ) || el;
    container.parentElement.remove();
    console.log("[userscript] adblock popup removed");
  };

  const scan = (root) => {
    if (!root) return;
    const candidates =
      root.querySelectorAll?.('[class*="popup_contents__"]') ?? [];
    candidates.forEach((el) => {
      if (isPopupContents(el)) removePopup(el);
    });

    const all = root.querySelectorAll?.("*") ?? [];
    all.forEach((n) => {
      if (n.shadowRoot) scan(n.shadowRoot);
    });
    if (root.shadowRoot) scan(root.shadowRoot);
  };

  const ready = () => scan(document);
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", ready);
  } else {
    ready();
  }

  const mo = new MutationObserver((mutations) => {
    for (const m of mutations) {
      m.addedNodes.forEach((node) => {
        if (node.nodeType === 1) scan(node); // Element
      });
      if (m.type === "characterData" && m.target?.parentElement) {
        const el = m.target.parentElement.closest?.(
          '[class*="popup_contents__"]'
        );
        if (el && isPopupContents(el)) removePopup(el);
      }
    }
  });

  mo.observe(document.documentElement, {
    childList: true,
    subtree: true,
    characterData: true,
  });
})();