MDA youtube common

my JS YT library

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greatest.deepsurf.us/scripts/577494/1821178/MDA%20youtube%20common.js

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         MDA youtube common
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  my JS YT library
// @author       mr-d-r
// ==/UserScript==


// @r equire  file://D:\...\_MDAlib_tampermonkey.js ВСЕГДА добавляет в самое начало скрипта, где он использован
// т.о. переменные объявленные здесь всегда глобальные !!!
var MDAytlib="included";  // real global vars !!!  // dbg - отладка    // verb - немного расширенные сообщения
var scrpt="inited"; // scriptname to define in scripts

var ThreeDotsMenuButton="#actions-inner .dropdown-trigger", ThreeDotsMenuContent="ytd-popup-container tp-yt-iron-dropdown ytd-menu-popup-renderer";
document.ThreeDotsMenuClickStillWorking=false; 	// document var to prevent clicking threedots menu button multiple times simultaneously
document.ThreeDotsMenuClickOWNER='n/a'; 		// script name which clicked threedots menu button

var Save2playlistMenuButton="#mdaSave", 					Save2playlistMenuContent="[aria-label='Save video to...']";
document.Save2playlistClickDONE=false; 			// document var to prevent clicking save2playlist button multiple times simultaneously
document.Save2playlistClickStillWorking-'n/a'; 	// script name which clicked save2playlist button

var isMenuClicked=false; // remove later
var replAA="+";  // my title for save to playlists button


// tmp workaround   // выскакивающее окно - Чтобы скачать это видео, подпишитесь на Premium
	// Кроме того, вы сможете смотреть любимые ролики без рекламы, в фоновом режиме и офлайн.  Высокое – 720 пикс. Стандартное – 480 пкс
/*
ttout(1000, ()=> { 	log("closing offline-promo-renderer шт MDAytlib");
					qS("ytd-offline-promo-renderer yt-button-renderer[id='dismiss-button'] button")?.click();
					qS("ytd-offline-promo-renderer")?.remove();		});
ttout(2000, ()=> { 	qS("ytd-offline-promo-renderer yt-button-renderer[id='dismiss-button'] button")?.click();
					qS("ytd-offline-promo-renderer")?.remove();		});
ttout(3000, ()=> { 	qS("ytd-offline-promo-renderer yt-button-renderer[id='dismiss-button'] button")?.click();
					qS("ytd-offline-promo-renderer")?.remove();		});
ttout(4000, ()=> { 	qS("ytd-offline-promo-renderer yt-button-renderer[id='dismiss-button'] button")?.click();
					qS("ytd-offline-promo-renderer")?.remove();		});
ttout(5000, ()=> { 	qS("ytd-offline-promo-renderer yt-button-renderer[id='dismiss-button'] button")?.click();
					qS("ytd-offline-promo-renderer")?.remove();		});
ttout(6000, ()=> { 	qS("ytd-offline-promo-renderer yt-button-renderer[id='dismiss-button'] button")?.click();
					qS("ytd-offline-promo-renderer")?.remove();		});
*/
					


var ytSavePlaylistButtonAlreadyPressed=false;

        if ( (typeof doc == "undefined")  ||  (typeof doc == undefined) ) 	doc=document;
        if ( (typeof epoc == "undefined") ||  (typeof epoc == undefined) ) 	epoc=0;

	function func1 (scr, caller) { 	var aa,  eC,  startMs, fn='func1set:';
		epoc=Math.round( window.performance.now()*100 )/100; // Date.now(); // global
		eC=Math.round( window.performance.now()*100 )/100; // Date.now();
		startMs=new Date().format("MM:ss..l");

		doc.var1=`var111 ${startMs}`;
		log(`${scr} ${fn}  ${startMs}(+${eC-epoc}):  ${caller}: ${doc.var1}`);
	}

	function func2 (scr, caller) { 	var aa,  eC,  startMs, fn='func2read:';
		eC=Math.round( window.performance.now()*100 )/100; // Date.now();
		startMs=new Date().format("MM:ss..l");

		log(`${scr} ${fn}  ${startMs}(+${eC-epoc}):  ${caller}: ${doc.var1}`);
	}

	function func3 (scr, caller) { 	var aa,  eC,  startMs, fn='func3upd:';
		eC=Math.round( window.performance.now()*100 )/100; // Date.now();
		startMs=new Date().format("MM:ss..l");

		doc.var1=`var111 UPD ${startMs}`;
		log(`${scr} ${fn}  ${startMs}(+${eC-epoc}):  ${caller}: ${doc.var1}`);
	}



