Last active
February 17, 2022 16:10
-
-
Save lleaff/48db35c180ab1b684a0f2c7d9c493244 to your computer and use it in GitHub Desktop.
Youtube Userscript: Copy short URL to clipboard on video title click
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Youtube - Copy short URL to clipboard | |
// @namespace lleaff | |
// @supportURL https://gist.github.com/lleaff/48db35c180ab1b684a0f2c7d9c493244#comments | |
// @match https://www.youtube.com/* | |
// @version 2 | |
// @run-at document-end | |
// @grant GM_setClipboard | |
// @noframes | |
// @description Copy short URL to clipboard on video title click | |
// ==/UserScript== | |
const TEXT = { | |
CLICK_TO_COPY_VID_URL_TO_CLIPBOARD: 'Click to copy short URL', | |
}; | |
function sleep(ms) { | |
return new Promise((resolve) => setTimeout(resolve, ms)) | |
} | |
async function waitForSelector(selector, { intervalMs, root=document }) { | |
while(true) { | |
const element = root.querySelector(selector) | |
if (element) { | |
return element; | |
} | |
await sleep(intervalMs) | |
} | |
} | |
async function waitForSelectorAll(selector, { intervalMs, root=document }) { | |
while(true) { | |
const element = root.querySelectorAll(selector) | |
if (element.length > 0) { | |
return [...element]; | |
} | |
await sleep(intervalMs) | |
} | |
} | |
function getResetable({ callbackIn, callbackOut, timeout }) { | |
let timeoutId; | |
return () => { | |
callbackIn(); | |
clearTimeout(timeoutId); | |
timeoutId = setTimeout(() => { | |
callbackOut(); | |
timeoutId = null; | |
}, timeout); | |
}; | |
} | |
function setupYTTooltip({ element, text }) { | |
element.setAttribute('title', text) | |
} | |
const extractYoutubeVidId = url => { | |
const match = url.match(/v=([^&]+)/); | |
return match && match[1]; | |
}; | |
const getShortVidUrl = (url) => `https://youtu.be/${extractYoutubeVidId(url.search || url)}`; | |
function setupClickToPushToClipboardElement({ element: el, text }) { | |
const color = { | |
initial: el.style.color || null, | |
active: '#aaa' | |
}; | |
const opacity = { | |
initial: el.style.opacity || null, | |
active: '0.9' | |
}; | |
function setActiveStyle() { | |
el.style.color = color.active; | |
el.style.opacity = opacity.active; | |
} | |
function setInactiveStyle() { | |
el.style.color = color.initial; | |
el.style.opacity = opacity.initial; | |
} | |
const animate = getResetable({ callbackIn: setActiveStyle, | |
callbackOut: setInactiveStyle, | |
timeout: 0.5e3 | |
}); | |
el.addEventListener('click', ev => { | |
console.log('Clicked video title 🖱️') | |
GM_setClipboard(text); | |
animate(); | |
ev.preventDefault(); | |
}); | |
setupYTTooltip({ element: el, | |
text: TEXT.CLICK_TO_COPY_VID_URL_TO_CLIPBOARD }); | |
return el; | |
} | |
/** | |
* Click on video title to copy short url to clipboard | |
*/ | |
async function setupClickToCopyVidTitle(url) { | |
const innerTitleEl = await waitForSelector('h1.title:not([hidden]', { intervalMs: 50, }); | |
if (innerTitleEl) { | |
setupClickToPushToClipboardElement({ element: innerTitleEl, | |
text: getShortVidUrl(url), | |
}); | |
} | |
} | |
/** | |
* Executed on every new page load | |
*/ | |
async function main() { | |
/* Watching video */ | |
if (location.href.match(/\/watch\?/)) { | |
await setupClickToCopyVidTitle(location); | |
} | |
} | |
main(); | |
/* Structured Page Fragment api, Youtube's lib to avoid full-page refreshes*/ | |
document.addEventListener("spfdone", main); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment