volkan-k / Listen to Youtube

// ==UserScript==
// @name        Listen to Youtube
// @id          Volkan-K.Userscript.Listen-To-Youtube
// @namespace   volkan-k
// @description Removes video and plays mp3 on youtube.
// @author      Volkan K.
// @license     MIT
// @copyright   2018+ Volkan K.
// @version     3.0.2
// @domain      youtube.com
// @domain      www.youtube.com
// @include     http://youtube.com/*
// @include     http://www.youtube.com/*
// @include     https://youtube.com/*
// @include     https://www.youtube.com/*
// @grant 		GM_xmlhttpRequest
// @grant 		GM_setValue
// @grant 		GM_getValue
// @grant 		unsafeWindow
// @require 	https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @require 	https://cdnjs.cloudflare.com/ajax/libs/mediaelement/4.2.9/mediaelement-and-player.min.js
// @run-at 		document-end
// @connect 	youtubemp3api.com
// @connect 	youtube.com
// ==/UserScript==
var debug_internal = 1; // 1=enable debug , 0 =disable debug
var use_youtube_player = 1; // 1=enable ytplayer, 0= disable ytplayer
var use_youtube_source = 1; // 1=use youtube source, 0=use external source.

var use_yt_config = 1;
//var DECODE_RULE=[21,-3,41,0,-1,21,-1]; // positive = swap , zero = reverse, negative=slice
var DECODE_RULE=[];
var audio_selector= "mime=audio";
var audio_quality="highest"; // highest or lowest, you either save bandwidth or not.
var STORAGE_URL='download-youtube-script-url';
var STORAGE_CODE='download-youtube-signature-code';
var isDecodeRuleUpdated=false;
var scriptURL=null;

this.$ = this.jQuery = jQuery.noConflict(true);

//console.log(unsafeWindow.ytplayer.config.args.adaptive_fmts); // for debug

var videoElements = $('video');
var videoElement = videoElements[0];

if ($("div[id='player-api']:not('.off-screen-target')").length > 0) {
	var target_node = "div[id='player-api']:not('.off-screen-target')";
} else {
	var target_node = "#player-container";
}

var VIDEO_ID = get_video_id_from_yturl(window.location.href);
window.VIDEO_ID = VIDEO_ID;
//console.log(VIDEO_ID);
//return ; // for debug 
if ($('meta[name="GM_VIDEO_ID"]').length == 0) {
	$("head").append('<meta name="GM_VIDEO_ID" content="' + VIDEO_ID + '"/>');
}

function debugLog(message) {
	if (debug_internal == 1) {
		console.log("USER-SCRIPT LISTEN-TO-YT | " + message);
	}
}

function isString(s) {
	return (typeof s === 'string' || s instanceof String);
}

function isInteger(n) {
	return (typeof n === 'number' && n % 1 == 0);
}

String.prototype.replaceAt = function(index, char) {
	var a = this.split("");
	a[index] = char;
	return a.join("");
}

function decryptSignature(sig) {
	function remove(sig, arr) {
		if (!arr || arr.length === 0) {
			arr = [6, 5, 4, -2, 1];
		}
		str = sig.split('');
		for (var i = 0; i < arr.length; i++) {
			str.splice(arr[i], 1);
		}
		str = str.join('');
		return str;
	}

	function swap(a, b) {
		var c = a[0];
		a[0] = a[b % a.length];
		a[b] = c;
		return a
	}

	function swap_new(string, position) {
		string = string.split('');
		var last = string[string.length - 1];
		string[position] = last;
		string = string.join('');
		return string;
	}

	function decode(sig, arr) { // encoded decryption
		if (!isString(sig)) return null;
		var sigA = sig.split('');
		for (var i = 0; i < arr.length; i++) {
			var act = arr[i];
			if (!isInteger(act)) return null;
			sigA = (act > 0) ? swap(sigA, act) : ((act == 0) ? sigA.reverse() : sigA.slice(-act));
		}
		var result = sigA.join('');
		return result;
	}

	if (sig == null) return '';
	var arr = DECODE_RULE;
	if (arr) {
		var sig2 = decode(sig, arr);
		//debugLog("DEBUG: encrypted signature = "+sig+", decrypted signature = "+sig2);
		if (sig2) return sig2;
	} else {
		/*setPref(STORAGE_URL, '');
		setPref(STORAGE_CODE, '');*/
	}
	return sig;
}