const mdaDateTimeSavelistTBL=`<table id="mdaDateTimeSavelist_tbl" style="font-size: 10px;">	<tbody>
					<tr id=mdaDateTime_row1>
						<td id=mdaDateTime_td111>	  <h3 id=mda_datetime> </h3>	</td>
						<td id=mdaDateTime_inf1> 									</td>
					</tr>
	<!--			<tr id=mdaDateTime_row2>
						<td id=mdaDateTime_filler>					</td>
					</tr>  --!>
				</tbody> </table>`  // &#x3000;&#x3000;&#x3000;

const mdaSavelistTBL=`<table id="mdaSavelist_tbl" style="font-size: 10px;">	<tbody>
					<tr id=mdaDateTime_row>
					</tr>
				</tbody> </table>`  // &#x3000;&#x3000;&#x3000;

function chgSavePlaylist (max='700px') {  // save to playlist dialog window size // obsoleted by google !!!
		GM_addStyle(`.ytd-add-to-playlist-renderer { 	padding-bottom: 3px; 	padding-top:   3px; 	margin-bottom:  0px !important;  	}
					 .ytdMenuTitleRendererHost     {	padding-bottom: 1px; 	padding-top:   1px; 										}
					 ytd-add-to-playlist-renderer[dialog] #playlists.ytd-add-to-playlist-renderer {  max-height: ${max}; 					}
					`);
} // chgSavePlaylist()


// candidate 2 remove !!!   replaced by ThreeDotsMenuClick/Close() !!!
function hide_Save2playlistDialog (fl) { 	var a='hidden', bb;
	logdbg("> > > > > > > > hide_Save2playlistDialog ГЛЮК"); //return;

	if(fl) 	a='hidden'
	else 	a=''; // a='visible'

	// закрывать только если aria-hidden != true !!!


	// WORKS, но изображение чуть подрагивает !!!
	if( bb=qS("tp-yt-iron-dropdown  .ytContextualSheetLayoutContentContainer .ytListViewModelHost[role='list']")?.parentNode?.parentNode?.parentNode?.parentNode?.parentNode ) {
		log(`save butt 46593g2as1: made INVISIBLE`)
		bb.style.visibility=a;
		//if(fl) 	bb.style.display='none'
		//else 	bb.style.display='block';
	}
	logdbg(`hide_Save2playlistDialog(${fl}):   bb found: ${bb}  visibility: ${a}` );

	// experim 2 del
	//if( bb=qS("#above-the-fold #top-row") ) {	bb.style.visibility=a;	}
	//logdbg(`hide_Save2playlistDialog(${fl}):   bb found: ${bb}  visibility: ${a}` );

	//if( bb=qS("#mdaSaveButt") ) 	{	bb.style.visibility=a;	}
	//if( bb=qS("#mdaSaveCont") ) 	{	bb.style.visibility=a;	}
	//if( bb=qS("#mdaSave") ) 		{	bb.style.visibility=a;	}
	logdbg(`hide_Save2playlistDialog(${fl}):   bb found: ${bb}  visibility: ${a}` );

} // hide_Save2playlistDialog()


