Greasy Fork 还支持 简体中文。

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.6
  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. return true;
  67. },
  68. set(nv) {
  69. return true;
  70. },
  71. enumerable: false,
  72. configurable: true
  73. });
  74.  
  75. const supportedFormatsConfig = () => {
  76.  
  77. function typeTest(type) {
  78. if (typeof type === 'string' && type.startsWith('video/')) {
  79. return false;
  80. }
  81. }
  82.  
  83. // return a custom MIME type checker that can defer to the original function
  84. function makeModifiedTypeChecker(origChecker) {
  85. // Check if a video type is allowed
  86. return function (type) {
  87. let res = undefined;
  88. if (type === undefined) res = false;
  89. else {
  90. res = typeTest.call(this, type);
  91. }
  92. if (res === undefined) res = origChecker.apply(this, arguments);
  93. return res;
  94. };
  95. }
  96.  
  97. // Override video element canPlayType() function
  98. const proto = (HTMLVideoElement || 0).prototype;
  99. if (proto && typeof proto.canPlayType == 'function') {
  100. proto.canPlayType = makeModifiedTypeChecker(proto.canPlayType);
  101. }
  102.  
  103. // Override media source extension isTypeSupported() function
  104. const mse = window.MediaSource;
  105. // Check for MSE support before use
  106. if (mse && typeof mse.isTypeSupported == 'function') {
  107. mse.isTypeSupported = makeModifiedTypeChecker(mse.isTypeSupported);
  108. }
  109.  
  110. };
  111.  
  112. supportedFormatsConfig();
  113. }
  114.  
  115. const isEnable = (typeof GM !== 'undefined' && typeof GM.getValue === 'function') ? (await GM.getValue("isEnable_aWsjF", true)) : null;
  116. if (typeof isEnable !== 'boolean') throw new DOMException("Please Update your browser", "NotSupportedError");
  117. if (isEnable) {
  118. const element = document.createElement('button');
  119. element.setAttribute('onclick', `(${pageInjectionCode})()`);
  120. element.click();
  121. }
  122.  
  123. GM_registerMenuCommand(`Turn ${isEnable ? 'OFF' : 'ON'} YouTube Audio Mode`, async function () {
  124. await GM.setValue("isEnable_aWsjF", !isEnable);
  125. location.reload();
  126. });
  127.  
  128. let messageCount = 0;
  129. window.addEventListener('message', (evt) => {
  130.  
  131. const v = ((evt || 0).data || 0).ZECxh;
  132. if (typeof v === 'boolean') {
  133. const t = ++messageCount;
  134. if (v && isEnable) {
  135. requestAnimationFrame(async () => {
  136. if (t !== messageCount) return;
  137. if (confirm("Livestream is detected. Press OK to disable YouTube Audio Mode.")) {
  138. await GM.setValue("isEnable_aWsjF", !isEnable);
  139. location.reload();
  140. }
  141. });
  142. }
  143. }
  144.  
  145. });
  146.  
  147.  
  148. })();