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.5
  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. // embed & desktop & mobile
  33. window.XMLHttpRequest = ((XMLHttpRequest_) => {
  34.  
  35. class XMLHttpRequest extends XMLHttpRequest_ {
  36.  
  37. constructor(...args) {
  38. super(...args);
  39. }
  40. open(method, url, ...args) {
  41. if (typeof url === 'string' && url.length > 24 && url.includes('/videoplayback?') && url.replace('?', '&').includes('&source=')) {
  42. window.postMessage({ ZECxh: url.includes('source=yt_live_broadcast') }, "*");
  43. }
  44. return super.open(method, url, ...args);
  45. }
  46. }
  47.  
  48. return XMLHttpRequest;
  49.  
  50. })(window.XMLHttpRequest);
  51.  
  52. // desktop only
  53. // document.addEventListener('yt-page-data-fetched', async (evt) => {
  54.  
  55. // const pageFetchedDataLocal = evt.detail;
  56. // let isLiveNow;
  57. // try {
  58. // isLiveNow = pageFetchedDataLocal.pageData.playerResponse.microformat.playerMicroformatRenderer.liveBroadcastDetails.isLiveNow;
  59. // } catch (e) { }
  60. // window.postMessage({ ZECxh: isLiveNow === true }, "*");
  61.  
  62. // }, false);
  63.  
  64. Object.defineProperty(Object.prototype, 'deviceIsAudioOnly', {
  65. get() {
  66. console.log(1238)
  67. return true;
  68. },
  69. set(nv) {
  70. return true;
  71. },
  72. enumerable: false,
  73. configurable: true
  74. });
  75.  
  76. const supportedFormatsConfig = () => {
  77.  
  78. function typeTest(type) {
  79. if (typeof type === 'string' && type.startsWith('video/')) {
  80. return false;
  81. }
  82. }
  83.  
  84. // return a custom MIME type checker that can defer to the original function
  85. function makeModifiedTypeChecker(origChecker) {
  86. // Check if a video type is allowed
  87. return function (type) {
  88. let res = undefined;
  89. if (type === undefined) res = false;
  90. else {
  91. res = typeTest.call(this, type);
  92. }
  93. if (res === undefined) res = origChecker.apply(this, arguments);
  94. return res;
  95. };
  96. }
  97.  
  98. // Override video element canPlayType() function
  99. const proto = (HTMLVideoElement || 0).prototype;
  100. if (proto && typeof proto.canPlayType == 'function') {
  101. proto.canPlayType = makeModifiedTypeChecker(proto.canPlayType);
  102. }
  103.  
  104. // Override media source extension isTypeSupported() function
  105. const mse = window.MediaSource;
  106. // Check for MSE support before use
  107. if (mse && typeof mse.isTypeSupported == 'function') {
  108. mse.isTypeSupported = makeModifiedTypeChecker(mse.isTypeSupported);
  109. }
  110.  
  111. };
  112.  
  113. supportedFormatsConfig();
  114. }
  115.  
  116. const isEnable = (typeof GM !== 'undefined' && typeof GM.getValue === 'function') ? (await GM.getValue("isEnable_aWsjF", true)) : null;
  117. if (typeof isEnable !== 'boolean') throw new DOMException("Please Update your browser", "NotSupportedError");
  118. if (isEnable) {
  119. const element = document.createElement('button');
  120. element.setAttribute('onclick', `(${pageInjectionCode})()`);
  121. element.click();
  122. }
  123.  
  124. GM_registerMenuCommand(`Turn ${isEnable ? 'OFF' : 'ON'} YouTube Audio Mode`, async function () {
  125. await GM.setValue("isEnable_aWsjF", !isEnable);
  126. location.reload();
  127. });
  128.  
  129. let messageCount = 0;
  130. window.addEventListener('message', (evt) => {
  131.  
  132. const v = ((evt || 0).data || 0).ZECxh;
  133. if (typeof v === 'boolean') {
  134. const t = ++messageCount;
  135. if (v && isEnable) {
  136. requestAnimationFrame(async () => {
  137. if (t !== messageCount) return;
  138. if (confirm("Livestream is detected. Press OK to disable YouTube Audio Mode.")) {
  139. await GM.setValue("isEnable_aWsjF", !isEnable);
  140. location.reload();
  141. }
  142. });
  143. }
  144. }
  145.  
  146. });
  147.  
  148.  
  149. })();