function remove_all_video_players() {
	if ($("video,iframe,div.html5-video-player").length < 1) {
		return;
	}
	$("div.html5-video-player").each(function() {
		//$(this).remove();
		$(this)[0].destroy();
		$(this).hide();
	});
	$('video').each(function() {
		$(this)[0].pause();
		$(this)[0].currentTime = 0;
		$(this).attr('src', '');
		$(this)[0].load();
		$(this)[0].innerHTML = '';
		$(this)[0].load();
		$(this).removeAttr('src');
		$(this)[0].load();
		//$(this).remove();
		$(this).hide();
	});
	$("iframe").each(function() {
		$(this).attr('src', 'https://www.youtube.com/html5');
		//$(this).remove();
		$(this).hide();
	});
	window.yt = null;
	window.ytplayer = null;
}

function pause_all_video_players() {
	$('video').not("[src*='googlevideo']").not("[src*='youtubemp3api']").each(function() {
		$(this)[0].pause();
		$(this)[0].currentTime = 0;
		/*if ($(this).attr('src').startsWith("blob:")){
			// causes problem with "autoplay next video" feature.
			$(this).attr('src', 'https://github.com/anars/blank-audio/raw/master/750-milliseconds-of-silence.mp3');
		}*/
		/*$(this).attr('src', '');
		$(this)[0].load();
		$(this)[0].innerHTML='';
		$(this)[0].load();
		$(this).removeAttr('src');
		$(this)[0].load();*/
		$(this).parents("div.html5-video-player").each(function() {
			$(this)[0].stopVideo();
		});
	});
}

/*var script = document.createElement("script");
script.type = "text/javascript";
script.textContent = 'document.createElement("video").constructor.prototype.canPlayType = function(type){return ""}';
document.documentElement.appendChild(script);
*/
function addStyle_external(css_link) {
	var head, style;
	head = document.getElementsByTagName('head')[0];
	if (!head) {
		return;
	}
	style = document.createElement('link');
	style.setAttribute("rel", "stylesheet");
	style.setAttribute("type", "text/css");
	style.setAttribute("href", css_link);
	head.appendChild(style);
}

addStyle_external("https://cdnjs.cloudflare.com/ajax/libs/mediaelement/4.2.9/mediaelementplayer.min.css");

function choose_adaptive_fmt_by_url(adaptive_fmts, url_part, decrypt) {
	if (typeof adaptive_fmts ==="undefined" || adaptive_fmts === "" || adaptive_fmts === null) {
		debugLog("DEBUG: adaptive_fmts empty at choose_adaptive_fmt_by_url()");
		return;
	}
	adaptive_fmts_arr = adaptive_fmts.split(",");
	var return_value=null;
	var current_bitrate=0;
	var available_bitrates=[];
	for (i = 0; i < adaptive_fmts_arr.length; i++) {
		if (loadStringVar_new(adaptive_fmts_arr[i], "url").includes(url_part)) {
			i_bitrate=parseInt(loadStringVar_new(adaptive_fmts_arr[i], "bitrate"));
			available_bitrates.push(i_bitrate);
			if ((audio_quality==="lowest" && i_bitrate>current_bitrate && current_bitrate!==0) ||
			(audio_quality!=="lowest") && i_bitrate<current_bitrate){
				// this bitrate is not desired
				continue;
			}
			current_bitrate=i_bitrate;
			var selected_url = loadStringVar_new(adaptive_fmts_arr[i], "url");
			if (selected_url.includes("&signature=") || loadStringVar_new(adaptive_fmts_arr[i], "s") == "") {
				decrypt = 0; // TEST: dont decrypt signature if it is already in URL
				if (decrypt == 1 || decrypt == true) {
					var parsed_signature = loadStringVar_new(selected_url, "signature");
					if (parsed_signature!==false) {
						var decoded_url=selected_url.replace(/\&signature=[\w\.]+/, '&signature='+decryptSignature(parsed_signature));
						return_value= decoded_url;
					} else {
						debugLog("DEBUG: failed to decrypt url");
						return_value=null;
					}
				} else {
					return selected_url;
				}
			} else {
				if (decrypt == 1 || decrypt == true) {
					return_value=selected_url + "&signature=" + decryptSignature(loadStringVar_new(adaptive_fmts_arr[i], "s"));
				} else {
					return_value=selected_url + "&signature=" + loadStringVar_new(adaptive_fmts_arr[i], "s");
				}
			}
		}
	}
	debugLog("available bitrates = "+available_bitrates.sort(function(a, b){return a - b})+" ; selected bitrate = "+current_bitrate);
	return return_value;
}