function save2playlist_cosmetic () { // pre-2025 old save2playlist dialogue
	logwarn("save2playlist_cosmetic(): disabled  obsolete 2 delete");
	return;
	aa=document.querySelectorAll("ytd-popup-container.style-scope.ytd-app tp-yt-iron-dropdown.style-scope.ytd-popup-container")[1];
	aa.style.height="800px";
	aa.querySelector("#contentWrapper").style.height="800px";
	aa.querySelector("yt-sheet-view-model").style.height="800px";
	aa.querySelector("yt-sheet-view-model").style.maxHeight="700px";
	aa.querySelectorAll("ytContextualSheetLayoutContentContainer toggleable-list-item-view-model")[1]
} // save2playlist_cosmetic()


function save2playlistResize () {  var aa;  // 2025 new save2playlist dialogue
	if( aa=document.querySelectorAll("ytd-popup-container.style-scope.ytd-app tp-yt-iron-dropdown.style-scope.ytd-popup-container") ) {
		if( typeof aa[1] !='undefined' ) {
			//log(`---- --- --- save2playlistResize():`, aa, aa[1]);
			if( typeof aa[1].style != 'undefined' )  aa[1].style.height="800px";
			aa[1].querySelector("#contentWrapper").style.height="800px";
			aa[1].querySelector("yt-sheet-view-model").style.height="800px";
			aa[1].querySelector("yt-sheet-view-model").style.maxHeight="700px";
			aa[1].querySelectorAll("ytContextualSheetLayoutContentContainer toggleable-list-item-view-model")[1]

			aa[1].querySelectorAll("toggleable-list-item-view-model")?.forEach((it) => {
						it.querySelector(".yt-list-item-view-model__image-container.yt-list-item-view-model__leading")?.remove(); // icons
						it.querySelector("span.yt-core-attributed-string.yt-list-item-view-model__subtitle.yt-core-attributed-string--white-space-pre-wrap")?.remove(); // share type
			});

			//aa.querySelectorAll("toggleable-list-item-view-model")[1].querySelector(".yt-list-item-view-model__image-container.yt-list-item-view-model__leading").remove()
			//aa.querySelectorAll("toggleable-list-item-view-model")[2].querySelector(".yt-list-item-view-model__image-container.yt-list-item-view-model__leading").remove()
		}
	}
	// при проверке наличия savebutton сначала проверять в открытых местах и только потом внутри threedots !!!!
} // save2playlistResize()



