Greasy Fork is available in English.
No Video Streaming
- // ==UserScript==
- // @name YouTube Music: Audio Only
- // @description No Video Streaming
- // @description:en No Video Streaming
- // @description:ja No Video Streaming
- // @description:zh-TW No Video Streaming
- // @description:zh-CN No Video Streaming
- // @namespace UserScript
- // @version 0.1.20
- // @author CY Fung
- // @match https://music.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
- // @require https://cdn.jsdelivr.net/gh/cyfung1031/userscript-supports@5d83d154956057bdde19e24f95b332cb9a78fcda/library/default-trusted-type-policy.js
- // @license MIT
- // @compatible chrome
- // @compatible firefox
- // @compatible opera
- // @compatible edge
- // @compatible safari
- // @allFrames true
- //
- // ==/UserScript==
- (async function () {
- 'use strict';
- const defaultPolicy = (typeof trustedTypes !== 'undefined' && trustedTypes.defaultPolicy) || { createHTML: s => s };
- function createHTML(s) {
- return defaultPolicy.createHTML(s);
- }
- let trustHTMLErr = null;
- try {
- document.createElement('div').innerHTML = createHTML('1');
- } catch (e) {
- trustHTMLErr = e;
- }
- if (trustHTMLErr) {
- console.log(`trustHTMLErr`, trustHTMLErr);
- trustHTMLErr(); // exit userscript
- }
- /** @type {globalThis.PromiseConstructor} */
- const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
- if (typeof AbortSignal === 'undefined') throw new DOMException("Please update your browser.", "NotSupportedError");
- async function confirm(message) {
- // Create the HTML for the dialog
- if (!document.body) return;
- let dialog = document.getElementById('confirmDialog794');
- if (!dialog) {
- const dialogHTML = `
- <div id="confirmDialog794" class="dialog-style" style="display: block;">
- <div class="confirm-box">
- <p>${message}</p>
- <div class="confirm-buttons">
- <button id="confirmBtn">Confirm</button>
- <button id="cancelBtn">Cancel</button>
- </div>
- </div>
- </div>
- `;
- // Append the dialog to the document body
- document.body.insertAdjacentHTML('beforeend', createHTML(dialogHTML));
- dialog = document.getElementById('confirmDialog794');
- }
- // Return a promise that resolves or rejects based on the user's choice
- return new Promise((resolve) => {
- document.getElementById('confirmBtn').onclick = () => {
- resolve(true);
- cleanup();
- };
- document.getElementById('cancelBtn').onclick = () => {
- resolve(false);
- cleanup();
- };
- function cleanup() {
- dialog && dialog.remove();
- dialog = null;
- }
- });
- }
- if (location.pathname === '/live_chat' || location.pathname === 'live_chat_replay') return;
- const kEventListener = (evt) => {
- if (document.documentElement.hasAttribute('forceRefresh032')) {
- evt.stopImmediatePropagation();
- evt.stopPropagation();
- }
- }
- window.addEventListener('beforeunload', kEventListener, false);
- const pageInjectionCode = function () {
- const A_D_B_Y_PASS = true;
- if (typeof AbortSignal === 'undefined') throw new DOMException("Please update your browser.", "NotSupportedError");
- const URL = window.URL || new Function('return URL')();
- const createObjectURL = URL.createObjectURL.bind(URL);
- /** @type {globalThis.PromiseConstructor} */
- const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
- const PromiseExternal = ((resolve_, reject_) => {
- const h = (resolve, reject) => { resolve_ = resolve; reject_ = reject };
- return class PromiseExternal extends Promise {
- constructor(cb = h) {
- super(cb);
- if (cb === h) {
- /** @type {(value: any) => void} */
- this.resolve = resolve_;
- /** @type {(reason?: any) => void} */
- this.reject = reject_;
- }
- }
- };
- })();
- const createPipeline = () => {
- let pipelineMutex = Promise.resolve();
- const pipelineExecution = fn => {
- return new Promise((resolve, reject) => {
- pipelineMutex = pipelineMutex.then(async () => {
- let res;
- try {
- res = await fn();
- } catch (e) {
- console.log(e);
- reject(e);
- }
- resolve(res);
- }).catch(console.warn);
- });
- };
- return pipelineExecution;
- }
- const observablePromise = (proc, timeoutPromise) => {
- let promise = null;
- return {
- obtain() {
- if (!promise) {
- promise = new Promise(resolve => {
- let mo = null;
- const f = () => {
- let t = proc();
- if (t) {
- mo.disconnect();
- mo.takeRecords();
- mo = null;
- resolve(t);
- }
- }
- mo = new MutationObserver(f);
- mo.observe(document, { subtree: true, childList: true })
- f();
- timeoutPromise && timeoutPromise.then(() => {
- resolve(null)
- });
- });
- }
- return promise
- }
- }
- }
- const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);
- const prototypeInherit = (d, b) => {
- const m = Object.getOwnPropertyDescriptors(b);
- for (const p in m) {
- if (!Object.getOwnPropertyDescriptor(d, p)) {
- Object.defineProperty(d, p, m[p]);
- }
- }
- };
- let setTimeout_ = setTimeout;
- let clearTimeout_ = clearTimeout;
- const delayPn = delay => new Promise((fn => setTimeout_(fn, delay)));
- const mockEvent = (o, elem) => {
- o = o || {};
- elem = elem || null;
- return {
- preventDefault: () => { },
- stopPropagation: () => { },
- stopImmediatePropagation: () => { },
- returnValue: true,
- target: elem,
- srcElement: elem,
- defaultPrevented: false,
- cancelable: true,
- timeStamp: performance.now(),
- ...o
- }
- };
- const generalRegister = (prop, symbol, checker, pg) => {
- const objSet = new Set();
- let done = false;
- const f = (o) => {
- const ct = o.constructor;
- const proto = ct.prototype;
- if (!done && proto && ct !== Function && ct !== Object && checker(proto)) {
- done = true;
- delete Object.prototype[prop];
- objSet.delete(proto);
- objSet.delete(o);
- for (const obj of objSet) {
- obj[prop] = obj[symbol];
- delete obj[symbol];
- }
- objSet.clear();
- Object.defineProperty(proto, prop, pg);
- return proto;
- }
- return false;
- };
- Object.defineProperty(Object.prototype, prop, {
- get() {
- const p = f(this);
- if (p) {
- return p[prop];
- } else {
- return this[symbol];
- }
- },
- set(nv) {
- const p = f(this);
- if (p) {
- p[prop] = nv;
- } else {
- objSet.add(this);
- this[symbol] = nv;
- }
- return true;
- },
- enumerable: false,
- configurable: true
- });
- };
- if (!Object.defineProperty322 && typeof Object.defineProperty === 'function' && Object.defineProperty.length === 3) {
- // _definePropertyAccessor
- Object.defineProperty322 = Object.defineProperty;
- const st = new Set(
- [
- 'videoMode', 'hasAvSwitcher', 'isVideo',
- 'playbackMode', 'selectedItemHasVideo'
- ]
- );
- const defineProperty322 = Object.defineProperty322;
- if (defineProperty322) {
- Object.defineProperty = function (o, k, t) {
- if (typeof o.is === 'string') {
- if (!('configurable' in t) && typeof t.get === 'function' && typeof t.set === 'function') {
- t.configurable = true;
- if (st.has(k)) {
- t.set = function (e) {
- this._setPendingProperty(k, e, !0) && this._invalidateProperties()
- }
- }
- }
- }
- return defineProperty322(o, k, t);
- }
- }
- }
- const updateLastActiveTimeAsync = (player_) => {
- // TBC
- Promise.resolve().then(() => {
- if (typeof player_.updateLastActiveTime === 'function') {
- player_.updateLastActiveTime();
- }
- });
- };
- const attachOneTimeEvent = function (eventType, callback) {
- let kz = false;
- document.addEventListener(eventType, function (evt) {
- if (kz) return;
- kz = true;
- callback(evt);
- }, { capture: true, passive: true, once: true });
- }
- function removeTempObjectProp01() {
- delete Object.prototype['kevlar_non_watch_unified_player'];
- delete Object.prototype['kevlar_unified_player'];
- }
- function ytConfigFix(config__) {
- const config_ = config__;
- if (config_) {
- const playerKevlar = ((config_ || 0).WEB_PLAYER_CONTEXT_CONFIGS || 0).WEB_PLAYER_CONTEXT_CONFIG_ID_KEVLAR_WATCH || 0;
- if (playerKevlar) {
- // console.log(322, playerKevlar)
- playerKevlar.allowWoffleManagement = false;
- playerKevlar.cinematicSettingsAvailable = false;
- playerKevlar.showMiniplayerButton = false;
- playerKevlar.showMiniplayerUiWhenMinimized = false;
- playerKevlar.transparentBackground = false;
- playerKevlar.enableCsiLogging = false;
- playerKevlar.externalFullscreen = false;
- if (typeof playerKevlar.serializedExperimentFlags === 'string') {
- playerKevlar.serializedExperimentFlags = '';
- // playerKevlar.serializedExperimentFlags = playerKevlar.serializedExperimentFlags.replace(/[-\w]+=(\[\]|[.-\d]+|[_a-z]+|)(&|$)/g,'').replace(/&$/,'')
- }
- if (typeof playerKevlar.serializedExperimentIds === 'string') {
- playerKevlar.serializedExperimentIds = '';
- // playerKevlar.serializedExperimentIds = playerKevlar.serializedExperimentIds.replace(/\d+\s*(,\s*|$)/g,'')
- }
- }
- removeTempObjectProp01();
- let configs = config_.WEB_PLAYER_CONTEXT_CONFIGS || {};
- for (const [key, entry] of Object.entries(configs)) {
- if (entry && typeof entry.serializedExperimentFlags === 'string' && entry.serializedExperimentFlags.length > 16) {
- // prevent idle playback failure
- entry.serializedExperimentFlags = entry.serializedExperimentFlags.replace(/\b(html5_check_for_idle_network_interval_ms|html5_trigger_loader_when_idle_network|html5_sabr_fetch_on_idle_network_preloaded_players|html5_autonav_cap_idle_secs|html5_autonav_quality_cap|html5_disable_client_autonav_cap_for_onesie|html5_idle_rate_limit_ms|html5_sabr_fetch_on_idle_network_preloaded_players|html5_webpo_idle_priority_job|html5_server_playback_start_policy|html5_check_video_data_errors_before_playback_start|html5_check_unstarted|html5_check_queue_on_data_loaded)=([-_\w]+)(\&|$)/g, (_, a, b, c) => {
- return a + '00' + '=' + b + c;
- });
- }
- }
- const EXPERIMENT_FLAGS = config_.EXPERIMENT_FLAGS;
- if (EXPERIMENT_FLAGS) {
- EXPERIMENT_FLAGS.kevlar_unified_player = true;
- EXPERIMENT_FLAGS.kevlar_non_watch_unified_player = true;
- }
- const EXPERIMENTS_FORCED_FLAGS = config_.EXPERIMENTS_FORCED_FLAGS;
- if (EXPERIMENTS_FORCED_FLAGS) {
- EXPERIMENTS_FORCED_FLAGS.kevlar_unified_player = true;
- EXPERIMENTS_FORCED_FLAGS.kevlar_non_watch_unified_player = true;
- }
- }
- }
- Object.defineProperty(Object.prototype, 'kevlar_non_watch_unified_player', {
- get() {
- // console.log(501, this.constructor.prototype)
- return true;
- },
- set(nv) {
- return true;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(Object.prototype, 'kevlar_unified_player', {
- get() {
- // console.log(501, this.constructor.prototype)
- return true;
- },
- set(nv) {
- return true;
- },
- enumerable: false,
- configurable: true
- });
- let cw = 0;
- function fixThumbnailURL(src) {
- if (typeof src === 'string' && src.length >= 4) {
- let m = /\b[a-z0-9]{4,13}\.jpg\b/.exec(src);
- if (m && m[0]) {
- const t = m[0];
- let idx = src.indexOf(t);
- let nSrc = idx >= 0 ? src.substring(0, idx + t.length) : '';
- return nSrc;
- }
- }
- return src;
- }
- const avFix = async () => {
- // if (cw < 6) cw = 6;
- // const config_ = typeof yt !== 'undefined' ? (yt || 0).config_ : 0;
- // ytConfigFix(config_);
- const songImageThumbnail = document.querySelector('#song-image #thumbnail');
- if (songImageThumbnail) {
- if (songImageThumbnail.getAttribute('object-fit') !== 'CONTAIN') songImageThumbnail.setAttribute('object-fit', 'CONTAIN');
- mo2.observe(songImageThumbnail, { attributes: true });
- const img = HTMLElement.prototype.querySelector.call(songImageThumbnail, 'img#img[src]');
- if (img) {
- mo2.observe(img, { attributes: true });
- const src = img.getAttribute('src');
- let nSrc = fixThumbnailURL(src);
- if (nSrc !== src && nSrc && src) {
- // https://i.ytimg.com/vi/gcCqclvIcn4/sddefault.jpg?sqp=-oa&rs=A
- // https://i.ytimg.com/vi/gcCqclvIcn4/sddefault.jpg
- img.setAttribute('src', nSrc)
- }
- /*
- iurl: "default.jpg",
- iurlmq: "mqdefault.jpg",
- iurlhq: "hqdefault.jpg",
- iurlsd: "sddefault.jpg",
- iurlpop1: "pop1.jpg",
- iurlpop2: "pop2.jpg",
- iurlhq720: "hq720.jpg",
- iurlmaxres: "maxresdefault.jpg"
- */
- }
- }
- for (const s of document.querySelectorAll('[playback-mode][selected-item-has-video]')) {
- s.removeAttribute('selected-item-has-video');
- }
- for (const s of document.querySelectorAll('ytmusic-player-page')) {
- // s.setAttribute('has-av-switcher', '')
- s.removeAttribute('has-av-switcher')
- }
- for (const s of document.querySelectorAll('[video-mode]')) {
- s.removeAttribute('video-mode')
- }
- for (const ytElement of document.querySelectorAll('ytmusic-player-page')) {
- if (ytElement.is === 'ytmusic-player-page') {
- mo2.observe(ytElement, { attributes: true });
- const cnt = insp(ytElement);
- const cProto = cnt.constructor.prototype;
- if (!cProto.setFn322) {
- cProto.setFn322 = function () {
- if (this.videoMode === true) this.videoMode = false;
- // if (this.hasAvSwitcher === false) this.hasAvSwitcher = true;
- if (this.hasAvSwitcher === true) this.hasAvSwitcher = false;
- }
- }
- if (typeof cProto.computeShowAvSwitcher === 'function' && !cProto.computeShowAvSwitcher322) {
- cProto.computeShowAvSwitcher322 = cProto.computeShowAvSwitcher;
- cProto.computeShowAvSwitcher = function () {
- this.setFn322();
- return this.computeShowAvSwitcher322(...arguments);
- }
- }
- cnt.setFn322();
- }
- }
- for (const ytElement of document.querySelectorAll('ytmusic-av-toggle')) {
- if (ytElement.is === 'ytmusic-av-toggle') {
- mo2.observe(ytElement, { attributes: true });
- const cnt = insp(ytElement);
- // cnt.toggleDisabled = false;
- const cProto = cnt.constructor.prototype;
- if (!cProto.setFn322) {
- cProto.setFn322 = function () {
- if (this.mustPlayAudioOnly === false) this.mustPlayAudioOnly = true;
- // if(this.isVideo === true) this.isVideo = false;
- // if(this.playbackMode !== 'ATV_PREFERRED') this.playbackMode = 'ATV_PREFERRED';
- if (this.selectedItemHasVideo === true) this.selectedItemHasVideo = false;
- }
- }
- if (typeof cProto.computeToggleDisabled === 'function' && !cProto.computeToggleDisabled322) {
- cProto.computeToggleDisabled322 = cProto.computeToggleDisabled;
- cProto.computeToggleDisabled = function () {
- this.setFn322();
- return this.computeToggleDisabled322(...arguments);
- }
- }
- cnt.setFn322();
- // cnt.computeToggleDisabled = ()=>{};
- // if(cnt.isVideo === true) cnt.isVideo = false;
- // cnt.mustPlayAudioOnly = false;
- // cnt.playbackMode = 'ATV_PREFERRED';
- // if(cnt.selectedItemHasVideo === true) cnt.selectedItemHasVideo = false;
- if (!cnt.onVideoAvToggleTap322 && typeof cnt.onVideoAvToggleTap === 'function' && cnt.onVideoAvToggleTap.length === 0) {
- cnt.onVideoAvToggleTap322 = cnt.onVideoAvToggleTap;
- cnt.onVideoAvToggleTap = function () {
- const pr = new Proxy(this, {
- get(target, prop) {
- // if (prop === 'mustPlayAudioOnly') return true;
- // if (prop === 'playbackMode') return 'NONE';
- if (prop === 'selectedItemHasVideo') return false;
- // if (prop === 'isVideo') return false;
- let v = target[prop];
- // if (typeof v === 'function') return () => { };
- return v;
- },
- set(target, prop, value) {
- return true;
- }
- });
- this.onVideoAvToggleTap322.call(pr);
- }
- }
- if (!cnt.onSongAvToggleTap322 && typeof cnt.onSongAvToggleTap === 'function' && cnt.onSongAvToggleTap.length === 0) {
- cnt.onSongAvToggleTap322 = cnt.onSongAvToggleTap;
- cnt.onSongAvToggleTap = function () {
- const pr = new Proxy(this, {
- get(target, prop) {
- // if (prop === 'mustPlayAudioOnly') return true;
- // if (prop === 'playbackMode') return 'NONE';
- if (prop === 'selectedItemHasVideo') return false;
- // if (prop === 'isVideo') return false;
- let v = target[prop];
- // if (typeof v === 'function') return () => { };
- return v;
- },
- set(target, prop, value) {
- return true;
- }
- });
- this.onSongAvToggleTap322.call(pr);
- }
- }
- cnt.onSongAvToggleTap();
- // cnt.playbackMode = 'ATV_PREFERRED';
- }
- }
- if (A_D_B_Y_PASS) Promise.resolve().then(() => {
- // skip a$d.s
- const isAdsPlaying = document.querySelector('[is-advertisement-playing]');
- if (isAdsPlaying) {
- const audios = document.querySelectorAll('audio');
- const onlyAudio = audios.length === 1 ? audios[0] : 0;
- if (onlyAudio instanceof HTMLMediaElement && onlyAudio.paused === false && onlyAudio.currentTime > 0 && onlyAudio.duration > onlyAudio.currentTime && onlyAudio.playbackRate < 1.2 && onlyAudio.playbackRate > 0.8){
- try {
- if (onlyAudio.duration - onlyAudio.currentTime > 0.7) onlyAudio.currentTime += onlyAudio.duration - onlyAudio.currentTime - 0.52 - 0.14 * Math.random();
- } catch (e) { }
- onlyAudio.playbackRate = 15 - Math.random() * 0.04;
- }
- }
- }).catch(console.warn);
- }
- const mo = new MutationObserver(() => {
- if (cw > 0) {
- cw--;
- avFix();
- }
- });
- mo.observe(document, { childList: true, subtree: true });
- const mo2 = new MutationObserver(() => {
- if (cw < 1) cw = 1;
- if (cw > 0) {
- cw--;
- avFix();
- }
- });
- document.addEventListener('fullscreenchange', () => {
- if (cw < 3) cw = 3;
- });
- document.addEventListener('yt-navigate-start', () => {
- if (cw < 3) cw = 3;
- });
- document.addEventListener('yt-navigate-finish', () => {
- if (cw < 6) cw = 6;
- avFix();
- });
- document.addEventListener('yt-navigate-cache', () => {
- if (cw < 3) cw = 3;
- });
- window.addEventListener("updateui", function () {
- if (cw < 3) cw = 3;
- });
- window.addEventListener("resize", () => {
- if (cw < 3) cw = 3;
- });
- window.addEventListener("state-navigatestart", function () {
- if (cw < 3) cw = 3;
- });
- window.addEventListener("state-navigateend", () => {
- if (cw < 6) cw = 6;
- avFix();
- })
- let cv = null;
- document.addEventListener('durationchange', (evt) => {
- const target = (evt || 0).target;
- if (!(target instanceof HTMLMediaElement)) return;
- const targetClassList = target.classList || 0;
- const isPlayerVideo = typeof targetClassList.contains === 'function' ? targetClassList.contains('video-stream') && targetClassList.contains('html5-main-video') : false;
- if (isPlayerVideo) {
- if (target.readyState === 1 && target.networkState === 2) {
- target.__spfgs__ = true;
- if (cv) {
- cv.resolve();
- cv = null;
- }
- } else {
- target.__spfgs__ = false;
- }
- if (cw < 6) cw = 6;
- avFix();
- }
- }, true);
- (() => {
- XMLHttpRequest = (() => {
- const XMLHttpRequest_ = XMLHttpRequest;
- if ('__xmMc8__' in XMLHttpRequest_.prototype) return XMLHttpRequest_;
- const url0 = createObjectURL(new Blob([], { type: 'text/plain' }));
- const c = class XMLHttpRequest extends XMLHttpRequest_ {
- constructor(...args) {
- super(...args);
- }
- open(method, url, ...args) {
- let skip = false;
- if (!url || typeof url !== 'string') skip = true;
- else if (typeof url === 'string') {
- let turl = url[0] === '/' ? `.youtube.com${url}` : `${url}`;
- if (turl.includes('googleads') || turl.includes('doubleclick.net')) {
- skip = true;
- } else if (turl.includes('.youtube.com/pagead/')) {
- skip = true;
- } else if (turl.includes('.youtube.com/ptracking')) {
- skip = true;
- } else if (turl.includes('.youtube.com/api/stats/')) { // /api/stats/
- // skip = true; // for user activity logging e.g. watched videos
- } else if (turl.includes('play.google.com/log')) {
- skip = true;
- } else if (turl.includes('.youtube.com//?')) { // //?cpn=
- skip = true;
- }
- }
- if (!skip) {
- this.__xmMc8__ = 1;
- return super.open(method, url, ...args);
- } else {
- this.__xmMc8__ = 2;
- return super.open('GET', url0);
- }
- }
- send(...args) {
- if (this.__xmMc8__ === 1) {
- return super.send(...args);
- } else if (this.__xmMc8__ === 2) {
- return super.send();
- } else {
- console.log('xhr warning');
- return super.send(...args);
- }
- }
- }
- c.prototype.__xmMc8__ = 0;
- return c;
- })();
- const s7 = Symbol();
- const f7 = () => true;
- !window.canRetry9048 && generalRegister('canRetry', s7, (p) => {
- return typeof p.onStateChange === 'function' && typeof p.dispose === 'function' && typeof p.hide === 'undefined' && typeof p.show === 'undefined' && typeof p.isComplete === 'undefined' && typeof p.getDuration === 'undefined'
- }, {
- get() {
- if ('logger' in this && 'policy' in this && 'xhr' && this) {
- if (this.errorMessage && typeof this.errorMessage === 'string' && this.errorMessage.includes('XMLHttpRequest') && this.errorMessage.includes('Invalid URL')) { // "SyntaxError_Failed to execute 'open' on 'XMLHttpRequest': Invalid URL"
- // OKAY !
- console.log('canRetry05 - ', this.errorMessage)
- return f7;
- }
- // console.log(this)
- console.log('canRetry02 - ', this.errorMessage, this)
- } else {
- console.log('canRetry ERR - ', this.errorMessage)
- }
- return this[s7];
- },
- set(nv) {
- this[s7] = nv;
- return true;
- },
- enumerable: false,
- configurable: true
- });
- window.canRetry9048 = 1;
- })();
- attachOneTimeEvent('yt-action', function () {
- const config_ = typeof yt !== 'undefined' ? (yt || 0).config_ : 0;
- ytConfigFix(config_);
- });
- let prepared = false;
- function prepare() {
- if (prepared) return;
- prepared = true;
- if (typeof _yt_player !== 'undefined' && _yt_player && typeof _yt_player === 'object') {
- for (const [k, v] of Object.entries(_yt_player)) {
- const p = typeof v === 'function' ? v.prototype : 0;
- if (p
- && typeof p.clone === 'function'
- && typeof p.get === 'function' && typeof p.set === 'function'
- && typeof p.isEmpty === 'undefined' && typeof p.forEach === 'undefined'
- && typeof p.clear === 'undefined'
- ) {
- key = k;
- }
- }
- }
- if (key) {
- const ClassX = _yt_player[key];
- _yt_player[key] = class extends ClassX {
- constructor(...args) {
- if (typeof args[0] === 'string' && args[0].startsWith('http://')) args[0] = '';
- super(...args);
- }
- }
- _yt_player[key].luX1Y = 1;
- prototypeInherit(_yt_player[key].prototype, ClassX.prototype);
- }
- }
- let s3 = Symbol();
- generalRegister('deviceIsAudioOnly', s3, (p) => {
- return typeof p.getPlayerType === 'function' && typeof p.getVideoEmbedCode === 'function' && typeof p.getVideoUrl === 'function' && !p.onCueRangeEnter && !p.getVideoData && !('ATTRIBUTE_NODE' in p)
- }, {
- get() {
- return this[s3];
- },
- set(nv) {
- if (typeof nv === 'boolean') this[s3] = true;
- else this[s3] = undefined;
- prepare();
- return true;
- },
- enumerable: false,
- configurable: true
- });
- let s1 = Symbol();
- let s2 = Symbol();
- Object.defineProperty(Object.prototype, 'defraggedFromSubfragments', {
- get() {
- // console.log(501, this.constructor.prototype)
- return undefined;
- },
- set(nv) {
- return true;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(Object.prototype, 'hasSubfragmentedFmp4', {
- get() {
- // console.log(502, this.constructor.prototype)
- return this[s1];
- },
- set(nv) {
- if (typeof nv === 'boolean') this[s1] = false;
- else this[s1] = undefined;
- return true;
- },
- enumerable: false,
- configurable: true
- });
- Object.defineProperty(Object.prototype, 'hasSubfragmentedWebm', {
- get() {
- // console.log(503, this.constructor.prototype)
- return this[s2];
- },
- set(nv) {
- if (typeof nv === 'boolean') this[s2] = false;
- else this[s2] = undefined;
- 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 isTyp###pported() function
- const mse = window.MediaSource;
- // Check for MSE support before use
- if (mse && typeof mse.isTyp###pported == 'function') {
- mse.isTyp###pported = makeModifiedTypeChecker(mse.isTyp###pported);
- }
- };
- // supportedFormatsConfig(); // avoid issue due to failure on only video source (like ads)
- }
- 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', createHTML(`(${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;
- let busy = false;
- window.addEventListener('message', (evt) => {
- const v = ((evt || 0).data || 0).ZECxh;
- if (typeof v === 'boolean') {
- if (messageCount > 1e9) messageCount = 9;
- const t = ++messageCount;
- if (v && isEnable) {
- requestAnimationFrame(async () => {
- if (t !== messageCount) return;
- if (busy) return;
- busy = true;
- if (await confirm("Livestream is detected. Press OK to disable YouTube Audio Mode.")) {
- await GM.setValue("isEnable_aWsjF", !isEnable);
- location.reload();
- }
- busy = false;
- });
- }
- }
- });
- const pLoad = new Promise(resolve => {
- if (document.readyState !== 'loading') {
- resolve();
- } else {
- window.addEventListener("DOMContentLoaded", resolve, false);
- }
- });
- function contextmenuInfoItemAppearedFn(target) {
- const btn = target.closest('[role="option"]');
- if (!btn) return;
- if (btn.parentNode.querySelector('[role="option"].audio-only-toggle-btn')) return;
- document.documentElement.classList.add('with-audio-only-toggle-btn');
- const newBtn = btn.cloneNode(true);
- const h = () => {
- newBtn.classList.remove('iron-selected');
- newBtn.classList.remove('focused');
- newBtn.removeAttribute('iron-selected');
- newBtn.removeAttribute('focused');
- let a = newBtn.querySelector('a');
- if (a) a.removeAttribute('href');
- newBtn.classList.add('audio-only-toggle-btn');
- }
- h();
- async function reloadPage() {
- await GM.setValue("isEnable_aWsjF", !isEnable);
- document.documentElement.setAttribute('forceRefresh032', '');
- location.reload();
- }
- newBtn.addEventListener('click', (evt) => {
- evt.preventDefault();
- evt.stopImmediatePropagation();
- evt.stopPropagation();
- reloadPage();
- }, true);
- btn.parentNode.insertBefore(newBtn, null);
- // let t;
- // let h = 0;
- // t = btn.closest('.ytp-panel-menu[style*="height"]');
- // if (t) t.style.height = t.scrollHeight + 'px';
- // t = btn.closest('.ytp-panel[style*="height"]');
- // if (t) t.style.height = (h = t.scrollHeight) + 'px';
- // t = btn.closest('.ytp-popup.ytp-contextmenu[style*="height"]');
- // if (t && h > 0) t.style.height = h + 'px';
- const f = () => {
- h();
- const mx = newBtn.querySelector('yt-formatted-string');
- if (mx) {
- mx.removeAttribute('is-empty');
- mx.textContent = `Turn ${isEnable ? 'OFF' : 'ON'} YouTube Audio Mode`;
- }
- let t;
- t = btn.closest('ytmusic-menu-popup-renderer[style*="max-height"]');
- if (t) t.style.maxHeight = t.scrollHeight + 'px';
- }
- f();
- setTimeout(f, 40);
- }
- function mobileMenuItemAppearedFn(target) {
- }
- pLoad.then(() => {
- document.addEventListener('animationstart', (evt) => {
- const animationName = evt.animationName;
- if (!animationName) return;
- if (animationName === 'contextmenuInfoItemAppeared') contextmenuInfoItemAppearedFn(evt.target);
- if (animationName === 'mobileMenuItemAppeared') mobileMenuItemAppearedFn(evt.target);
- }, true);
- const style = document.createElement('style');
- style.id = 'fm9v0';
- style.textContent = `
- .html5-video-player {
- background-color: black;
- }
- #song-image.ytmusic-player {
- background-color: black;
- }
- ytmusic-player-page:not([player-fullscreened]) #main-panel.style-scope.ytmusic-player-page[style*="padding"] {
- padding: 0px 0px !important;
- box-sizing: border-box;
- }
- ytmusic-player-page:not([player-fullscreened]) ytmusic-player#player.style-scope.ytmusic-player-page {
- max-height: 100%;
- margin-top: calc(-1*var(--ytmusic-player-page-vertical-padding));
- box-sizing: border-box;
- }
- /* #movie_player > .ytp-iv-video-content {
- pointer-events: none; // allow clicking
- } */
- #movie_player > .html5-video-container:not(:empty) {
- box-sizing: border-box;
- height: 100%;
- }
- @keyframes mobileMenuItemAppeared {
- 0% {
- background-position-x: 3px;
- }
- 100% {
- background-position-x: 4px;
- }
- }
- ytm-select.player-speed-settings ~ ytm-menu-item:last-of-type {
- animation: mobileMenuItemAppeared 1ms linear 0s 1 normal forwards;
- }
- @keyframes contextmenuInfoItemAppeared {
- 0% {
- background-position-x: 3px;
- }
- 100% {
- background-position-x: 4px;
- }
- }
- ytmusic-popup-container.ytmusic-app ytmusic-menu-popup-renderer tp-yt-paper-listbox > [role="option"]:first-child {
- animation: contextmenuInfoItemAppeared 1ms linear 0s 1 normal forwards;
- }
- #confirmDialog794 {
- z-index:999999 !important;
- display: none;
- /* Hidden by default */
- position: fixed;
- /* Stay in place */
- z-index: 1;
- /* Sit on top */
- left: 0;
- top: 0;
- width: 100%;
- /* Full width */
- height: 100%;
- /* Full height */
- overflow: auto;
- /* Enable scroll if needed */
- background-color: rgba(0,0,0,0.4);
- /* Black w/ opacity */
- }
- #confirmDialog794 .confirm-box {
- position:relative;
- color: black;
- z-index:999999 !important;
- background-color: #fefefe;
- margin: 15% auto;
- /* 15% from the top and centered */
- padding: 20px;
- border: 1px solid #888;
- width: 30%;
- /* Could be more or less, depending on screen size */
- box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
- }
- #confirmDialog794 .confirm-buttons {
- text-align: right;
- }
- #confirmDialog794 button {
- margin-left: 10px;
- }
- `
- document.head.appendChild(style);
- })
- })();