Replace youtube redirect links

Replace youtube redirect links with direct links and extend links text to its full length

// ==UserScript==
// @name          Replace youtube redirect links
// @description   Replace youtube redirect links with direct links and extend links text to its full length
// @author        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.3.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';

  const rootCallback = function (mutationsList, observer) {
    document.querySelectorAll("a[href*='/redirect?']").forEach(replaceRedirect);
    document.querySelectorAll("a:not([expanded-by-script])").forEach(showFullLink);
    document.querySelectorAll("div#below span.yt-core-attributed-string > span > span.yt-core-attributed-string--highlight-text-decorator:not([expanded-by-script]) > a[href*='/watch?']").forEach(showFullVideoName);
    document.querySelectorAll("div#below span.yt-core-attributed-string > span > span.yt-core-attributed-string--highlight-text-decorator:not([expanded-by-script]) > a[href*='/shorts/']").forEach(showFullVideoName);
    document.querySelectorAll("div#below span.yt-core-attributed-string > span > span.yt-core-attributed-string--highlight-text-decorator:not([expanded-by-script]) > a[href*='/playlist?']").forEach(showFullVideoName);
  }

  const rootNode = document.querySelector("body");
  if (rootNode != null) {
    const rootObserver = new MutationObserver(rootCallback);
    rootObserver.observe(rootNode, {childList: true, subtree: true});
  }

  function replaceRedirect(link) { //Remove redirection
    link.href = decodeURIComponent(link.href.replace (/^.*\?(.*&)q=([^&]+)(&.*)?$/, '$2'));
    const wrpLink = link.wrappedJSObject || link;
    if (wrpLink.data && wrpLink.data.urlEndpoint) {
      wrpLink.data.urlEndpoint.url = link.href;
    }
    showFullLink(link);
  }

  function showFullLink(link) { //Expand link to full length
    if (link.innerText.substring(0, 20) == link.href.substring(0, 20) && link.innerText.substring(link.innerText.length-3, link.innerText.length) === "...") {
      link.innerText = link.href;
      link.setAttribute("expanded-by-script", "true");
    } else link.setAttribute("expanded-by-script", "false");
  }

  function showFullVideoName(link) { //Expand short video name to full one, taken from previous text
    link.parentElement.setAttribute("expanded-by-script", "false");

    const rangeBefore = document.createRange(); //Create a range
    if (link.parentElement.parentElement.previousElementSibling == null) {
      rangeBefore.setStart(link.parentElement.parentElement.parentElement, 0); //from the start of the parent
    } else {
      rangeBefore.setStartAfter(link.parentElement.parentElement.previousElementSibling); //from the end of previous element
    }
    rangeBefore.setEndBefore(link.parentElement.parentElement); // till the target element
    var strFullName = rangeBefore.toString();
    strFullName = strFullName.replace(/.*[\n](?!$)/g, ""); //Remove all text except the last line
    strFullName = strFullName.replace(/\&/g, "&"); //Replace & with its html-code

    if (strFullName.length > 0) {
      var strHTML = link.parentElement.parentElement.parentElement.outerHTML;
      strHTML = strHTML.replace(strFullName, ""); //Remove full video name from text
      strHTML = strHTML.replace(/yt-core-image--content-mode-scale-to-fill"><\/span>/gi, "yt-core-image--content-mode-scale-to-fill yt-core-image--loaded\" src=\"https://www.gstatic.com/youtube/img/watch/yt_favicon.png\"></span>"); //Add YT icon if missed
      link.parentElement.parentElement.parentElement.outerHTML = strHTML;

      strFullName = strFullName.replace(/[\n]/g, ""); //Remove \n
      strFullName = strFullName.replace(/\s+$/g, ""); //Remove trailing spaces
      strFullName = strFullName.replace(/:+$/g, ""); //Remove trailing semicolon
      strFullName = strFullName.replace(/-+$/g, ""); //Remove trailing dash
      strFullName = strFullName.replace(/:+$/g, ""); //Remove trailing semicolon
      strFullName = strFullName.replace(/\(+$/g, ""); //Remove trailing (
      strFullName = strFullName.replace(/\s+$/g, ""); //Remove trailing spaces
      strFullName = strFullName.replace(/^\s*/g, ""); //Remove leading spaces
      strFullName = strFullName.replace(/^\)/g, ""); //Remove leading )
      strFullName = strFullName.replace(/^\./g, ""); //Remove leading .
      strFullName = strFullName.replace(/^,/g, ""); //Remove leading ,
      strFullName = strFullName.replace(/^\s*/g, ""); //Remove leading spaces

      var newLink = document.querySelector("div#below span.yt-core-attributed-string > span > span.yt-core-attributed-string--highlight-text-decorator[expanded-by-script='false'] > a"); //New query, because previous setting of outerHTML rebuilded this node in DOM
      if (newLink != null) {
        newLink.parentElement.setAttribute("expanded-by-script", "true");
        var bgStyle = newLink.parentElement.getAttribute("style");
        newLink.parentElement.style = ""; //Remove background
        newLink.parentElement.firstElementChild.style = "margin: 1px 0px; " + bgStyle; //Add background

        var strHTML2 = newLink.parentElement.outerHTML;
        strHTML2 = strHTML2.replace(/<\/span>&nbsp;•&nbsp;.*&nbsp;&nbsp;<\/a><\/span>/gi, "<div class='yt-core-attributed-string__link yt-core-attributed-string__link--display-type yt-core-attributed-string__link--call-to-action-color'>&nbsp;•&nbsp;</div></span>" + strFullName + "&nbsp;&nbsp;</a></span>"); //Add full video name to link
        newLink.parentElement.outerHTML = strHTML2;
      }
    }
  }
})();