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.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, }); } } const rootCallback = function (mutationsList, observer) { //Remove all events listeners from redirected links var link = document.querySelectorAll("a[href*='/redirect?']"); if (link != null && link.length > 0) { for (var i = 0; i < link.length; i++) { link[i].replaceWith(link[i].cloneNode(true)); } } //Replace redirect links by direct links 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> • .* <\/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'> • </div></span>" + strFullName + " </a></span>"); //Add full video name to link newLink.parentElement.outerHTML = strHTML2; } } } })();