返回首頁 

Greasy Fork is available in English.

YT Live Chat Emoji Copy Tool

Make YouTube™ Live Chat's emoji can be copied.


Installer ce script?
// ==UserScript==// @name                YT Live Chat Emoji Copy Tool// @name:zh             YT 直播聊天室貼圖複製工具// @name:zh-TW          YT 直播聊天室貼圖複製工具// @name:zh-CN          YT 直播聊天室贴图复制工具// @namespace           https://github.com/kevin823lin// @version             0.3// @description         Make YouTube™ Live Chat's emoji can be copied.// @description:zh      讓 YouTube™ 直播聊天室的貼圖可以被複製// @description:zh-TW   讓 YouTube™ 直播聊天室的貼圖可以被複製// @description:zh-CN   让 YouTube™ 直播聊天室的贴图可以被复制// @author              kevin823lin// @match               https://www.youtube.com/live_chat*// @match               https://www.youtube.com/live_chat_replay*// @match               https://studio.youtube.com/live_chat*// @match               https://studio.youtube.com/live_chat_replay*// @icon                https://www.google.com/s2/favicons?domain=youtube.com// @require             https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js// @grant               none// @date                2025-01-11// @license             MIT// ==/UserScript==/*!* YT Live Chat Emoji Copy Tool v0.3* https://github.com/kevin823lin/yt-live-chat-emoji-copy-tool** Includes Lodash v4.17.21** Copyright (c) 2025 kevin823lin* Released under the MIT license* https://opensource.org/licenses/MIT** Date: 2025-01-11*/(function() {'use strict';// Your code here...const TYPES = {CHAT: Symbol("Chat"),INPUT: Symbol("Input"),PICKER: Symbol("Picker"),};const YOUTUBE_CHANNEL_ID_REGEX = /^UCkszU2WH9gy1mb0dV-11UJg\//;const CHAT_EMOJI_SELECTOR = 'img.emoji.yt-live-chat-text-message-renderer[shared-tooltip-text][data-emoji-id]:not(.copyable)';const INPUT_FIELD_SELECTOR = 'yt-live-chat-text-input-field-renderer > #input';const EMOJI_PICKER_SELECTOR = 'div.yt-emoji-picker-renderer#categories';const VERSION = (typeof GM_info !== 'undefined') ? GM_info?.script?.version : (typeof chrome !== 'undefined') ? chrome?.runtime?.getManifest()?.version : '';/*** Check if the emoji is already copyable.*/function isEmojiCopyable(emoji) {return emoji.classList.contains('copyable');}/*** Retrieve the emoji's ID.*/function getEmojiId(emoji) {return emoji.dataset.emojiId || emoji.id;}/*** Determine if the emoji is a YouTube-specific emoji.*/function isYoutubeEmoji(emoji) {const id = getEmojiId(emoji);return !id || YOUTUBE_CHANNEL_ID_REGEX.test(id);}/*** Update the emoji's alt attribute with colon format.*/function updateEmojiAltWithColon(emoji, compareText = null) {if (compareText && !compareText.match(emoji.alt)) return;emoji.alt = `:${isYoutubeEmoji(emoji) ? '' : '_'}${emoji.alt}:`;}/*** Process the emoji based on its type.*/function processEmoji(emoji, type) {if (isEmojiCopyable(emoji)) return;emoji.classList.add('copyable');let compareText = null;switch (type) {case TYPES.CHAT:if (!document.contains(emoji)) return;compareText = emoji.getAttribute('shared-tooltip-text');break;case TYPES.INPUT:if (!getEmojiId(emoji)) return;break;case TYPES.PICKER:compareText = emoji.getAttribute('aria-label');break;default:console.warn(`Unknown emoji type: ${type}`);return;}updateEmojiAltWithColon(emoji, compareText);}/*** Update all emojis in the selected range.*/function updateSelectedRangeEmojis() {try {const selection = window.getSelection();if (!selection.rangeCount) return;const range = selection.getRangeAt(0);const fragment = range.cloneContents();const selectedEmojis = fragment.querySelectorAll(CHAT_EMOJI_SELECTOR);selectedEmojis.forEach(clonedEmoji => {const originalEmoji = range.commonAncestorContainer.querySelector(`img.emoji#${clonedEmoji.id}`);if (originalEmoji) {processEmoji(originalEmoji, TYPES.CHAT);}});} catch (error) {console.error(`Error in updateSelectedRangeEmojis: ${error}`);}}/*** Update all emojis inside the input field.*/function updateInputFieldEmojis(inputField) {const inputEmojis = inputField?.getElementsByClassName('yt-live-chat-text-input-field-renderer') || [];Array.from(inputEmojis).forEach((node) => processEmoji(node, TYPES.INPUT));}/*** Update emojis in the emoji picker based on mutations.*/function updateEmojiPickerEmojis(mutations) {mutations.forEach((mutation) => {mutation.addedNodes.forEach((node) => {if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'IMG') {processEmoji(node, TYPES.PICKER);}});});}/*** Initialize observers and event listeners on page load.*/function initializeObservers() {const inputField = document.querySelector(INPUT_FIELD_SELECTOR);const emojiPicker = document.querySelector(EMOJI_PICKER_SELECTOR);const selectionchangeCallback = _.debounce(updateSelectedRangeEmojis, 200);const observeInputFieldCallback = _.debounce(() => {if (inputField) updateInputFieldEmojis(inputField);}, 200);const observeEmojiPickerCallback = updateEmojiPickerEmojis;const inputFieldObserver = new MutationObserver(observeInputFieldCallback);const emojiPickerObserver = new MutationObserver(observeEmojiPickerCallback);if (inputField) inputFieldObserver.observe(inputField, { childList: true, subtree: true });if (emojiPicker) emojiPickerObserver.observe(emojiPicker, { childList: true, subtree: true });document.addEventListener('selectionchange', selectionchangeCallback);}if (/^\/live_chat/.test(window.location.pathname)) {initializeObservers();console.log(`[YT Live Chat Emoji Copy Tool${VERSION ? ` v${VERSION}` : ''}]`);}})();