Export Youtube Playlist in tab delimited text

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       1.1.1
// @license       MIT
// @require       https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @run-at        document-idle
// ==/UserScript==

(function() {
  'use strict';

  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() {
      if (document.querySelectorAll("#exportTabTextList").length == 0) {

        if (document.querySelectorAll("div.ytd-playlist-header-renderer > div.play-menu.ytd-playlist-header-renderer").length > 0) { //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) {
            qRes[0].insertAdjacentHTML("afterend", "<button id='exportTabTextList' class='yt-spec-button-shape-next--size-m' style='font-family: Roboto, Arial, sans-serif; font-size: 13px; margin-bottom: 16px; padding-top:2px; border: none; height: 28px; line-height: normal; opacity: 0.8;'>Export list as tab delimited text</button>");
          }
          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) {
            qRes[0].insertAdjacentHTML("afterend", "<button id='exportTabTextList' style='font-family: Roboto, Arial, sans-serif; font-size: 13px; margin: 10px 0px;'>Export list as tab delimited text</button>");
          }
          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;

    var numOfVideos = document.querySelectorAll(".metadata-stats");
    if (numOfVideos != null && numOfVideos.length > 0) {
      numOfVideos = numOfVideos[0].innerText.replace(/\ .*/g, '');

      listCreationAllowed = false;
      var qRes = document.querySelectorAll("#exportTabTextList:not(.yt-spec-button-shape-next--size-m)");
      if (qRes != null && qRes.length > 0) {
        qRes[0].insertAdjacentHTML("afterend", `<p id="listBuildMessage" style="color: red; font-size: 1.33em;">Getting full list, please wait...</p>`);
      }
      qRes = document.querySelectorAll("#exportTabTextList.yt-spec-button-shape-next--size-m");
      if (qRes != null && qRes.length > 0) {
        qRes[0].insertAdjacentHTML("afterend", `<p id="listBuildMessage" style="color: red; font-size: 1.33em; margin-bottom: 16px; mix-blend-mode: lighten;">Getting full list, please wait...</p>`);
      }

      let scrollInterval = setInterval(function(){
        if (document.querySelectorAll("ytd-continuation-item-renderer.ytd-playlist-video-list-renderer").length > 0) {
          $(document).scrollTop($(document).height());
        } 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() {

    let list = "<Name>\t<Channel>\t<Duration>\t<URL>";
    var myNodeList = document.querySelectorAll("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, " ");
            list += "\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
          }
        }
        list += "\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, " ");
            list += strAux.trim();
          }
        }
        list += "\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, " ");
            list += strAux.trim();
          }
        }
        list += "\t" + strAux2.trim(); //Video URL is the last column
      }
    }

    var qRes = document.querySelectorAll("body");
    if (qRes != null && qRes.length > 0) {
      qRes[0].insertAdjacentHTML("afterend", '<div id="tablistDisplayContainer" style="position: fixed; z-index: 9999; top: 5%; right: 5%; background-color: gray; padding: 10px; border-radius: 5px;"><button id="selectAllAndCopy" style="font-family: Roboto, Arial, sans-serif; font-size: 13px;">Select all and copy</button>&nbsp;&nbsp;&nbsp;<button id="closeTheListThing" style="font-family: Roboto, Arial, sans-serif; font-size: 13px;">Close</button>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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;">'+list+'</textarea></div>');
    }

    qRes = document.querySelector("#listBuildMessage");
    if (qRes != null) qRes.remove();

    qRes = document.querySelector("#closeTheListThing");
    if (qRes != null) {
      qRes.onclick = function() {
        qRes = document.querySelector("#tablistDisplayContainer");
        if (qRes != null) qRes.remove();
        listCreationAllowed = true;
      }
    }

    qRes = document.querySelector("#selectAllAndCopy");
    if (qRes != null) {
      qRes.onclick = function() {
        document.getElementById("tabPlayList").select();
        document.execCommand("copy");
      }
    }
  }

}) ();