您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
add user name for comment
// ==UserScript== // @name YouTube Comment Username Reveals // @description add user name for comment // @namespace https://htsign.hateblo.jp // @version 0.3.7 // @author htsign // @match https://www.youtube.com/* // @grant none // ==/UserScript== { 'use strict'; /** @type {Map<string, string | null>} */ const nameMap = new Map(); const pageManager = document.getElementById('page-manager'); if (pageManager != null) { /** * @param {Node} node * @returns {node is HTMLElement} */ const isHTMLElement = node => node instanceof HTMLElement; /** * * @param {HTMLElement} element * @param {Name} name * @returns {element is HTMLElement & { is: Name }} * @template {string} Name */ const is = (element, name) => 'is' in element && element.is === name; const decode = (() => { /** * @type {[string, string][]} */ const ENTITIES = [ ['amp', '&'], ['apos', '\''], ['quot', '"'], ['nbsp', ' '], ['lt', '<'], ['gt', '>'], ['#39', '\''], ]; /** * @param {string} s * @returns {string} */ return s => ENTITIES.reduce((acc, [entity, sym]) => acc.replaceAll(`&${entity};`, sym), s); })(); /** * @param {HTMLAnchorElement} anchor * @param {string} name */ const appendName = (anchor, name) => { // <span style="margin-left: 4px;" data-name="$name">( $name )</span> const span = anchor.querySelector(`span[data-name="${name}"]`) ?? Object.assign( document.createElement('span'), { textContent: `( ${name} )`, style: 'margin-left: 4px' }, ); Object.assign(span.dataset, { name }); // remove other names if exists for (const el of anchor.querySelectorAll(`span[data-name]:not([data-name="${name}"])`)) { el.remove(); } // append them name (anchor.querySelector('ytd-channel-name') ?? anchor).append(span); }; const pageManagerObserver = new MutationObserver(records => { const addedElements = records.flatMap(r => [...r.addedNodes]).filter(isHTMLElement); for (const el of addedElements) { const commentsWrapper = el.querySelector('#columns #primary-inner #below ytd-comments'); if (commentsWrapper != null) { const contentsObserver = new MutationObserver(records => { const addedElements = records.flatMap(r => [...r.addedNodes]).filter(isHTMLElement); for (const el of addedElements.filter(el => is(el, 'ytd-comment-view-model'))) { for (const author of el.querySelectorAll('#author-text, #name')) { const channelName = author.textContent.trim(); if (channelName == null) { console.warn('Username Reveals [name not found]:', author); continue; } // append user name from map if nameMap has if (nameMap.has(channelName)) { const f = () => { // break if record is removed if (!nameMap.has(channelName)) return; const name = nameMap.get(channelName); if (name == null) { return requestIdleCallback(f); } appendName(author, name); }; f(); continue; } // reserve a record key for supress unnecessary request nameMap.set(channelName, null); fetch(author.href).then(async response => { const text = await response.text(); const [name] = text.match(/(?<=\<title\>).+?(?= - YouTube)/) ?? []; if (name != null) { const _name = decode(name); appendName(author, _name); nameMap.set(channelName, _name); } }, error => { console.warn('Username Reveals [error]:', error); nameMap.delete(channelName); }); } } }); contentsObserver.observe(commentsWrapper, { childList: true, subtree: true }); } } }); pageManagerObserver.observe(pageManager, { childList: true }); } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址