您需要先安装一个扩展,例如 篡改猴、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);