Live Link Cleaner

Strip trackers and clean shared links (YouTube, Amazon, Substack, etc.), mark only real cleaned links with hover reason

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Live Link Cleaner
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Strip trackers and clean shared links (YouTube, Amazon, Substack, etc.), mark only real cleaned links with hover reason
// @match        *://*/*
// @match        file:///*
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    if (/youtube\.com/i.test(location.hostname) || /amazon\./i.test(location.hostname)) {
        return;
    }

    // Only known trackers, do NOT include `list`
    const trackerPattern = /([?&])(si=[^&]*|utm_[^&]*|fbclid=[^&]*|gclid=[^&]*)/gi;

    function cleanUrl(url) {
        if (!url) return { cleaned: url, reason: null };
        let reason = [];
        let cleaned = url;

        // Skip Discord media / Tenor
        if (/^https?:\/\/(cdn\.discordapp\.com|media\.discordapp\.net|tenor\.com)/i.test(url)) {
            return { cleaned: url, reason: null };
        }

        // Skip YouTube clips
        if (/^https?:\/\/(www\.)?youtube\.com\/clip\//i.test(url)) {
            return { cleaned: url, reason: null };
        }

        // Shorts → watch
        const shortsMatch = url.match(/^https?:\/\/(www\.)?youtube\.com\/shorts\/([A-Za-z0-9_-]+)/i);
        if (shortsMatch) {
            reason.push("Expanded shorts → watch");
            return { cleaned: `https://www.youtube.com/watch?v=${shortsMatch[2]}`, reason: reason.join(", ") };
        }

        // watch?v= cleanup
        const watchMatch = url.match(/^https?:\/\/(www\.)?youtube\.com\/watch\?v=([A-Za-z0-9_-]+)/i);
        if (watchMatch) {
            reason.push("Normalized watch URL");
            return { cleaned: `https://www.youtube.com/watch?v=${watchMatch[2]}`, reason: reason.join(", ") };
        }

        // youtu.be → expand
        const shortMatch = url.match(/^https?:\/\/youtu\.be\/([A-Za-z0-9_-]+)/i);
        if (shortMatch) {
            reason.push("Expanded youtu.be → watch");
            return { cleaned: `https://www.youtube.com/watch?v=${shortMatch[1]}`, reason: reason.join(", ") };
        }

        // Amazon cleanup
        if (/^https?:\/\/(www\.)?amazon\./i.test(url)) {
            const asinMatch = url.match(/\/(dp|gp\/product)\/([A-Z0-9]{10})/i);
            if (asinMatch) {
                reason.push("Amazon shortened to ASIN");
                return { cleaned: `https://www.amazon.com/dp/${asinMatch[2]}/`, reason: reason.join(", ") };
            }
        }

        // Substack cleanup - removes referral tracker ?r= or &r=
        if (/^https?:\/\/(www\.)?substack\.com/i.test(url)) {
            const subTrackers = [];
            cleaned = cleaned.replace(/([?&])(r=[^&]*)/gi, (match, sep, keyval) => {
                subTrackers.push(keyval.split("=")[0]);
                return "";
            });
            cleaned = cleaned.replace(/[?&]+$/, "");
            if (subTrackers.length > 0) {
                reason.push("Removed Substack " + subTrackers.join(", "));
            }
        }

        // Generic tracker cleanup
        const trackers = [];
        cleaned = cleaned.replace(trackerPattern, (match, sep, keyval) => {
            trackers.push(keyval.split("=")[0]);
            return "";
        });
        cleaned = cleaned.replace(/[?&]+$/, "");
        if (trackers.length > 0) {
            reason.push("Removed " + trackers.join(", "));
        }

        if (reason.length > 0) {
            return { cleaned, reason: reason.join(", ") };
        } else {
            return { cleaned: url, reason: null };
        }
    }

    function markCheck(el, reason) {
        if (!el.nextSibling || !el.nextSibling.classList || !el.nextSibling.classList.contains("link-clean-check")) {
            const check = document.createElement("span");
            check.textContent = " ✔";
            check.style.color = "#FFD700";
            check.style.fontSize = "smaller";
            check.className = "link-clean-check";
            check.title = reason || "Cleaned";
            check.setAttribute("data-clean-reason", reason || "");
            el.parentNode.insertBefore(check, el.nextSibling);
        }
    }

    function cleanLinks() {
        // Anchors
        document.querySelectorAll('a[href]').forEach(a => {
            const oldHref = a.href;
            const result = cleanUrl(oldHref);
            if (!result.reason) return;

            if (a.getAttribute("data-role") === "img") return;

            if (result.cleaned !== oldHref) {
                a.href = result.cleaned;
                if (a.innerText.includes(oldHref)) {
                    a.innerText = a.innerText.replace(oldHref, result.cleaned);
                }
                if (!a.querySelector("img")) {
                    markCheck(a, result.reason);
                }
            }
        });

        // Raw text URLs
        const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
        let node;
        while ((node = walker.nextNode())) {
            if (!node.nodeValue.includes("http")) continue;

            const urlRegex = /(https?:\/\/[^\s]+)/gi;
            let changed = false;
            let reasonStore = null;

            const newText = node.nodeValue.replace(urlRegex, (match) => {
                const res = cleanUrl(match);
                if (res.reason) {
                    changed = true;
                    reasonStore = res.reason;
                }
                return res.cleaned;
            });

            if (changed) {
                const span = document.createElement("span");
                span.textContent = newText;
                node.parentNode.replaceChild(span, node);
                markCheck(span, reasonStore);
            }
        }
    }

    cleanLinks();
    setInterval(cleanLinks, 2000);
})();