YouTube: Audio Only

No Video Streaming

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

// ==UserScript==
// @name                YouTube: Audio Only
// @description         No Video Streaming
// @namespace           UserScript
// @version             0.2.2
// @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 () {

    Object.defineProperty(Object.prototype, 'deviceIsAudioOnly', {
      get() {
        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();
  }

  if (typeof GM === 'undefined' || typeof GM.getValue === 'undefined') throw new DOMException("Please Update your browser", "NotSupportedError");

  const isEnable = await GM.getValue("isEnable_aWsjF", true);
  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 pageCounter = 0;
  isEnable && document.addEventListener('yt-page-data-fetched', async (evt) => {

    const t = ++pageCounter;
    const pageFetchedDataLocal = evt.detail;
    let isLiveNow;
    try {
      isLiveNow = pageFetchedDataLocal.pageData.playerResponse.microformat.playerMicroformatRenderer.liveBroadcastDetails.isLiveNow;
    } catch (e) { }
    if (isLiveNow === true) {
      requestAnimationFrame(async () => {
        if (t !== pageCounter) return;
        if (confirm("Live Video is detected. Press OK to disable YouTube Audio Mode.")) {
          await GM.setValue("isEnable_aWsjF", !isEnable);
          location.reload();
        }
      });
    }

  }, false);


})();