function ThreeDotsMenuClick () {  	var bu, co, _clicked=false, fn="ThreeDotsMenuClick()"; 	// open hiddenly threedots menu if it was not opened previously
	//logwarn("ThreeDotsMenuClick tmp disabled");			return;

	logdbg(`${scrpt} ${fn}: still working? ${document.ThreeDotsMenuClickStillWorking}`);
	//if( typeof ThreeDotsMenuButton == "undefined" 	)	{  logerr(`${fn}: no global variable ThreeDotsMenuButton set`); 	return; 	}
	//if( typeof ThreeDotsMenuContent == "undefined" 	)	{  logerr(`${fn}: no global variable ThreeDotsMenuContent set`); 	return; 	}

	if( document.ThreeDotsMenuClickDONE ) 				{ 	logdbg(`${fn}: already opened/closed by some other instance`); 	return; 	}
	if( document.ThreeDotsMenuClickStillWorking ) 		{ 	logwarn(`${fn}: >>another instance already running`); 			return; 	}
	if( co=qS(ThreeDotsMenuContent) )  					{ 	logdbg(`${fn}: threedots menu content ALREADY exists, NO need to click`); 	return;  }

	document.ThreeDotsMenuClickStillWorking=true;	document.ThreeDotsMenuClickOWNER=GM.info.script.name;
	let cnt=0;
	let ITRV=setInterval( ()=> {
		if( cnt++ <= 500 ) {
			if( document.ThreeDotsMenuClickDONE ) 	{ 	logdbg(`${fn}: already opened/closed by some other instance`);
														clearInterval(ITRV);
														return;
							 						}
			if(!bu) bu=qS(ThreeDotsMenuButton);  // check if button appeared
			if( bu ) {		if( !_clicked ) {  bu?.click(); _clicked=true;
															logdbg(`${fn}: threedots menu found - CLICK`);
							} else { 						logdbg(`${fn}: threedots menu already CLICKED`);			}
			}
			if(!co) co=qS(ThreeDotsMenuContent);
			if( co ) { // check if content appeared
						loginf(`${fn}: threedots content found`);


						// >>> ADD 
							hide_Save2playlistDialog(true); //- скрывает всю полоску с threedots menu !!!


						co.style.visibility="hidden";   co.setAttribute("mdaid", 'opened');
						isMenuClicked=true;  						// remove later - see MDA youtube +SaveButton +Datetime +ArrowJumps +ADs
						//_closethreedots(bu, co);
						ttout(300, ()=> { _closethreedots(bu, co); });
						clearInterval(ITRV);
						document.ThreeDotsMenuClickStillWorking=false;
						document.ThreeDotsMenuClickDONE=true;
						hide_Save2playlistDialog(false);
			} else logdbg(`${fn}: threedots content NOT found, iterating`);
		} else { 	logerr(`${fn}: FAILED due to setInterval timeout `, cnt);
					document.ThreeDotsMenuClickStillWorking=false;
					clearInterval(ITRV);
		}
	}, 10); // setInterval

	function _closethreedots (bu, co) {
		logdbg(`${fn}: threedots menu CLOSING   ariaHidden=${co?.parentNode?.parentNode?.ariaHidden}`);
		bu?.click(); 	// always fails - thus have to set display=none
		bu?.parentNode?.querySelector("yt-button-shape button")?.click(); 			// real way to close threedots menu !!!
		co.style.visibility="visible";  // return normal visibility

		//bu?.dispatchEvent(   new KeyboardEvent("keydown", { key: "Escape" }) );  // попытка его исправить через Escape
		//bu?.parentNode?.querySelector("yt-button-shape button")?.dispatchEvent(   new KeyboardEvent("keydown", { key: "Escape" }) );  // попытка его исправить через Escape
		/*
		// все равно остался мелкий косяк, когда после такого закрытия, и если не было пользовательских кликов, потом приходится тыкать threedots menu мышью дважды
		bu?.parentNode?.querySelector("yt-button-shape")?.dispatchEvent(   new KeyboardEvent("keydown", { key: "Escape" }) );  // попытка его исправить через Escape
		//co.style.display='none'; 		// closing the menu
		co.style.zIndex="";
		//co.ariaHidden=true;
		co.focused=false;  // later change to removeattribute !!!
		*/
		logdbg(`${fn}: threedots menu CLOSED   ariaHidden=${co?.parentNode?.parentNode?.ariaHidden}`);
	}
} // ThreeDotsMenuClick()





