YouTube: Audio Only

No Video Streaming

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

  1. // ==UserScript==
  2. // @name YouTube: Audio Only
  3. // @description No Video Streaming
  4. // @namespace UserScript
  5. // @version 0.2.2
  6. // @author CY Fung
  7. // @match https://www.youtube.com/*
  8. // @match https://www.youtube.com/embed/*
  9. // @match https://www.youtube-nocookie.com/embed/*
  10. // @match https://m.youtube.com/*
  11. // @exclude /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/
  12. // @icon https://raw.githubusercontent.com/cyfung1031/userscript-supports/main/icons/YouTube-Audio-Only.png
  13. // @grant GM_registerMenuCommand
  14. // @grant GM.setValue
  15. // @grant GM.getValue
  16. // @run-at document-start
  17. // @license MIT
  18. // @compatible chrome
  19. // @compatible firefox
  20. // @compatible opera
  21. // @compatible edge
  22. // @compatible safari
  23. // @allFrames true
  24.  
  25. // ==/UserScript==
  26.  
  27. (async function () {
  28. 'use strict';
  29.  
  30. const pageInjectionCode = function () {
  31.  
  32. Object.defineProperty(Object.prototype, 'deviceIsAudioOnly', {
  33. get() {
  34. return true;
  35. },
  36. set(nv) {
  37. return true;
  38. },
  39. enumerable: false,
  40. configurable: true
  41. });
  42.  
  43. const supportedFormatsConfig = () => {
  44.  
  45. function typeTest(type) {
  46. if (typeof type === 'string' && type.startsWith('video/')) {
  47. return false;
  48. }
  49. }
  50.  
  51. // return a custom MIME type checker that can defer to the original function
  52. function makeModifiedTypeChecker(origChecker) {
  53. // Check if a video type is allowed
  54. return function (type) {
  55. let res = undefined;
  56. if (type === undefined) res = false;
  57. else {
  58. res = typeTest.call(this, type);
  59. }
  60. if (res === undefined) res = origChecker.apply(this, arguments);
  61. return res;
  62. };
  63. }
  64.  
  65. // Override video element canPlayType() function
  66. const proto = (HTMLVideoElement || 0).prototype;
  67. if (proto && typeof proto.canPlayType == 'function') {
  68. proto.canPlayType = makeModifiedTypeChecker(proto.canPlayType);
  69. }
  70.  
  71. // Override media source extension isTypeSupported() function
  72. const mse = window.MediaSource;
  73. // Check for MSE support before use
  74. if (mse && typeof mse.isTypeSupported == 'function') {
  75. mse.isTypeSupported = makeModifiedTypeChecker(mse.isTypeSupported);
  76. }
  77.  
  78. };
  79.  
  80. supportedFormatsConfig();
  81. }
  82.  
  83. if (typeof GM === 'undefined' || typeof GM.getValue === 'undefined') throw new DOMException("Please Update your browser", "NotSupportedError");
  84.  
  85. const isEnable = await GM.getValue("isEnable_aWsjF", true);
  86. if (typeof isEnable !== 'boolean') throw new DOMException("Please Update your browser", "NotSupportedError");
  87. if (isEnable) {
  88. const element = document.createElement('button');
  89. element.setAttribute('onclick', `(${pageInjectionCode})()`);
  90. element.click();
  91. }
  92.  
  93. GM_registerMenuCommand(`Turn ${isEnable ? 'OFF' : 'ON'} YouTube Audio Mode`, async function () {
  94. await GM.setValue("isEnable_aWsjF", !isEnable);
  95. location.reload();
  96. });
  97.  
  98. let pageCounter = 0;
  99. isEnable && document.addEventListener('yt-page-data-fetched', async (evt) => {
  100.  
  101. const t = ++pageCounter;
  102. const pageFetchedDataLocal = evt.detail;
  103. let isLiveNow;
  104. try {
  105. isLiveNow = pageFetchedDataLocal.pageData.playerResponse.microformat.playerMicroformatRenderer.liveBroadcastDetails.isLiveNow;
  106. } catch (e) { }
  107. if (isLiveNow === true) {
  108. requestAnimationFrame(async () => {
  109. if (t !== pageCounter) return;
  110. if (confirm("Live Video is detected. Press OK to disable YouTube Audio Mode.")) {
  111. await GM.setValue("isEnable_aWsjF", !isEnable);
  112. location.reload();
  113. }
  114. });
  115. }
  116.  
  117. }, false);
  118.  
  119.  
  120. })();