// ==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 // @downloadURL none // ==/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(); } }); } } }); })();