Greasy Fork 还支持 简体中文。

YouTube: Single Column Tamer

Re-adoption of Single Column Detection against video and browser sizes

目前為 2024-03-12 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        YouTube: Single Column Tamer
// @namespace   UserScripts
// @match       https://www.youtube.com/*
// @grant       none
// @unwrap
// @inject-into page
// @version     0.1.2
// @author      CY Fung
// @description Re-adoption of Single Column Detection against video and browser sizes
// @require     https://cdn.jsdelivr.net/gh/cyfung1031/userscript-supports@ea433e2401dd5c8fdd799fda078fe19859b087f9/library/ytZara.js
// @require     https://update.greasyfork.org/scripts/475632/1340102/ytConfigHacks.js
// @license     MIT
// ==/UserScript==

(() => {
  const ENABLE_WHEN_CONTENT_OCCUPY_MORE_THAN = 0.2 // 20% or more of other content can be displayed in your browser

  // protait screen & vertical live

  let isSingleColumnPreferred = false;
  let bypass = false;

  const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);

  const Promise = (async () => { })().constructor;

  const createPipeline = () => {
    let pipelineMutex = Promise.resolve();
    const pipelineExecution = fn => {
      return new Promise((resolve, reject) => {
        pipelineMutex = pipelineMutex.then(async () => {
          let res;
          try {
            res = await fn();
          } catch (e) {
            console.log('error_F1', e);
            reject(e);
          }
          resolve(res);
        }).catch(console.warn);
      });
    };
    return pipelineExecution;
  };

  let rafPromise = null;
  const rafFn = (typeof webkitRequestAnimationFrame !== 'undefined' ? webkitRequestAnimationFrame : requestAnimationFrame).bind(window); // eslint-disable-line no-undef, no-constant-condition

  const getRafPromise = () => rafPromise || (rafPromise = new Promise(resolve => {
    rafFn(hRes => {
      rafPromise = null;
      resolve(hRes);
    });
  }));

  let videoRatio = null;

  const getProto = (element) => {
    if (element) {
      const cnt = insp(element);
      return cnt.constructor.prototype || null;
    }
    return null;
  }

  let qm = 0;

  function changeQ(q) {

    if (!q || typeof q !== 'string') return q;

    q = q.replace('1000px', '200.2px');
    q = q.replace('629px', '129.2px');
    q = q.replace('657px', '157.2px');
    q = q.replace('630px', '130.2px');
    q = q.replace('1327px', '237.2px');

    return q;

  }


  function changeQ_alwaysSingleColumn(q) {

    if (!q || typeof q !== 'string') return q;

    q = q.replace('1000px', '998200.3px');
    q = q.replace('629px', '998129.3px');
    q = q.replace('657px', '998157.3px');
    q = q.replace('630px', '998130.3px');
    q = q.replace('1327px', '998237.3px');

    return q;

  }


  let cachedSCUsage = null;

  function getShouldSingleColumn() {
    if (typeof cachedSCUsage == 'boolean') return cachedSCUsage;
    const { clientHeight, clientWidth } = document.documentElement;
    if (clientHeight > clientWidth) {
      const referenceVideoHeight = clientWidth * videoRatio;
      const belowSpace = clientHeight - referenceVideoHeight;
      if (belowSpace > -1e-3 && belowSpace - ENABLE_WHEN_CONTENT_OCCUPY_MORE_THAN * clientHeight > -1e-3) {
        return (cachedSCUsage = true);
      }
    }
    return (cachedSCUsage = false);
  }

  /** @type {Set<WeakRef<Object>>} */
  const querySet = new Set();
  const protoFnQueryChanged = async () => {

    await customElements.whenDefined('iron-media-query');
    const dummy = document.querySelector('iron-media-query') || document.createElement('iron-media-query');
    const cProto = getProto(dummy);

    if (typeof cProto.queryChanged !== 'function') return;
    if (cProto.queryChanged71) return;
    if (cProto.queryChanged.length !== 0) return;
    cProto.queryChanged71 = cProto.queryChanged;

    cProto.queryChanged = function () {

      /** @type {string} */
      let q = this.query;

      if (q) {

        if (!this.addedToSet53_) {
          this.addedToSet53_ = 1;
          querySet.add(new WeakRef(this));
        }

        if (!bypass) {
          if (q.length > 3 && !q.includes('.')) {
            this.lastQuery53_ = q;
          }
        }


        if (this.lastQuery53_) {

          if (isSingleColumnPreferred) {
            q = changeQ_alwaysSingleColumn(this.lastQuery53_);
          } else if (qm) {
            q = changeQ(this.lastQuery53_);
          } else {
            q = this.lastQuery53_;
          }

        }

        if (q !== this.query && typeof q === 'string' && q) {
          this.query = q;
        }

      }

      return this.queryChanged71();

    }

  };

  const protoFnRatioChanged = async () => {

    await customElements.whenDefined('ytd-watch-flexy');
    const dummy = document.querySelector('ytd-watch-flexy') || document.createElement('ytd-watch-flexy');
    const cProto = getProto(dummy);

    if (typeof cProto.videoHeightToWidthRatioChanged_ !== 'function') return;
    if (cProto.videoHeightToWidthRatioChanged23_) return;
    // if (cProto.videoHeightToWidthRatioChanged_.length !== 2) return;

    cProto.videoHeightToWidthRatioChanged23_ = cProto.videoHeightToWidthRatioChanged_;
    const ratioQueryFix24_ = () => {

      if (videoRatio > 1e-5) { } else return;
      let changeCSS = false;

      const changedSingleColumn = isSingleColumnPreferred !== (isSingleColumnPreferred = getShouldSingleColumn());
      let action = 0;
      if (changedSingleColumn) {
        action |= 4;
      }
      if (!isSingleColumnPreferred) {
        const ratio2 = videoRatio > 1.6 && videoRatio < 2.7;
        if (ratio2 && !qm) {
          changeCSS = true;
          qm = 1;
          action |= 1;
        } else if (!ratio2 && qm) {
          changeCSS = true;
          qm = 0;
          action |= 2;
        }
      }
      if (action) {
        for (const p of querySet) {
          const qnt = p.deref();
          if (!qnt || !qnt.lastQuery53_) continue;
          if (action & 4) {
            if (!qnt.q00 && !qnt.q02 && isSingleColumnPreferred) {
              qnt.q00 = qnt.lastQuery53_;
              qnt.q02 = changeQ_alwaysSingleColumn(qnt.q00);
            }
            action |= 8;
          }
          if (action & 1) {
            if (!qnt.q00 && !qnt.q01) {
              qnt.q00 = qnt.lastQuery53_;
              qnt.q01 = changeQ(qnt.q00);
            }
            if (qnt.q00 && qnt.q01) {
              action |= 8;
            }
          } else if (action & 2) {
            if (qnt.q00 && qnt.q01) {
              action |= 8;
            }
          }
        }

        if (action & 8) {
          bypass = true;
          for (const p of querySet) {
            const qnt = p.deref();
            if (qnt && qnt.lastQuery53_ && qnt.query) {
              qnt.queryChanged();
            }
          }
          bypass = false;
        }

      }

      let cssElm = null;

      if (changeCSS) {

        cssElm = cssElm || document.querySelector('style#oh7T7lsvcHJQ');

        if (!cssElm) {

          cssElm = document.createElement('style');
          cssElm.id = 'oh7T7lsvcHJQ';
          document.head.appendChild(cssElm);

          cssElm.textContent = `

              ytd-watch-flexy[flexy][is-two-columns_] {
                --ytd-watch-flexy-min-player-height-ss: 10px;
              }
              ytd-watch-flexy[flexy][is-two-columns_] #primary.ytd-watch-flexy {
                min-width: calc(var(--ytd-watch-flexy-min-player-height-ss)*1.7777777778);
              }
              ytd-watch-flexy[flexy][is-two-columns_]:not([is-four-three-to-sixteen-nine-video_]):not([is-extra-wide-video_]):not([full-bleed-player][full-bleed-no-max-width-columns]):not([fixed-panels]) #primary.ytd-watch-flexy {
                min-width: calc(var(--ytd-watch-flexy-min-player-height-ss)*1.7777777778);
              }
          `;

        }
      }

      cssElm = cssElm || document.querySelector('style#oh7T7lsvcHJQ');
      if (cssElm) {
        if (qm && cssElm.disabled) cssElm.disabled = false;
        else if (!qm && !cssElm.disabled) cssElm.disabled = true;
      }
    };

    const resizePipeline = createPipeline();

    cProto.videoHeightToWidthRatioChanged_ = function () {
      try {
        cachedSCUsage = null;
        videoRatio = this.videoHeightToWidthRatio_;
        resizePipeline(ratioQueryFix24_);
      } catch (e) {
      }
      return this.videoHeightToWidthRatioChanged23_(...arguments);
    };

    let rzid = 0;
    Window.prototype.addEventListener.call(window, 'resize', function () {
      cachedSCUsage = null;
      if (videoRatio > 1e-5) { } else return;
      if (rzid > 1e9) rzid = 9;
      const t = ++rzid;
      resizePipeline(async () => {
        if (t !== rzid) return;
        await getRafPromise();
        if (t !== rzid) return;
        let k = getShouldSingleColumn();
        if (isSingleColumnPreferred !== k) {
          resizePipeline(ratioQueryFix24_);
        }
      });
    }, { capture: false, passive: true });

  };

  window._ytConfigHacks.add((config_) => {

    const EXPERIMENT_FLAGS = config_.EXPERIMENT_FLAGS;

    if (EXPERIMENT_FLAGS) {

      EXPERIMENT_FLAGS.kevlar_set_internal_player_size = false; // vertical live -> schedulePlayerSizeUpdate_

    }

  });

  (async () => {

    if (!document.documentElement) await ytZara.docInitializedAsync(); // wait for document.documentElement is provided

    await ytZara.promiseRegistryReady(); // wait for YouTube's customElement Registry is provided (old browser only)

    protoFnQueryChanged();
    protoFnRatioChanged();

  })();

})();