// ==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.19
// @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
// @license MIT
// @compatible chrome
// @compatible firefox
// @compatible opera
// @compatible edge
// @compatible safari
// @allFrames true
//
// ==/UserScript==
(async function () {
'use strict';
!window.TTP && (() => {
// credit to Benjamin Philipp
// original source: https://greasyfork.org/en/scripts/433051-trusted-types-helper
// --------------------------------------------------- Trusted Types Helper ---------------------------------------------------
const overwrite_default = false; // If a default policy already exists, it might be best not to overwrite it, but to try and set a custom policy and use it to manually generate trusted types. Try at your own risk
const prefix = `TTP`;
var passThroughFunc = function (string, sink) {
return string; // Anything passing through this function will be returned without change
}
var TTPName = "passthrough";
var TTP_default, TTP = { createHTML: passThroughFunc, createScript: passThroughFunc, createScriptURL: passThroughFunc }; // We can use TTP.createHTML for all our assignments even if we don't need or even have Trusted Types; this should make fallbacks and polyfills easy
var needsTrustedHTML = false;
function doit() {
try {
if (typeof window.isSecureContext !== 'undefined' && window.isSecureContext) {
if (window.trustedTypes && window.trustedTypes.createPolicy) {
needsTrustedHTML = true;
if (trustedTypes.defaultPolicy) {
log("TT Default Policy exists");
if (overwrite_default)
TTP = window.trustedTypes.createPolicy("default", TTP);
else
TTP = window.trustedTypes.createPolicy(TTPName, TTP); // Is the default policy permissive enough? If it already exists, best not to overwrite it
TTP_default = trustedTypes.defaultPolicy;
log("Created custom passthrough policy, in case the default policy is too restrictive: Use Policy '" + TTPName + "' in var 'TTP':", TTP);
}
else {
TTP_default = TTP = window.trustedTypes.createPolicy("default", TTP);
}
log("Trusted-Type Policies: TTP:", TTP, "TTP_default:", TTP_default);
}
}
} catch (e) {
log(e);
}
}
function log(...args) {
if ("undefined" != typeof (prefix) && !!prefix)
args = [prefix + ":", ...args];
if ("undefined" != typeof (debugging) && !!debugging)
args = [...args, new Error().stack.replace(/^\s*(Error|Stack trace):?\n/gi, "").replace(/^([^\n]*\n)/, "\n")];
console.log(...args);
}
doit();
// --------------------------------------------------- Trusted Types Helper ---------------------------------------------------
window.TTP = TTP;
})();
function createHTML(s) {
if (typeof TTP !== 'undefined' && typeof TTP.createHTML === 'function') return TTP.createHTML(s);
return 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 isTypeSupported() function
const mse = window.MediaSource;
// Check for MSE support before use
if (mse && typeof mse.isTypeSupported == 'function') {
mse.isTypeSupported = makeModifiedTypeChecker(mse.isTypeSupported);
}
};
// 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);
})
})();