// ==UserScript==
// @name Return YouTube Comment Username
// @name:ja YouTubeコメント欄の名前を元に戻す
// @version 0.1.6
// @author yakisova41
// @license MIT
// @namespace https://yt-returnname-api.pages.dev/extension/
// @description This is to change the handle in the YouTube comments section to a username.
// @description:ja YouTubeのコメント欄の名前がハンドル(@...)表記になってしまった場合に、元のユーザーネームに上書きします。
// @match https://www.youtube.com/*
// @grant none
// ==/UserScript==
(() => {
// src/lib/eventRoot.ts
function pageChangeListener(eventElement) {
let beforeHref = "";
const observer = new MutationObserver(() => {
const href = location.href;
if (href !== beforeHref) {
eventElement.dispatchEvent(
new CustomEvent("pageChange", {
detail: {
beforeHref,
newHref: href
}
})
);
}
beforeHref = href;
});
observer.observe(document.querySelector("body"), {
childList: true,
subtree: true
});
}
function createEventRoot() {
const eventElement = document.createElement("div");
pageChangeListener(eventElement);
return {
addEventListener: (eName, listener, options) => {
eventElement.addEventListener(eName, listener, options);
const pageChangeListener2 = () => {
eventElement.removeEventListener(
"pageChange",
pageChangeListener2
);
eventElement.removeEventListener(eName, listener, options);
};
eventElement.addEventListener("pageChange", pageChangeListener2);
},
dispatchEvent: (e) => {
eventElement.dispatchEvent(e);
},
removeEventListener: (eName, listener, options) => {
eventElement.removeEventListener(eName, listener, options);
},
native: eventElement
};
}
// src/lib/findElement.ts
var findElement = (selector) => {
return new Promise((resolve, reject) => {
if (isNativeInterval()) {
const interval = setInterval(() => {
const elem = document.querySelector(selector);
if (elem !== null) {
clearInterval(interval);
resolve(elem);
}
});
} else {
let search = function() {
setTimeout(() => {
const elem = document.querySelector(selector);
if (elem !== null) {
resolve(elem);
} else {
search();
}
});
};
search();
}
});
};
var findElementAll = (selector) => {
return new Promise((resolve, reject) => {
if (isNativeInterval()) {
const interval = setInterval(() => {
const elems = document.querySelectorAll(selector);
if (elems.length !== 0) {
clearInterval(interval);
resolve(elems);
}
});
} else {
let search = function() {
setTimeout(() => {
const elems = document.querySelectorAll(selector);
if (elems.length !== 0) {
resolve(elems);
} else {
search();
}
});
};
search();
}
});
};
function isNativeInterval() {
return window.setInterval.toString() === "function setInterval() { [native code] }";
}
// src/lib/getUserName.ts
async function getUserName(href) {
const id = href.split("/")[4];
const data = await fetch(
`https://www.youtube.com/youtubei/v1/browse?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8&prettyPrint=false`,
{
method: "POST",
headers: {
accept: "*/*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "ja",
"content-type": "application/json",
cookie: `GPS=1; YSC=sHEZ9k4QSS0; DEVICE_INFO=DEVICE_INFO; VISITOR_INFO1_LIVE=LLIIVVEE; PREF=f6=40000000&tz=Asia.Tokyo; ST-o2eza2=itct=itct&endpoint=%7B%22clickTrackingParams%22%3A%22CBQQ8JMBGAciEwjNqtCN86H9AhXnm1YBHABY%3D%22%2C%22commandMetadata%22%3A%7B%22webCommandMetadata%22%3A%7B%22url%22%3A%22%2F%40FUCKYOUTUBE%2Fchannels%22%2C%22webPageType%22%3A%22WEB_PAGE_TYPE_CHANNEL%22%2C%22rootVe%22%3A3611%2C%22apiUrl%22%3A%22%2Fyoutubei%2Fv1%2Fbrowse%22%7D%7D%2C%22browseEndpoint%22%3A%7B%22browseId%22%3A%22${id}%22%2C%22params%22%3A%22EghjaGFubmVsc_IGBAoCUgA%253D%22%2C%22canonicalBaseUrl%22%3A%22%2F%40FUCK_YOUTUBE%22%7D%7D`,
dnt: "1",
referer: `https://www.youtube.com/channel/${id}`,
"sec-ch-ua": `"Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"`,
"sec-ch-ua-arch": "x86",
"sec-ch-ua-bitness": "64",
"sec-ch-ua-full-version": "110.0.5481.104",
"sec-ch-ua-full-version-list": `"Chromium";v="110.0.5481.104", "Not A(Brand";v="24.0.0.0", "Google Chrome";v="110.0.5481.104"`,
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "Windows",
"sec-ch-ua-platform-version": "15.0.0",
"sec-ch-ua-wow64": "?0",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "same-origin",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
"x-client-data": "x-client-data",
"x-goog-authuser": "0",
"x-goog-visitor-id": "visitorData",
"x-origin": "https://www.youtube.com",
"x-youtube-bootstrap-logged-in": "true",
"x-youtube-client-name": "1",
"x-youtube-client-version": "2.20230217.01.00"
},
body: JSON.stringify({
context: {
client: {
hl: "ja",
gl: "JP",
remoteHost: "1919:8a10:1145:1419:e1c9:b81a:09db:ff3a",
deviceMake: "",
deviceModel: "",
visitorData: "visitorData",
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36,gzip(gfe)",
clientName: "WEB",
clientVersion: "2.20230217.01.00",
osName: "Windows",
osVersion: "10.0",
originalUrl: "https://www.youtube.com/@FUCK_YOUTUBE/channels",
platform: "DESKTOP",
clientFormFactor: "UNKNOWN_FORM_FACTOR",
configInfo: {
appInstallData: "appInstallData"
},
userInterfaceTheme: "USER_INTERFACE_THEME_DARK",
timeZone: "Asia/Tokyo",
browserName: "Chrome",
browserVersion: "110.0.0.0",
acceptHeader: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
deviceExperimentId: "deviceExperimentId",
screenWidthPoints: 599,
screenHeightPoints: 937,
screenPixelDensity: 1,
screenDensityFloat: 1,
utcOffsetMinutes: 540,
memoryTotalKbytes: "8000000",
mainAppWebInfo: {
graftUrl: "/@FUCK_YOUTUBE/channels",
pwaInstallabilityStatus: "PWA_INSTALLABILITY_STATUS_CAN_BE_INSTALLED",
webDisplayMode: "WEB_DISPLAY_MODE_BROWSER",
isWebNativeShareAvailable: true
}
},
user: { lockedSafetyMode: false },
request: {
useSsl: true,
internalExperimentFlags: [],
consistencyTokenJars: []
},
clickTracking: {
clickTrackingParams: "CCgQ8JMBGAgiEwiW5Pey8qH9AhVVSA8CHUHLAGc="
},
adSignalsInfo: {
params: [
{ key: "dt", value: "1676820301790" },
{ key: "flash", value: "0" },
{ key: "frm", value: "0" },
{ key: "u_tz", value: "540" },
{ key: "u_his", value: "1" },
{ key: "u_h", value: "1080" },
{ key: "u_w", value: "1920" },
{ key: "u_ah", value: "1040" },
{ key: "u_aw", value: "1920" },
{ key: "u_cd", value: "24" },
{ key: "bc", value: "31" },
{ key: "bih", value: "937" },
{ key: "biw", value: "582" },
{
key: "brdim",
value: "-1920,0,-1920,0,1920,0,1920,1040,599,937"
},
{ key: "vis", value: "1" },
{ key: "wgl", value: "true" },
{ key: "ca_type", value: "image" }
]
}
},
browseId: id,
params: "EghjaGFubmVsc_IGBAoCUgA%3D"
})
}
).then((res) => res.text()).then((text) => {
const data2 = JSON.parse(text);
const name = data2["header"]["c4TabbedHeaderRenderer"]["title"];
return name;
});
return data;
}
// src/watch/replaceComments.ts
function replaceComments(comments, page, eventRoot) {
const nameStore = [];
comments.forEach(async (c, index) => {
const nthChild = page * 20 + (index + 1);
const commentElem = await findElement(
`#comments > #sections > #contents > ytd-comment-thread-renderer:nth-child(${nthChild})`
);
eventRoot.addEventListener("pageChange", () => {
commentElem.remove();
});
const channelHrefElem = commentElem.querySelector(
"#comment > #body > #main > #header > #header-author > h3 > a "
);
let nameElem;
nameElem = commentElem.querySelector(
"#comment > #body > #main > #header > #header-author > #author-comment-badge > ytd-author-comment-badge-renderer > #name > ytd-channel-name > #container > #text-container > #text "
);
if (nameElem === null) {
nameElem = channelHrefElem.querySelector("span");
}
if (channelHrefElem.href in nameStore) {
nameElem.innerHTML = nameStore[channelHrefElem.href];
} else {
getUserName(channelHrefElem.href).then((name) => {
nameElem.innerHTML = name;
nameStore[channelHrefElem.href] = name;
});
}
});
}
// src/listeners/commentRenderingListener.ts
async function renderingListener(eventRoot) {
let renderingTrigger = false;
const contents = await findElement("#comments > #sections");
const observer = new MutationObserver(() => {
if ("can-show-more" in contents.attributes) {
renderingTrigger = true;
} else if ("continuation-is-reloading" in contents.attributes) {
renderingTrigger = true;
eventRoot.dispatchEvent(
new CustomEvent("commentsContinuationReloading")
);
} else {
if (renderingTrigger) {
renderingTrigger = false;
eventRoot.dispatchEvent(
new CustomEvent("commentsRenderingSuccess")
);
}
}
});
observer.observe(contents, {
attributes: true
});
return observer;
}
// src/watch/replaceReplies.ts
async function replaceReplies(page, targetIndex) {
const nthChild = page * 20 + (targetIndex + 1);
const repliesElem = await findElementAll(
`#comments > #sections > #contents > ytd-comment-thread-renderer:nth-child(${nthChild}) > #replies > ytd-comment-replies-renderer > #expander > #expander-contents > #contents > ytd-comment-renderer`
);
repliesElem.forEach((elem) => {
const channelHrefElem = elem.querySelector(
"#body > #main > #header > #header-author > h3 > a "
);
let nameElem;
nameElem = elem.querySelector(
"#body > #main > #header > #header-author > #author-comment-badge > ytd-author-comment-badge-renderer > #name > ytd-channel-name > #container > #text-container > #text "
);
if (nameElem === null) {
nameElem = channelHrefElem.querySelector("span");
}
getUserName(channelHrefElem.href).then((name) => {
nameElem.innerHTML = name;
});
const textElems = elem.querySelectorAll(
"#body > #main > #comment-content > #expander > #content > #content-text > a.yt-formatted-string"
);
textElems.forEach((textElem) => {
const text = textElem.innerHTML;
if (text.match("@.*")) {
getUserName(textElem.href).then((name) => {
textElem.innerHTML = "@" + name;
});
}
});
});
}
// src/watch/watch.ts
async function watch(eventRoot) {
const renderListener = await renderingListener(eventRoot);
eventRoot.addEventListener("pageChange", () => {
renderListener.disconnect();
});
const commentsTargetIdStore = [];
let commentsPage = 0;
eventRoot.addEventListener("commentsContinuationReloading", () => {
commentsPage = 0;
});
eventRoot.addEventListener(
"commentFetch",
({ detail: { comments, mode } }) => {
comments.forEach((comment, index) => {
if (comment["commentThreadRenderer"]["replies"] !== void 0) {
commentsTargetIdStore.push({
index,
commentsPage,
targetId: comment["commentThreadRenderer"]["replies"]["commentRepliesRenderer"]["targetId"]
});
}
});
if (mode === 1) {
if (comments.length !== 20) {
replaceComments(comments, commentsPage, eventRoot);
commentsPage = commentsPage + 1;
} else {
const handleRenderingSucess = () => {
replaceComments(comments, commentsPage, eventRoot);
commentsPage = commentsPage + 1;
eventRoot.native.removeEventListener(
"commentsRenderingSuccess",
handleRenderingSucess
);
};
eventRoot.native.addEventListener(
"commentsRenderingSuccess",
handleRenderingSucess
);
}
} else {
replaceComments(comments, commentsPage, eventRoot);
commentsPage = commentsPage + 1;
}
}
);
eventRoot.addEventListener(
"repliesFetch",
({ detail: { replies, targetId } }) => {
commentsTargetIdStore.forEach((targetData, index) => {
if (targetData.targetId === targetId) {
replaceReplies(targetData.commentsPage, targetData.index);
}
});
}
);
}
// src/lib/FetchIntercepter.ts
function FetchIntercepter(originalFetch) {
const actions = [];
return {
start: () => {
window.fetch = (...arg) => {
const [request, init] = arg;
const response = originalFetch(request, init);
response.then((res) => {
actions.forEach((action) => {
action({
request,
init,
response: res
});
});
});
return response;
};
},
addAction: (action) => {
actions.push(action);
},
stop: () => {
window.fetch = originalFetch;
}
};
}
// src/listeners/commentFetchListener.ts
function commentFetchListener(eventRoot) {
const intercepter = FetchIntercepter(window.fetch);
intercepter.start();
intercepter.addAction(async (data) => {
if (typeof data.request["url"] === "string" && data.request["url"].match(
"https://www.youtube.com/youtubei/v1/next.*"
)) {
const responseClone = data.response.clone();
const text = await responseClone.text();
const body = JSON.parse(text);
const commentFetchMode = is_comments(body);
if (commentFetchMode === 1) {
const comments = body["onResponseReceivedEndpoints"][1]["reloadContinuationItemsCommand"]["continuationItems"];
const data2 = removeContinuationItem(comments);
eventRoot.dispatchEvent(
new CustomEvent("commentFetch", {
detail: {
comments: data2,
mode: commentFetchMode
}
})
);
}
if (commentFetchMode === 2) {
const comments = body["onResponseReceivedEndpoints"][0]["appendContinuationItemsAction"]["continuationItems"];
const data2 = removeContinuationItem(comments);
eventRoot.dispatchEvent(
new CustomEvent("commentFetch", {
detail: {
comments: data2,
mode: commentFetchMode
}
})
);
}
if (commentFetchMode === 0) {
if (body["onResponseReceivedEndpoints"][0]["appendContinuationItemsAction"]["targetId"].match("comment-replies.*")) {
const replies = body["onResponseReceivedEndpoints"][0]["appendContinuationItemsAction"]["continuationItems"];
const targetId = body["onResponseReceivedEndpoints"][0]["appendContinuationItemsAction"]["targetId"];
eventRoot.dispatchEvent(
new CustomEvent("repliesFetch", {
detail: {
replies,
targetId
}
})
);
}
}
}
});
return {
stop: intercepter.stop
};
}
function is_comments(body) {
const onResponseReceivedEndpoints = body["onResponseReceivedEndpoints"];
if (onResponseReceivedEndpoints.length > 1 && "reloadContinuationItemsCommand" in onResponseReceivedEndpoints[1] && onResponseReceivedEndpoints[1]["reloadContinuationItemsCommand"]["targetId"] === "comments-section") {
return 1;
}
if ("appendContinuationItemsAction" in onResponseReceivedEndpoints[0] && onResponseReceivedEndpoints[0]["appendContinuationItemsAction"]["targetId"] === "comments-section") {
return 2;
}
return 0;
}
function removeContinuationItem(comments) {
comments.forEach((comment, index) => {
if ("continuationItemRenderer" in comment) {
comments.splice(index, 1);
}
});
return comments;
}
// src/index.ts
function main() {
const eventRoot = createEventRoot();
commentFetchListener(eventRoot);
eventRoot.native.addEventListener(
"pageChange",
async (e) => {
const { newHref } = e.detail;
const pageName = newHref.split("/")[3].split("?")[0];
if (pageName === "watch") {
watch(eventRoot);
}
}
);
}
main();
})();