YouTube: Audio Only

No Video Streaming

目前为 2024-01-11 提交的版本。查看 最新版本

// ==UserScript==
// @name                YouTube: Audio Only
// @description         No Video Streaming
// @namespace           UserScript
// @version             0.2.5
// @author              CY Fung
// @match               https://www.youtube.com/*
// @match               https://www.youtube.com/embed/*
// @match               https://www.youtube-nocookie.com/embed/*
// @match               https://m.youtube.com/*
// @exclude             /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/
// @icon                https://raw.githubusercontent.com/cyfung1031/userscript-supports/main/icons/YouTube-Audio-Only.png
// @grant               GM_registerMenuCommand
// @grant               GM.setValue
// @grant               GM.getValue
// @run-at              document-start
// @license             MIT
// @compatible          chrome
// @compatible          firefox
// @compatible          opera
// @compatible          edge
// @compatible          safari
// @allFrames           true

// ==/UserScript==

(async function () {
  'use strict';

  const pageInjectionCode = function () {

    // embed & desktop & mobile
    window.XMLHttpRequest = ((XMLHttpRequest_) => {

      class XMLHttpRequest extends XMLHttpRequest_ {

        constructor(...args) {
          super(...args);
        }
        open(method, url, ...args) {
          if (typeof url === 'string' && url.length > 24 && url.includes('/videoplayback?') && url.replace('?', '&').includes('&source=')) {
            window.postMessage({ ZECxh: url.includes('source=yt_live_broadcast') }, "*");
          }
          return super.open(method, url, ...args);
        }
      }

      return XMLHttpRequest;

    })(window.XMLHttpRequest);

    // desktop only
    // document.addEventListener('yt-page-data-fetched', async (evt) => {

    //   const pageFetchedDataLocal = evt.detail;
    //   let isLiveNow;
    //   try {
    //     isLiveNow = pageFetchedDataLocal.pageData.playerResponse.microformat.playerMicroformatRenderer.liveBroadcastDetails.isLiveNow;
    //   } catch (e) { }
    //   window.postMessage({ ZECxh: isLiveNow === true }, "*");

    // }, false);

    Object.defineProperty(Object.prototype, 'deviceIsAudioOnly', {
      get() {
        console.log(1238)
        return true;
      },
      set(nv) {
        return true;
      },
      enumerable: false,
      configurable: true
    });

    const supportedFormatsConfig = () => {

      function typeTest(type) {
        if (typeof type === 'string' && type.startsWith('video/')) {
          return false;
        }
      }

      // return a custom MIME type checker that can defer to the original function
      function makeModifiedTypeChecker(origChecker) {
        // Check if a video type is allowed
        return function (type) {
          let res = undefined;
          if (type === undefined) res = false;
          else {
            res = typeTest.call(this, type);
          }
          if (res === undefined) res = origChecker.apply(this, arguments);
          return res;
        };
      }

      // Override video element canPlayType() function
      const proto = (HTMLVideoElement || 0).prototype;
      if (proto && typeof proto.canPlayType == 'function') {
        proto.canPlayType = makeModifiedTypeChecker(proto.canPlayType);
      }

      // Override media source extension isTypeSupported() function
      const mse = window.MediaSource;
      // Check for MSE support before use
      if (mse && typeof mse.isTypeSupported == 'function') {
        mse.isTypeSupported = makeModifiedTypeChecker(mse.isTypeSupported);
      }

    };

    supportedFormatsConfig();
  }

  const isEnable = (typeof GM !== 'undefined' && typeof GM.getValue === 'function') ? (await GM.getValue("isEnable_aWsjF", true)) : null;
  if (typeof isEnable !== 'boolean') throw new DOMException("Please Update your browser", "NotSupportedError");
  if (isEnable) {
    const element = document.createElement('button');
    element.setAttribute('onclick', `(${pageInjectionCode})()`);
    element.click();
  }

  GM_registerMenuCommand(`Turn ${isEnable ? 'OFF' : 'ON'} YouTube Audio Mode`, async function () {
    await GM.setValue("isEnable_aWsjF", !isEnable);
    location.reload();
  });

  let messageCount = 0;
  window.addEventListener('message', (evt) => {

    const v = ((evt || 0).data || 0).ZECxh;
    if (typeof v === 'boolean') {
      const t = ++messageCount;
      if (v && isEnable) {
        requestAnimationFrame(async () => {
          if (t !== messageCount) return;
          if (confirm("Livestream is detected. Press OK to disable YouTube Audio Mode.")) {
            await GM.setValue("isEnable_aWsjF", !isEnable);
            location.reload();
          }
        });
      }
    }

  });


})();