Creates the current playlist as tab delimited text to be easily copied
// ==UserScript== // @name Export Youtube Playlist in tab delimited text // @description Creates the current playlist as tab delimited text to be easily copied // @author 1N07 & MK // @namespace max44 // @homepage https://greasyfork.org/en/users/309172-max44 // @match *://*.youtube.com/* // @match *://*.youtu.be/* // @icon https://cdn.icon-icons.com/icons2/1488/PNG/512/5295-youtube-i_102568.png // @version 2.1.4 // @license MIT // @require https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js // @run-at document-idle // ==/UserScript== (function() { 'use strict'; //Workaround: This document requires 'TrustedHTML' assignment if (window.trustedTypes && trustedTypes.createPolicy) { if (!trustedTypes.defaultPolicy) { const passThroughFn = (x) => x; trustedTypes.createPolicy('default', { createHTML: passThroughFn, createScriptURL: passThroughFn, createScript: passThroughFn, }); } } var listCreationAllowed = true; var urlAtLastCheck = ""; setInterval(function() { if (urlAtLastCheck != window.location.href) { urlAtLastCheck = window.location.href; if (urlAtLastCheck.includes("/playlist?list=")) InsertButtonASAP(); } }, 100); function InsertButtonASAP() { var qRes = document.querySelectorAll("#exportTabTextList"); //Remove previous button for (let i = 0; i < qRes.length; i++) { qRes[i].remove(); } let buttonInsertInterval = setInterval(function() { var htmlButton = ""; if (document.querySelectorAll("#exportTabTextList").length == 0) { if (document.querySelectorAll("yt-page-header-renderer:not([hidden]) yt-page-header-view-model yt-flexible-actions-view-model").length > 0) { //New design qRes = document.querySelectorAll("yt-page-header-renderer:not([hidden]) yt-page-header-view-model yt-flexible-actions-view-model > div.yt-flexible-actions-view-model-wiz__action-row"); if (qRes != null && qRes.length > 0) { htmlButton = "<button id='exportTabTextList' class='yt-spec-button-shape-next--size-m' style='font-family: Roboto, Arial, sans-serif; font-size: 13px; margin-top: 8px; margin-bottom: 16px; padding-top: 2px; border: none; height: 28px; line-height: normal; opacity: 0.8;'>Export list as tab delimited text</button>"; qRes[0].insertAdjacentHTML("afterend", htmlButton); } qRes = document.querySelectorAll("#exportTabTextList"); if (qRes != null && qRes.length > 0) { qRes[0].onclick = ScrollUntilFullListVisible; } } else if (document.querySelectorAll("ytd-playlist-header-renderer:not([hidden]) div.ytd-playlist-header-renderer > div.play-menu.ytd-playlist-header-renderer").length > 0) { //Older new design qRes = document.querySelectorAll("div.metadata-wrapper.ytd-playlist-header-renderer > div.play-menu.ytd-playlist-header-renderer"); if (qRes != null && qRes.length > 0) { htmlButton = "<button id='exportTabTextList' class='yt-spec-button-shape-next--size-m' style='font-family: Roboto, Arial, sans-serif; font-size: 13px; margin-top: 8px; margin-bottom: 16px; padding-top: 2px; border: none; height: 28px; line-height: normal; opacity: 0.8;'>Export list as tab delimited text</button>"; qRes[0].insertAdjacentHTML("afterend", htmlButton); } qRes = document.querySelectorAll("#exportTabTextList"); if (qRes != null && qRes.length > 0) { qRes[0].onclick = ScrollUntilFullListVisible; } } else if (document.querySelectorAll("ytd-two-column-browse-r###lts-renderer:not([hidden])[page-subtype='playlist'] div#contents div#header > ytd-sort-filter-header-renderer #filter-menu").length > 0) { //Workaround for very new design - add button above user created playlists qRes = document.querySelectorAll("ytd-two-column-browse-r###lts-renderer:not([hidden])[page-subtype='playlist'] div#contents div#header > ytd-sort-filter-header-renderer #filter-menu"); if (qRes != null && qRes.length > 0) { htmlButton = "<button id='exportTabTextList' class='yt-spec-button-shape-next--size-m' style='font-family: Roboto, Arial, sans-serif; font-size: 13px; margin: auto 0 auto 25px; border: none; height: 28px; line-height: normal; opacity: 0.95;'>Export list as tab delimited text</button>"; qRes[0].insertAdjacentHTML("afterend", htmlButton); } qRes = document.querySelectorAll("#exportTabTextList"); if (qRes != null && qRes.length > 0) { qRes[0].onclick = ScrollUntilFullListVisible; } } else if (document.querySelectorAll("ytd-two-column-browse-r###lts-renderer:not([hidden])[page-subtype='playlist'] div#contents div#header #chips-content #scroll-container").length > 0) { //Workaround for very new design - add button above non-user created playlists qRes = document.querySelectorAll("ytd-two-column-browse-r###lts-renderer:not([hidden])[page-subtype='playlist'] div#contents div#header #chips-content #scroll-container"); if (qRes != null && qRes.length > 0) { htmlButton = "<button id='exportTabTextList' class='yt-spec-button-shape-next--size-m' style='font-family: Roboto, Arial, sans-serif; font-size: 13px; margin: auto 0 auto 25px; border: none; height: 28px; line-height: normal; opacity: 0.95;'>Export list as tab delimited text</button>"; qRes[0].insertAdjacentHTML("afterend", htmlButton); } qRes = document.querySelectorAll("#exportTabTextList"); if (qRes != null && qRes.length > 0) { qRes[0].onclick = ScrollUntilFullListVisible; } } else if (document.querySelectorAll("ytd-playlist-sidebar-renderer:not([hidden]) > ytd-playlist-sidebar-primary-info-renderer.style-scope.ytd-playlist-sidebar-renderer").length > 0) { //Old design qRes = document.querySelectorAll("ytd-playlist-sidebar-primary-info-renderer.style-scope.ytd-playlist-sidebar-renderer"); if (qRes != null && qRes.length > 0) { htmlButton = "<button id='exportTabTextList' style='font-family: Roboto, Arial, sans-serif; font-size: 13px; margin: 10px 0px;'>Export list as tab delimited text</button>"; qRes[0].insertAdjacentHTML("afterend", htmlButton); } qRes = document.querySelectorAll("#exportTabTextList"); if (qRes != null && qRes.length > 0) { qRes[0].onclick = ScrollUntilFullListVisible; } } //Check whether unavailable videos are hidden or not //var i; //var strAux = ""; //var flgHidden = false; //var myNodeList = document.querySelectorAll("#text"); //for (i = 0; i < myNodeList.length; i++) { // if (myNodeList[i].className.indexOf("style-scope ytd-alert-with-button-renderer") > -1) { // strAux = myNodeList[i].innerText; // strAux = strAux.trim(); // strAux = strAux.toLowerCase(); // if (strAux.indexOf("unavailable videos are hidden") > -1) { // flgHidden = true; // break; // } // } //} //if (flgHidden) { // $("#exportTabTextList").click(ScrollAsPossible); //Unavailable videos are hidden //} else { //$("#exportTabTextList").click(ScrollUntilFullListVisible); //} //clearInterval(buttonInsertInterval); - Do not clear interval in order to add button back if playlist is rebuilt } }, 100); } function ScrollUntilFullListVisible() { if (!listCreationAllowed) return; //Switch focus to playlist var listOfVideos = document.querySelector("ytd-browse[page-subtype='playlist'] #primary"); if (listOfVideos != null) { listOfVideos.click(); var htmlAlert = ""; listCreationAllowed = false; var qRes = document.querySelectorAll("#exportTabTextList:not(.yt-spec-button-shape-next--size-m)"); if (qRes != null && qRes.length > 0) { htmlAlert = `<p id="listBuildMessage" style="color: red; font-size: 1.33em;">Getting full list, please wait...</p>`; qRes[0].insertAdjacentHTML("afterend", htmlAlert); } qRes = document.querySelectorAll("#exportTabTextList.yt-spec-button-shape-next--size-m"); if (qRes != null && qRes.length > 0) { htmlAlert = `<p id="listBuildMessage" style="color: red; font-size: 1.33em; margin-bottom: 16px; mix-blend-mode: lighten;">Getting full list, please wait...</p>`; qRes[0].insertAdjacentHTML("afterend", htmlAlert); } let scrollInterval = setInterval(function(){ if (document.querySelectorAll("ytd-continuation-item-renderer.ytd-playlist-video-list-renderer").length > 0) { //$(document).scrollTop($(document).height()); document.documentElement.scrollTop = document.documentElement.scrollHeight; } else { BuildAndDisplayList(); clearInterval(scrollInterval); } }, 100); } } /*function ScrollAsPossible() { //If unavailable videos are hidden if (!listCreationAllowed) return; listCreationAllowed = false; $("#exportTabTextList").after(`<p id="listBuildMessage" style="color: red; font-size: 1.33em;">Getting full list, please wait...</p>`); $(document).scrollTop($(document).height()); let scrollInterval2 = setInterval(function(){ if (CheckSpinner()) { $(document).scrollTop($(document).height()); } else { BuildAndDisplayList(); clearInterval(scrollInterval2); } }, 500); } function CheckSpinner() { //True if playlist is still loading var i; var myNodeList = document.querySelectorAll("#spinner"); for (i = 0; i < myNodeList.length; i++) { if (myNodeList[i].className.indexOf("style-scope ytd-continuation-item-renderer") > -1) return true; } return false; }*/ function BuildAndDisplayList() { var htmlList = "<Name>\t<Channel>\t<Duration>\t<URL>"; var myNodeList = document.querySelectorAll("ytd-playlist-video-renderer.style-scope.ytd-playlist-video-list-renderer:not([hidden])"); //var myNodeList = document.getElementsByTagName("ytd-playlist-video-renderer"); var i; var myCount = 0; for (i = 0; i < myNodeList.length; i++) { //if (myNodeList[i].className.indexOf("style-scope ytd-playlist-video-list-renderer") > -1) { var mySpanList = myNodeList[i].querySelectorAll("span"); var myAList = myNodeList[i].querySelectorAll("a"); var j; var strAux = ""; var strAux2 = ""; myCount++; for (j = 0; j < myAList.length; j++) { if (myAList[j].id == "video-title") { strAux = myAList[j].innerText; //Video title strAux = strAux.replace(/[\x0D\x0A]/g, " "); htmlList += "\n" + strAux.trim(); strAux2 = myAList[j].href; //Video URL strAux2 = strAux2.replace(/&list=.*&index=\d+/gi, ""); //Remove reference to list and video's index strAux2 = strAux2.replace(/&t=.*$/gi, ""); //Remove timestamp strAux2 = strAux2.replace(/&pp=.*$/gi, ""); //Remove pp parameter } } htmlList += "\t"; for (j = 0; j < myAList.length; j++) { if (myAList[j].className == "yt-simple-endpoint style-scope yt-formatted-string") { strAux = myAList[j].innerText; //Channel name strAux = strAux.replace(/[\x0D\x0A]/g, " "); htmlList += strAux.trim(); } } htmlList += "\t "; for (j = 0; j < mySpanList.length; j++) { if (mySpanList[j].className == "style-scope ytd-thumbnail-overlay-time-status-renderer") { strAux = mySpanList[j].innerText; //Duration strAux = strAux.replace(/[\x0D\x0A]/g, " "); htmlList += strAux.trim(); } } htmlList += "\t" + strAux2.trim(); //Video URL is the last column //} } var qRes = document.querySelectorAll("body"); if (qRes != null && qRes.length > 0) { htmlList = '<div id="tablistDisplayContainer" style="position: fixed; z-index: 9999; top: 5%; right: 5%; background-color: gray; padding: 10px; border-radius: 5px;"><button id="selectAllAndCopyButton" style="font-family: Roboto, Arial, sans-serif; font-size: 13px;">Select all and copy</button> <button id="closeListButton" style="font-family: Roboto, Arial, sans-serif; font-size: 13px;">Close</button> <span style="font-family: Roboto, Arial, sans-serif; font-size: 13px; font-weight: bold; color: white">Total videos in list: '+myCount+'</span><br><br><textarea id="tabPlayList" style="width: 50vw; height: 80vh; max-width: 90vw; max-height: 90vh;">'+htmlList+'</textarea></div>'; qRes[0].insertAdjacentHTML("afterend", htmlList); } qRes = document.querySelector("#listBuildMessage"); if (qRes != null) qRes.remove(); qRes = document.querySelector("#closeListButton"); if (qRes != null) { qRes.onclick = function() { qRes = document.querySelector("#tablistDisplayContainer"); if (qRes != null) qRes.remove(); listCreationAllowed = true; } } qRes = document.querySelector("#selectAllAndCopyButton"); if (qRes != null) { qRes.onclick = function() { document.getElementById("tabPlayList").select(); document.execCommand("copy"); } } } }) ();