function loadStringVar_new(string, sParam) {
	var sURLVariables = string.split('&'),
		sParameterName,
		i;

	for (i = 0; i < sURLVariables.length; i++) {
		sParameterName = sURLVariables[i].split('=');

		if (decodeURIComponent(sParameterName[0]) === sParam) {
			return sParameterName[1] === undefined ? true : decodeURIComponent(sParameterName[1]);
		}
	}
	return false;
}

function loadStringVar(sVar, mystring) {
	if (!(/^[&?]/.test(mystring))) {
		mystring = "?" + mystring;
	}
	return unescape(mystring.replace(new RegExp("^(?:.*[&\\?]" + escape(sVar).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1"));
}

function loadPageVar(sVar) {
	return unescape(window.location.search.replace(new RegExp("^(?:.*[&\\?]" + escape(sVar).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1"));
}

function findMatch(text, regexp) {
	var matches=text.match(regexp);
	return (matches)?matches[1]:null;
}

function get_video_id_from_yturl(yturl) {
	/*var parser = document.createElement('a');
	parser.href = yturl;*/
	if (yturl.includes("embed/")){
		return findMatch(yturl, /embed\/([a-zA-Z0-9_-]{11})/i);
	}
	if (yturl) {
		var parser = yturl.split("?")[1];
	} else if (window.location.href) {
		var parser = window.location.href.split("?")[1];
	} else {
		return window.ytplayer.config.args.video_id;
	}
	return loadStringVar("v", parser);
}

function absoluteURL(url) {
	var link = document.createElement('a');
	link.href = url;
	return link.href;
}

function getPref(name) { // cross-browser GM_getValue
	var a = '',
		b = '';
	try {
		a = typeof GM_getValue.toString;
		b = GM_getValue.toString()
	} catch (e) {}
	if (typeof GM_getValue === 'function' &&
		(a === 'undefined' || b.indexOf('not supported') === -1)) {
		return GM_getValue(name, null); // Greasemonkey, Tampermonkey, Firefox extension
	} else {
		var ls = null;
		try {
			ls = window.localStorage || null
		} catch (e) {}
		if (ls) {
			return ls.getItem(name); // Chrome script, Opera extensions
		}
	}
	return;
}

function setPref(name, value) { //  cross-browser GM_setValue
	var a = '',
		b = '';
	try {
		a = typeof GM_setValue.toString;
		b = GM_setValue.toString()
	} catch (e) {}
	if (typeof GM_setValue === 'function' &&
		(a === 'undefined' || b.indexOf('not supported') === -1)) {
		GM_setValue(name, value); // Greasemonkey, Tampermonkey, Firefox extension
	} else {
		var ls = null;
		try {
			ls = window.localStorage || null
		} catch (e) {}
		if (ls) {
			return ls.setItem(name, value); // Chrome script, Opera extensions
		}
	}
}

function crossXmlHttpRequest(details) { // cross-browser GM_xmlhttpRequest
	if (typeof GM_xmlhttpRequest === 'function') { // Greasemonkey, Tampermonkey, Firefox extension, Chrome script
		GM_xmlhttpRequest(details);
	} else if (typeof window.opera !== 'undefined' && window.opera && typeof opera.extension !== 'undefined' &&
		typeof opera.extension.postMessage !== 'undefined') { // Opera 12 extension
		var index = operaTable.length;
		opera.extension.postMessage({
			'action': 'xhr-' + index,
			'url': details.url,
			'method': details.method
		});
		operaTable[index] = details;
	} else if (typeof window.opera === 'undefined' && typeof XMLHttpRequest === 'function') { // Opera 15+ extension
		var xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function() {
			if (xhr.readyState == 4) {
				if (details['onload']) {
					details['onload'](xhr);
				}
			}
		}
		xhr.open(details.method, details.url, true);
		xhr.send();
	}
}

function isValidSignatureCode(arr) { // valid values: '5,-3,0,2,5', 'error'
	if (!arr) return false;
	if (arr == 'error') return true;
	arr = arr.split(',');
	for (var i = 0; i < arr.length; i++) {
		if (!isInteger(parseInt(arr[i], 10))) return false;
	}
	return true;
}

function fetchSignatureScript(scriptURL) {
	var storageURL = getPref(STORAGE_URL);
	var storageCode = getPref(STORAGE_CODE);
	if (!(/,0,|^0,|,0$|\-/.test(storageCode))) storageCode = null; // hack for only positive items
	if (storageCode && isValidSignatureCode(storageCode) && storageURL &&
		scriptURL == absoluteURL(storageURL)) return;
	try {
		debugLog('fetch ' + scriptURL);
		isSignatureUpdatingStarted = true;
		crossXmlHttpRequest({
			method: 'GET',
			url: scriptURL,
			onload: function(response) {
				debugLog('fetch status ' + response.status);
				if (response.readyState === 4 && response.status === 200 && response.responseText) {
					findSignatureCode(response.responseText);
				}
			}
		});
	} catch (e) {}
}

function findSignatureCode(sourceCode) {
	debugLog('Info: signature start ' + getPref(STORAGE_CODE));
	var signatureFunctionName =
		findMatch(sourceCode,
			/\.set\s*\("signature"\s*,\s*([a-zA-Z0-9_$][\w$]*)\(/) ||
		findMatch(sourceCode,
			/\.sig\s*\|\|\s*([a-zA-Z0-9_$][\w$]*)\(/) ||
		findMatch(sourceCode,
			/\.signature\s*=\s*([a-zA-Z_$][\w$]*)\([a-zA-Z_$][\w$]*\)/) //old
		||
		findMatch(sourceCode,
			/\|\|\s*"signature"\s*,\s*([a-zA-Z0-9_$][\w$]*)\(/); //new
	if (signatureFunctionName == null) return setPref(STORAGE_CODE, 'error');
	signatureFunctionName = signatureFunctionName.replace('$', '\\$');
	var regCode = new RegExp(signatureFunctionName + '\\s*=\\s*function' +
		'\\s*\\([\\w$]*\\)\\s*{[\\w$]*=[\\w$]*\\.split\\(""\\);\n*(.+);return [\\w$]*\\.join');
	var regCode2 = new RegExp('function \\s*' + signatureFunctionName +
		'\\s*\\([\\w$]*\\)\\s*{[\\w$]*=[\\w$]*\\.split\\(""\\);\n*(.+);return [\\w$]*\\.join');
	var functionCode = findMatch(sourceCode, regCode) || findMatch(sourceCode, regCode2);
	debugLog('Info: signaturefunction ' + signatureFunctionName + ' -- ' + functionCode);
	if (functionCode == null) return setPref(STORAGE_CODE, 'error');

	var reverseFunctionName = findMatch(sourceCode,
		/([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.reverse\s*\(\s*\)\s*}/);
	debugLog('Info: reversefunction ' + reverseFunctionName);
	if (reverseFunctionName) reverseFunctionName = reverseFunctionName.replace('$', '\\$');
	var sliceFunctionName = findMatch(sourceCode,
		/([\w$]*)\s*:\s*function\s*\(\s*[\w$]*\s*,\s*[\w$]*\s*\)\s*{\s*(?:return\s*)?[\w$]*\.(?:slice|splice)\(.+\)\s*}/);
	debugLog('Info: slicefunction ' + sliceFunctionName);
	if (sliceFunctionName) sliceFunctionName = sliceFunctionName.replace('$', '\\$');

	var regSlice = new RegExp('\\.(?:' + 'slice' + (sliceFunctionName ? '|' + sliceFunctionName : '') +
		')\\s*\\(\\s*(?:[a-zA-Z_$][\\w$]*\\s*,)?\\s*([0-9]+)\\s*\\)'); // .slice(5) sau .Hf(a,5)
	var regReverse = new RegExp('\\.(?:' + 'reverse' + (reverseFunctionName ? '|' + reverseFunctionName : '') +
		')\\s*\\([^\\)]*\\)'); // .reverse() sau .Gf(a,45)
	var regSwap = new RegExp('[\\w$]+\\s*\\(\\s*[\\w$]+\\s*,\\s*([0-9]+)\\s*\\)');
	var regInline = new RegExp('[\\w$]+\\[0\\]\\s*=\\s*[\\w$]+\\[([0-9]+)\\s*%\\s*[\\w$]+\\.length\\]');
	var functionCodePieces = functionCode.split(';');
	var decodeArray = [];
	for (var i = 0; i < functionCodePieces.length; i++) {
		functionCodePieces[i] = functionCodePieces[i].trim();
		var codeLine = functionCodePieces[i];
		if (codeLine.length > 0) {
			var arrSlice = codeLine.match(regSlice);
			var arrReverse = codeLine.match(regReverse);
			debugLog(i + ': ' + codeLine + ' --' + (arrSlice ? ' slice length ' + arrSlice.length : '') + ' ' + (arrReverse ? 'reverse' : ''));
			if (arrSlice && arrSlice.length >= 2) { // slice
				var slice = parseInt(arrSlice[1], 10);
				if (isInteger(slice)) {
					decodeArray.push(-slice);
				} else return setPref(STORAGE_CODE, 'error');
			} else if (arrReverse && arrReverse.length >= 1) { // reverse
				decodeArray.push(0);
			} else if (codeLine.indexOf('[0]') >= 0) { // inline swap
				if (i + 2 < functionCodePieces.length &&
					functionCodePieces[i + 1].indexOf('.length') >= 0 &&
					functionCodePieces[i + 1].indexOf('[0]') >= 0) {
					var inline = findMatch(functionCodePieces[i + 1], regInline);
					inline = parseInt(inline, 10);
					decodeArray.push(inline);
					i += 2;
				} else return setPref(STORAGE_CODE, 'error');
			} else if (codeLine.indexOf(',') >= 0) { // swap
				var swap = findMatch(codeLine, regSwap);
				swap = parseInt(swap, 10);
				if (isInteger(swap) && swap > 0) {
					decodeArray.push(swap);
				} else return setPref(STORAGE_CODE, 'error');
			} else return setPref(STORAGE_CODE, 'error');
		}
	}

	if (decodeArray) {
		setPref(STORAGE_URL, scriptURL);
		setPref(STORAGE_CODE, decodeArray.toString());
		DECODE_RULE = decodeArray;
		debugLog('Info: signature ' + decodeArray.toString() + ' ' + scriptURL);
		// update download links and add file sizes
		var elem = document.querySelector("div.html5-video-container > video");
		var url = elem.src;
		var sig = loadStringVar_new(url,"signature");
		if (elem && url && sig) {
			url = url.replace(/\&signature=[\w\.]+/, '&signature=' + decryptSignature(sig));
			elem.setAttribute('src', url);
			elem.play();
		}
	}
}

function getDecodeRules(rules) {
	var storageCode = getPref(STORAGE_CODE);
	if (storageCode && storageCode != 'error' && isValidSignatureCode(storageCode)) {
		var arr = storageCode.split(',');
		for (var i = 0; i < arr.length; i++) {
			arr[i] = parseInt(arr[i], 10);
		}
		rules = arr;
		debugLog('Info: signature ' + arr.toString() + ' ' + scriptURL);
	}
	return rules;
}

function show_poster_image() {
	if ($("img#poster_image").length > 0) {
		debugLog("DEBUG: show_poster_image() exits because img tag exists");
		return;
	}
	if ($(target_node).filter("[style*='img.youtube.com']").length > 0) {
		debugLog("DEBUG: show_poster_image() exits because background-image css exists");
		return;
	}
	if ($('meta[name="GM_VIDEO_ID"]').length > 0) {
		VIDEO_ID = $('meta[name="GM_VIDEO_ID"]').attr("content");
	} else {
		VIDEO_ID = get_video_id_from_yturl(window.location.href);
	}
	if (!(VIDEO_ID)) {
		debugLog("DEBUG : show_poster_image() cant find VIDEO_ID");
		return;
	}
	var bgUrl = 'https://img.youtube.com/vi/' + VIDEO_ID + '/0.jpg';
	if ($("video").eq(0).filter("[style*='img.youtube.com']").length > 0) {
		debugLog("DEBUG: show_poster_image() exits because img.youtube.com exists in style");
		return;
	}
	if (use_youtube_player === 0) {
		//$(target_node).append('<div><img id="poster_image" src="https://img.youtube.com/vi/'+VIDEO_ID+'/hqdefault.jpg" style="display:block; margin:auto;"/></div>');
		$(target_node).css({
			"background-image": "url(https://img.youtube.com/vi/" + VIDEO_ID + "/hqdefault.jpg)",
			"background-repeat": "no-repeat",
			/* "background-attachment": "fixed", */
			"background-position": "center"
		});
	} else {
		$("video").eq(0).css("background", 'transparent url(' + bgUrl + ') no-repeat center');
		$("video").eq(0).css("backgroundSize", '80%');
	}
}

function prepare_audio_player() {
	if ($("audio").length > 0) {
		debugLog("DEBUG : prepare_audio_player() exits because there is audio in page.");
		return;
	}
	if ($('meta[name="GM_RUNNING"]').length > 0) {
		if ($('meta[name="GM_MP3_URL"]').length > 0) {
			show_audio_player($('meta[name="GM_MP3_URL"]').attr("content"));
			debugLog("DEBUG : prepare_audio_player() runs show_audio_player with last mp3 url.");
			return;
		} else {
			debugLog("DEBUG : prepare_audio_player() exits because it already completed a request.");
			return;
		}
	}
	if ($('meta[name="GM_RAN"]').attr("content") > 3) {
		debugLog("DEBUG : prepare_audio_player() exits because it has ran 3 times without success.");
		return;
	}
	if ($('meta[name="GM_VIDEO_ID"]').length > 0) {
		VIDEO_ID = $('meta[name="GM_VIDEO_ID"]').attr("content");
	} else {
		VIDEO_ID = get_video_id_from_yturl(window.location.href);
	}
	if (!(VIDEO_ID)) {
		debugLog("DEBUG : prepare_audio_player() cant find VIDEO_ID");
		return;
	}
	debugLog("DEBUG : prepare_audio_player() running with VIDEO_ID = " + VIDEO_ID);

	function xhr_onload(response) {
		if (response.status == 200) {
			// we can parse now
			//console.log(response.responseText);
			if (use_youtube_source == 1) {
				// youtube GVI parsing
				adaptive_fmts = loadStringVar_new(response.responseText, "adaptive_fmts");
				console.log(response.responseText);
				if (adaptive_fmts===false){
					debugLog("DEBUG: couldn't find adaptive_fmts in response.responseText. Exiting!");
					return;
				}
				var download_link = choose_adaptive_fmt_by_url(adaptive_fmts, audio_selector, 1);
				debugLog("DEBUG: adaptive_fmts = "+adaptive_fmts);
				debugLog("DEBUG: download_link = "+download_link);
			} else {
				var response_parsed = $.parseHTML(response.responseText);
				//var download_link = $('a[href*="youtubemp3api.com/@download/"]', response_parsed);
				var download_link = $(response_parsed).filter('a[href*="youtubemp3api.com/@download/"]').eq(0).attr("href");
			}
			process_download_link(download_link);
			if ($('meta[name="GM_RUNNING"]').length == 0) {
				$("head").append('<meta name="GM_RUNNING" content="YES"/>');
			}
		} else {
			debugLog("response.status = " + response.status +",use_youtube_source="+use_youtube_source);
		}
	}
	function process_download_link(download_link){
		show_audio_player(download_link);
		if ($('meta[name="GM_MP3_URL"]').length == 0) {
			$("head").append('<meta name="GM_MP3_URL" content="' + download_link + '"/>');
		}
	}
	if (use_youtube_source == 1) {
		var args = null;
		var usw = (typeof unsafeWindow !== 'undefined') ? unsafeWindow : window; // Firefox, Opera<15
		//console.log(unsafeWindow.ytplayer.config.args.adaptive_fmts); // for debug
		if (usw.ytplayer && usw.ytplayer.config && usw.ytplayer.config.args) {
			args = usw.ytplayer.config.args;
		}
		if (usw.ytplayer && usw.ytplayer.config && usw.ytplayer.config.assets) {
			scriptURL=usw.ytplayer.config.assets.js;
		}
		if (!isDecodeRuleUpdated) {
			DECODE_RULE = getDecodeRules(DECODE_RULE);
			isDecodeRuleUpdated = true;
		}
		if (scriptURL) {
			scriptURL = absoluteURL(scriptURL);
			debugLog('Info: Full script URL: '+scriptURL);
			fetchSignatureScript(scriptURL);
		}
  		//console.log(window.ytplayer.config.args); // for debug
		if (args && args['adaptive_fmts'] && use_yt_config === 1) {
			debugLog("DEBUG: using youtube configuration");
			adaptive_fmts = args['adaptive_fmts'];
			var download_link = choose_adaptive_fmt_by_url(adaptive_fmts, audio_selector, 1);
			process_download_link(download_link);
		} else {
			// GVI call
			debugLog("DEBUG: using Get_Video_Info configuration");
			xhr_ret_val = GM_xmlhttpRequest({
				synchronous: false,
				method: "GET",
				headers: {
					'Referer': "https://www.youtube.com/watch?v=" + VIDEO_ID
				},
				url: "https://www.youtube.com/get_video_info?el=detailpage&video_id=" + VIDEO_ID,
				onerror: function(oEvent) {
					debugLog("Error " + oEvent.target.status + " occurred while receiving the document.");
				},
				onload: function(response) {
					xhr_onload(response);
				}
			});
		}
	} else {
		xhr_ret_val = GM_xmlhttpRequest({
			synchronous: false,
			method: "GET",
			headers: {
				'Referer': "https://youtubemp3api.com/@api/button/mp3/" + VIDEO_ID
			},
			//url: "https://youtubemp3api.com/@api/button/audiostreams/"+VIDEO_ID,
			url: "https://youtubemp3api.com/@grab?vidID=" + VIDEO_ID + "&format=mp3&streams=audiostreams&api=button",
			onerror: function(oEvent) {
				debugLog("Error " + oEvent.target.status + " occurred while receiving the document.");
			},
			onload: function(response) {
				xhr_onload(response);
			}
		});
	}
	//xhr_onload(xhr_ret_val);
	if ($('meta[name="GM_RAN"]').length == 0) {
		$("head").append('<meta name="GM_RAN" content="1"/>');
	} else {
		$('meta[name="GM_RAN"]').attr("content", function(i, val) {
			return parseInt(val) + 1;
		});
	}
}

function is_it_already_processed() {
	if ($("audio").length > 0) {
		return true;
	}
	if ($("video").eq(0).filter("[src*='youtubemp3api']").length > 0) {
		return true;
	}
	//console.log($("video").eq(0).attr("src"));
	if ($("video").eq(0).filter("[src*='googlevideo']").length > 0) {
		return true;
	}
	return false;
}

function show_audio_player(audio_url) {
	if (is_it_already_processed() === true) {
		return;
	}
	if (typeof audio_url==="undefined" || audio_url === "" || audio_url === null || audio_url === undefined) {
		if ($('meta[name="GM_MP3_URL"]').length > 0 && $('meta[name="GM_MP3_URL"]').attr("content")!="undefined") {
			audio_url = $('meta[name="GM_MP3_URL"]').attr("content");
			debugLog("DEBUG : show_audio_player() runs with last mp3 url because of empty parameter.");
		} else {
			debugLog("NOTICE: show_audio_player() fails to run because of empty parameter and undefined last mp3 url");
			return;
		}
	}
	/*$('audio').each(function() {
		$(this)[0].pause();
		$(this)[0].currentTime = 0;
	});*/
	if (use_youtube_player === 0) {
		if (!(document.getElementById("player_mp3"))) {
			$(target_node).prepend('<audio class="mejs__player" id="player_mp3" preload="auto" autoplay="autoplay" controls="controls" style="max-width: 100%">' +
				'<source src="' + audio_url + '" type="audio/mp3">' +
				'</audio>');
		}
		$('audio').mediaelementplayer({
			enablePluginDebug: true,
			alwaysShowControls: true,
			// Do not forget to put a final slash (/)
			pluginPath: 'https://cdnjs.com/libraries/mediaelement/',
			// this will allow the CDN to use Flash without restrictions
			// (by default, this is set as `sameDomain`)
			shimScriptAccess: 'always',
			// more configuration
			success: function(mediaElement, domObject) {
				debugLog("DEBUG: media was successfully loaded");

				mediaElement.play();
				// add event listener
				mediaElement.addEventListener('canplay', function(e) {
					// Player is ready
					mediaElement.play();
				}, false);
				mediaElement.addEventListener('ended', function(e) {

					//Do Stuff here
					//alert("sometext"); 
					if ((document.getElementById("autoplay-checkbox") && document.getElementById("autoplay-checkbox").checked) ||
						(document.getElementById("toggle") && document.getElementById("toggle").checked)) {
						if ($("ytd-compact-autoplay-renderer a#thumbnail").length > 0) {
							$("ytd-compact-autoplay-renderer a#thumbnail")[0].click();
						} else if ($("div.watch-sidebar-body div.content-wrapper a.content-link").length > 0) {
							$("div.watch-sidebar-body div.content-wrapper a.content-link")[0].click();
						}
					}
				}, false);
				/*var events = ['loadstart', 'play','pause', 'ended'];
		
				for (var i=0, il=events.length; i<il; i++) {
			
					var eventName = events[i];
			
					mediaElement.addEventListener(events[i], function(e) {
						debugLog(e.type);
					});
				}*/
			},
			error: function(mediaElement, domObject) {
				debugLog("DEBUG: media had an error loading");

				setTimeout(function() {
					mediaElement.play();
				}, 1000);
			},
		});
	} else {
		/*if ( $('meta[name="GM_PROCESSED"]').length >0 ) {
			return ;
		} else {
			$("head").append('<meta name="GM_PROCESSED" content="YES"/>');
		}*/
		//console.log(is_it_already_processed());
		if (is_it_already_processed() === false && $("video").attr("src") != audio_url && typeof audio_url !=="undefined") {
			$("video")[0].pause();
			$("video").eq(0).attr('src', audio_url);
			debugLog("DEBUG: set src to " + audio_url);
			$("video")[0].currentTime = 0;
			$("video")[0].play();
		} else {
			debugLog("DEBUG: won't set source! is_it_already_processed() = "+is_it_already_processed()+", typeof audio_url = "+typeof audio_url);
		}
	}
}

function my_ready_function() {
	/*if (is_it_already_processed()===true) {
		//clearInterval(window.my_ival);
		return;
	}*/
	if ($("audio").length > 0) {
		if (use_youtube_player === 0) {
			setInterval(remove_all_video_players, 1000);
			//	} else {
			//	setInterval(pause_all_video_players,1000);
		}
		return;
	}
	if (use_youtube_player === 0) {
		remove_all_video_players();
	} else {
		pause_all_video_players();
	}
	show_poster_image();
	show_audio_player();
}

$(document).ready(function() {
	my_ready_function();
	prepare_audio_player();
});
window.my_ival = setInterval(my_ready_function, 1000);