Replaces all present Twitch clip links with the better clip link (clips.twitch.tv)
// ==UserScript==
// @name Twitch Clip Link Replacer
// @namespace twitch.tv
// @include *://*.twitch.tv/*/clip
// @include http://clips.twitch.tv/*
// @include https://clips.twitch.tv/*
// @include https://*.twitch.tv/*
// @version 1.0.1
// @author ftn
// @description Replaces all present Twitch clip links with the better clip link (clips.twitch.tv)
// @license GPLv3
// ==/UserScript==
const CLIP_REGEX = /^https?:\/\/(?:www\.)?twitch\.tv\/[^\/]+\/clip\/([^\/?#]+)/i;
function createUrl(url) {
const m = url.match(CLIP_REGEX);
// the first returned index is unneeded so the second should be the one to grab
return m ? `https://clips.twitch.tv/${m[1]}` : null;
}
function replace(nodeList) {
nodeList.forEach(a => {
const better = createUrl(a.href);
if (better) a.href = better;
});
}
function processDocument() {
replace(document.querySelectorAll('a[href]'));
const observer = new MutationObserver(mutations => {
for (const mut of mutations) {
mut.addedNodes.forEach(node => {
if (node.nodeType !== Node.ELEMENT_NODE) return;
if (node.matches && node.matches('a[href]')) {
replace([node]);
}
const innerLinks = node.querySelectorAll? node.querySelectorAll('a[href]'): [];
if (innerLinks.length) replace(innerLinks);
});
}
});
// this stuff is for sites that navigate pages directly
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
if (self.navigation) {
navigation.addEventListener('navigatesuccess', processDocument);
} else {
let u = location.href;
new MutationObserver(() => u !== (u = location.href) && processDocument())
.observe(document, {subtree: true, childList: true});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', processDocument);
} else {
processDocument();
}