Skip to content

Instantly share code, notes, and snippets.

@lumynou5
Last active July 2, 2025 14:57
Show Gist options
  • Save lumynou5/74bcbab54cd9d8fcd3c873fffbac5d3d to your computer and use it in GitHub Desktop.
Save lumynou5/74bcbab54cd9d8fcd3c873fffbac5d3d to your computer and use it in GitHub Desktop.
Make YouTube display the names of commenters instead of their handles.
// ==UserScript==
// @name YouTube Commenter Names
// @version 1.7.5
// @description Make YouTube display the names of commenters instead of their handles.
// @author Lumynous
// @license MIT
// @match https://www.youtube.com/*
// @match https://studio.youtube.com/*
// @exclude https://www.youtube.com/persist_identity
// @exclude https://studio.youtube.com/persist_identity
// @exclude https://studio.youtube.com/ytscframe
// @downloadURL https://gist.github.com/lumynou5/74bcbab54cd9d8fcd3c873fffbac5d3d/raw/youtube-commenter-names.user.js
// @updateURL https://gist.github.com/lumynou5/74bcbab54cd9d8fcd3c873fffbac5d3d/raw/~meta
// ==/UserScript==
'use strict';
const watchElm = (function () {
const TRUE_FN = () => true;
const elmObserver = new MutationObserver(elmObserverCallback);
elmObserver.observe(document, {childList: true, subtree: true});
const callbacks = new Set();
function elmObserverCallback(mutations) {
for (const {callback, selector} of callbacks) {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType !== Node.ELEMENT_NODE)
continue;
if (node.matches(selector)) {
callback(node);
}
for (const x of node.querySelectorAll(selector)) {
callback(x);
}
}
}
}
}
function elmCallback(observer, action, filter, elm) {
if (filter(elm)) {
action(elm);
observer.observe(elm, {attributes: true});
}
}
return (selector, action, filter) => {
filter ??= TRUE_FN;
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
action(mutation.target);
}
});
const callback = elmCallback.bind(null, observer, action, filter);
for (const elm of document.querySelectorAll(selector)) {
callback(elm);
}
callbacks.add({callback, selector});
};
})();
async function fetchInternalApi(endpoint, body) {
const response = await fetch(
`https://www.youtube.com/youtubei/v1/${endpoint}?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8`,
{
method: 'POST',
body: JSON.stringify({
context: {client: {clientName: 'WEB', clientVersion: '2.20240411.01.00'}},
...body,
}),
}
);
return await response.json();
}
function cacheFunctionDecorator(f) {
const cache = new Map();
return (arg) => {
let res = cache.get(arg);
if (res === undefined) {
res = f(arg);
cache.set(arg, res);
}
return res;
};
}
const getChannelId = cacheFunctionDecorator(async (url) => {
let json = await fetchInternalApi('navigation/resolve_url', {url});
if (!json.endpoint.browseEndpoint) {
// Workaround: Some channels such as @rayduenglish behave strange. Normally GETing
// channel pages result 303 and redirect to `/rayduenglish` for example; the internal
// API responses similarly, the workaround is to resolve twice. However, some are
// impossible to resolve correctly; for example, requesting `/@Konata` redirected to
// `/user/Konata`, and `/user/Konata` leads 404. This is probably a bug of YouTube.
json = await fetchInternalApi('navigation/resolve_url', json.endpoint.urlEndpoint);
}
return json.endpoint.browseEndpoint.browseId;
});
const getChannelName = cacheFunctionDecorator(async (id) => {
const json = await fetchInternalApi('browse', {browseId: id});
return json.metadata.channelMetadataRenderer.title;
});
function replaceText(node, text) {
if (node.firstChild.textContent === text)
return;
node.firstChild.textContent = text;
}
if (location.pathname === '/live_chat') {
watchElm('yt-live-chat-author-chip:not([hidden])', (elm) => {
getChannelName(elm.__dataHost.__data.data.authorExternalChannelId)
.then((name) => replaceText(elm.querySelector('#author-name'), name));
});
} else if (location.hostname === 'www.youtube.com') {
// Mentions in titles.
watchElm('#title.ytd-watch-metadata a.yt-simple-endpoint', (elm) => {
getChannelName(elm.data.browseEndpoint.browseId)
.then((name) => replaceText(elm, name));
}, (elm) => elm.pathname[1] === '@' /* Skip hashtags. */);
// Commenters.
watchElm('#author-text.ytd-comment-view-model' +
':not(ytd-browse *, ytd-popup-container #expander *)', (elm) => {
getChannelName(elm.data.browseEndpoint.browseId)
.then((name) => replaceText(elm.firstElementChild, name));
});
watchElm('#name.ytd-author-comment-badge-renderer', (elm) => {
getChannelName(elm.data.browseEndpoint.browseId)
.then((name) => replaceText(elm.querySelector('#text'), name));
});
// Commenters in posts and in notification previews.
watchElm('ytd-browse #author-text.ytd-comment-view-model, ' +
'ytd-popup-container #expander #author-text.ytd-comment-view-model', (elm) => {
getChannelId(elm.href)
.then(getChannelName)
.then((name) => replaceText(elm.firstElementChild, name));
});
// Mentions in comments.
watchElm('#content-text.ytd-comment-view-model a', (elm) => {
getChannelName(elm.href.slice(elm.href.lastIndexOf('/') + 1))
.then((name) => replaceText(elm, `\xA0${name}\xA0`));
}, (elm) => elm.textContent.trim()[0] === '@' /* Skip anchors such as timestamps. */);
} else {
// TODO: After changing the filters of comments, some commenters' names might be IDs
// if they're the same one, because their attributes wasn't changed. A solution is
// to watch their character data, but it causes a lock with Edge's translation
// feature.
watchElm('#name.ytcp-comment', (elm) => {
getChannelId(elm.href)
.then(getChannelName)
.then((name) => replaceText(elm.firstElementChild, name));
});
watchElm('#badge-name.ytcp-author-comment-badge', (elm) => {
getChannelId(elm.href)
.then(getChannelName)
.then((name) => replaceText(elm.firstElementChild, name));
});
}
// ==UserScript==
// @name YouTube Commenter Names
// @version 1.7.5
// @description Make YouTube display the names of commenters instead of their handles.
// @author Lumynous
// @license MIT
// @match https://www.youtube.com/*
// @match https://studio.youtube.com/*
// @exclude https://www.youtube.com/persist_identity
// @exclude https://studio.youtube.com/persist_identity
// @exclude https://studio.youtube.com/ytscframe
// @downloadURL https://gist.github.com/lumynou5/74bcbab54cd9d8fcd3c873fffbac5d3d/raw/youtube-commenter-names.user.js
// @updateURL https://gist.github.com/lumynou5/74bcbab54cd9d8fcd3c873fffbac5d3d/raw/~meta
// ==/UserScript==
@DarkblooM-IO
Copy link

Thank you for this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment