您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Copy short URL to clipboard on video title click
// ==UserScript== // @name Youtube - Copy short URL to clipboard // @namespace lleaff // @supportURL https://gist.github.com/lleaff/48db35c180ab1b684a0f2c7d9c493244 // @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);