您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
This is to change the handle in the YouTube comments section to a username.
当前为
// ==UserScript== // @name Return YouTube Comment Username // @name:ja YouTubeコメント欄の名前を元に戻す // @version 0.1.3 // @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/watch/getUserName.ts async function getUserName(href) { const id = href.split("/")[4]; const data = await fetch( `https://yt-returnname-api.pages.dev/api/idToName?id=${id}`, { method: "POST" } ).then((res) => res.text()); return data; } // src/watch/replaceComments.ts function replaceComments(comments, page) { const nameStore = []; comments.forEach(async (c, index) => { let nthChild; if (page === 0) { nthChild = index + 1; } else { nthChild = page * 20 + (index + 1); } const commentElem = await findElement( `#comments > #sections > #contents > ytd-comment-thread-renderer:nth-child(${nthChild})` ); 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) { let nthChild; if (page === 0) { nthChild = targetIndex + 1; } else { 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) { const handleRenderingSucess = () => { replaceComments(comments, commentsPage); commentsPage = commentsPage + 1; eventRoot.native.removeEventListener( "commentsRenderingSuccess", handleRenderingSucess ); }; eventRoot.native.addEventListener( "commentsRenderingSuccess", handleRenderingSucess ); } else { replaceComments(comments, commentsPage); 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(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址