function ThreeDotsMenuClose () {  	var aa, bb,  fn="ThreeDotsMenuClose()";
	logerr("ThreeDotsMenuClose obsolete 2 delete");
	return;

	logdbg(`${scrpt} ${fn}: still working? ${document.ThreeDotsMenuClickStillWorking}  clickOWNER: ${document.ThreeDotsMenuClickOWNER}`);
	if( typeof ThreeDotsMenuButton == "undefined" 	)	{  logerr(`${fn}: no global variable ThreeDotsMenuButton set`);   return; }
	if( typeof ThreeDotsMenuContent == "undefined" 	)	{  logerr(`${fn}: no global variable ThreeDotsMenuContent set`);  return; }

	// теоретически надо еще проверять document.ThreeDotsMenuClickOWNER - кто открыл меню, тот и закрывает !!!
	if( document.ThreeDotsMenuClickOWNER != GM.info.script.name ) {	
		logwarn(`${fn}: i'm NOT the clickOWNER - EXITING      owner: ${document.ThreeDotsMenuClickOWNER}`)
		return;
	}
	let cnt=0;
	let ITRV=setInterval( ()=> { 			 // The first execution happens after delay milliseconds.
		if( cnt++ <= 25 ) {
			if( document.ThreeDotsMenuClickDONE  ) { 		logdbg(`${fn}: already opened/closed by some other instance`);
															clearInterval(ITRV);
															return; 				}
			aa=qS(ThreeDotsMenuContent);
			if( !aa )  								logwarn(`${fn}: still NO threedots menu CONTENT - iterating `, cnt);

			if( document.ThreeDotsMenuClickStillWorking )  	logerr(`${fn}: ThreeDotsMenuClickStillWorking == TRUE `, cnt);  else logerr(`${fn}: ThreeDotsMenuClickStillWorking == false `, cnt);
			if( !document.ThreeDotsMenuClickStillWorking  &&  aa  &&  aa?.style.display != 'none' ) {
				bb=qS(ThreeDotsMenuButton);
				logdbg(`${fn}: threedots menu CLOSING   ariaHidden=${bb?.ariaHidden}  `, cnt);
				//bb?.click(); // fails - thus have to set display=none
				document.ThreeDotsMenuClickDONE=true;
				aa.style.display='none';
				aa.style.visibility="visible";  // return normal visibility
				aa.style.zIndex="";
				aa.ariaHidden=true;
				aa.focused=false;  // later change to removeattribute !!!
				// все равно остался мелкий косяк, когда после такого закрытия, и если не было пользовательских кликов, потом приходится тыкать threedots menu мышью дважды
				aa.dispatchEvent(   new KeyboardEvent("keydown", { key: "Escape" }) );  // попытка его исправить
				logdbg(`${fn}: threedots menu CLOSED   ariaHidden=${bb?.ariaHidden}  `, cnt);
				clearInterval(ITRV);
			}
		}
		else { 	logerr(`${fn}: setInterval timeout `, cnt);  clearInterval(ITRV);	}
	}, 500); // setInterval
} // ThreeDotsMenuClose()




function Save2playlist_FindButton () { 	var bu, co, stopFL=false, fn="Save2playlist_FindButton()";
	logdbg(`${scrpt} ${fn}: searching Save2playlist button`);
	if( bu=qS("#mdaSave") )  { logdbg(`${fn}: Save2playlist button found by #mdaSave`); 	return bu; 	}

	// normal place // ("[aria-label^='Save']") можно кликать даже внутри threedots !!!
	if( bu=qS("[aria-label^='Save']") )  {
			logdbg(`${fn}: Save2playlist button found by aria-label 111`, bu);
			bu.id='mdaSaveButt'
			return bu;
	}

	

	// осталось проверить мое место куда делаю drag !!!



	return null;
} // Save2playlist_FindButton()


function Save2playlistClick () {  	var bu, co, _clicked=false, fn="Save2playlistClick()"; 	// open hiddenly Save2playlist menu if it was not opened previously
	logdbg(`${scrpt} ${fn}: still working? ${document.Save2playlistClickStillWorking}`);
	logdbg(`${scrpt} ${fn}: TMP DISABLED`);
	// глюк - пропадает полоса с названием-лайками-дизлайками - возможно конфликт с improved youtube

	//if( typeof Save2playlistMenuButton == "undefined" 	)	{  logerr(`${fn}: no global variable Save2playlistMenuButton set`); 	return; 	}
	//if( typeof Save2playlistMenuContent == "undefined" 	)	{  logerr(`${fn}: no global variable Save2playlistMenuContent set`); 	return; 	}

	if( document.Save2playlistClickDONE ) 				{ 	logdbg(`${fn}: already opened/closed by some other instance`); 	return; 	}
	if( document.Save2playlistClickStillWorking ) 		{ 	logwarn(`${fn}: >>another instance already running`); 			return; 	}
	if( co=qS(Save2playlistMenuContent) )  				{ 	logdbg(`${fn}: Save2playlist menu content ALREADY exists, NO need to click`); 	return;  }

	document.Save2playlistClickStillWorking=true;	document.Save2playlistClickOWNER=GM.info.script.name;
	let cnt=0;
	let ITRV=setInterval( ()=> {
		if( cnt++ <= 300 ) { 
			if( document.Save2playlistClickDONE ) 	{ 	logdbg(`${fn}: already opened/closed by some other instance`);
														clearInterval(ITRV);
														return;
							 						}
			if(!bu) bu=Save2playlist_FindButton();  // check if button appeared

			//return; // tmp return !!!!!

			if( bu ) {		if( !_clicked ) {  bu?.click(); _clicked=true;
															logdbg(`${fn}: Save2playlist menu found - CLICK`);
							} else { 						logdbg(`${fn}: Save2playlist menu already CLICKED`);			}
			} else {
				ThreeDotsMenuClick();   logdbg(`${fn}: running ThreeDotsMenuClick() to reveal Save2playlist button`);
			}
			//return; // tmp return !!!!!

			if(!co) { // co=qS(Save2playlistMenuContent);
				qSA("ytd-app   ytd-popup-container   tp-yt-iron-dropdown").forEach(el=>{
					//log(el);
					let stopFL=false;
					if( !stopFL && el.querySelector(Save2playlistMenuContent) ) {
						el.id='mdaSaveCont'; 	stopFL=true;
						logdbg(`${fn}: Save2playlist button found by aria-label !!! !!! !!! 222`, el);
						co=el;
					}
				});
			}
			// return; // tmp return !!!!!

			if( co ) { // check if content appeared
						loginf(`${fn}: Save2playlist content found `, co);
						// tmp disabled    co.style.visibility="hidden";   co.setAttribute("mdaid", 'opened');
						// tmp disabled    hide_Save2playlistDialog(true);
						//co.style.display="none";

						// какая-то херня с bu
						//ttout(200, ()=> { _closeSave2playlist(bu, co); });
						
						// WORKS GOOD !!!
						ttout(400, ()=> { _closeSave2playlist(qS("#mdaSaveButt"), co); });

						// окно херово закрывается, тоже проверять в цикле aria-hidden=true !!!

						clearInterval(ITRV);
						document.Save2playlistClickStillWorking=false;
						document.Save2playlistClickDONE=true;
			} else logdbg(`${fn}: Save2playlist content NOT found, iterating`);
		} else { 	logerr(`${fn}: FAILED due to setInterval timeout `, cnt);
					document.Save2playlistClickStillWorking=false;
					clearInterval(ITRV);
		}
	}, 30); // setInterval
	return;

	// закрывать также при смене вкладки - navigation?

function _closeSave2playlist (bu, co) {
		logdbg(`${fn}: Save2playlist menu CLOSING   ariaHidden=${co?.ariaHidden}`, bu);
		bu?.click(); 	// always fails - thus have to set display=none
		//bu?.dispatchEvent(   new KeyboardEvent("keydown", { key: "Escape" }) );  // попытка его исправить через Escape
		//bu?.dispatchEvent(   new KeyboardEvent("keyup",   { key: "Escape" }) );  // попытка его исправить через Escape
		//window.dispatchEvent(   new KeyboardEvent("keydown", { key: "Escape" }) );  // попытка его исправить через Escape
		//hide_Save2playlistDialog(false);
		co.style.visibility="visible";  // return normal visibility
		//co.ariaHidden=true;
		//co.style.display='';
		//co.style.zIndex="";
		logdbg(`${fn}: Save2playlist menu CLOSED   ariaHidden=${co?.ariaHidden}`);
	}
} // Save2playlistClick()


function showmsg_mdaDateTime_inf1 (msg, delay=2500) {  var aa, save;
	if( aa=qS("#mdaDateTime_inf1") ) {
		save=aa.innerHTML;
		loginfo(`showmsg_mdaDateTime_inf1(): ${msg} - ${delay}`);
		ttoutSMART(delay, 		'DateTime_inf1', 	'set', ()=>{		aa.innerHTML=msg; 		});
		ttoutSMART(delay+100, 	'DateTime_inf1_a', 	'set', ()=>{		aa.innerHTML=save; 		});
	} else loginfo("showmsg_mdaDateTime_inf1(): no #mdaDateTime_inf1 found");
}