您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
YouTube用户黑名单,拉黑指定用户(自动隐藏其评论,可手动查看)。
当前为
// ==UserScript== // @name YouTube User Block // @name:en YouTube User Block // @namespace https://github.com/ChanthMiao/ // @icon https://www.gstatic.com/youtube/img/branding/favicon/favicon_144x144.png // @version 1.5.0 // @description YouTube用户黑名单,拉黑指定用户(自动隐藏其评论,可手动查看)。 // @description:en YouTube User BlockList,blocking specific users (hide all their comments, support manual viewing). // @author ChanthMiao // @match *://www.youtube.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @compatible chrome // @compatible firefox // @license MIT // ==/UserScript== (function () { 'use strict'; function toggleButton(element) { // Check to see if the button is pressed var pressed = (element.getAttribute("aria-pressed") === "true"); const user = element.parentElement.parentElement.parentElement.parentElement.querySelector("#author-text > span").textContent.trim(); if (!pressed && !blockList.includes(user)) { blockUser(user); } else if (pressed && blockList.includes(user)) { disblockUser(user); } } function handleBtnClick(event) { toggleButton(event.target); } function handleBtnKeyDown(event) { // Check to see if space or enter were pressed if (event.key === " " || event.key === "Enter" || event.key === "Spacebar") { // "Spacebar" for IE11 support // Prevent the default action to stop scrolling when space is pressed event.preventDefault(); toggleButton(event.target); } } function createNodeListener(node, config, mutationCallback) { const observer = new MutationObserver(mutationCallback); observer.observe(node, config); return observer; } function injectBlockButton(comment) { const user = comment.querySelector("#author-text > span").textContent.trim(); const arc = comment.querySelector("#creator-heart"); var block = document.createElement("div"); block.setAttribute("id", "block-button"); if (blockList.includes(user)) { block.setAttribute("title", "解封"); } else { block.setAttribute("title", "拉黑"); } var btn = document.createElement("button"); btn.setAttribute("id", "block-button-item"); btn.setAttribute("role", "button"); btn.setAttribute("aria-pressed", blockList.includes(user)); btn.addEventListener("tap", handleBtnClick, false); btn.addEventListener("keydown", handleBtnKeyDown, false); block.append(btn); var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("id", "block-button-icon"); svg.setAttribute("viewbox", "0 0 24 24"); svg.setAttribute("width", "16px"); svg.setAttribute("height", "16px"); svg.setAttribute("focusable", false); svg.setAttribute("class", "style-scope yt-icon"); svg.setAttribute("preserveAspectRatio", "xMidYMid meet"); svg.setAttribute("style", "pointer-events: none;display: block;width: 100%;height: 100%;"); btn.append(svg); var g = document.createElementNS("http://www.w3.org/2000/svg", "g"); g.setAttribute("class", "style-scope yt-icon"); svg.append(g); var path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", "M 8 4.667969 C 9.839844 4.667969 11.332031 6.160156 11.332031 8 C 11.332031 8.433594 11.246094 8.839844 11.09375 9.21875 L 13.039062 11.167969 C 14.046875 10.328125 14.839844 9.238281 15.328125 8 C 14.171875 5.074219 11.328125 3 7.992188 3 C 7.058594 3 6.167969 3.167969 5.339844 3.464844 L 6.78125 4.90625 C 7.160156 4.753906 7.566406 4.667969 8 4.667969 Z M 1.332031 2.847656 L 2.851562 4.367188 L 3.160156 4.671875 C 2.054688 5.535156 1.1875 6.679688 0.667969 8 C 1.820312 10.925781 4.667969 13 8 13 C 9.035156 13 10.019531 12.800781 10.921875 12.441406 L 11.199219 12.71875 L 13.152344 14.667969 L 14 13.820312 L 2.179688 2 Z M 5.019531 6.535156 L 6.054688 7.566406 C 6.019531 7.707031 6 7.851562 6 8 C 6 9.105469 6.894531 10 8 10 C 8.148438 10 8.292969 9.980469 8.433594 9.945312 L 9.464844 10.980469 C 9.019531 11.199219 8.527344 11.332031 8 11.332031 C 6.160156 11.332031 4.667969 9.839844 4.667969 8 C 4.667969 7.472656 4.800781 6.980469 5.019531 6.535156 Z M 7.894531 6.011719 L 9.992188 8.113281 L 10.007812 8.007812 C 10.007812 6.898438 9.113281 6.007812 8.007812 6.007812 Z M 7.894531 6.011719 "); path.setAttribute("class", "style-scope yt-icon"); g.append(path); arc.before(block); } function commentHandler(comment) { const user = comment.querySelector("#author-text > span").textContent.trim(); var btnDiv = comment.querySelector("#block-button"); var btn = btnDiv.children[0]; if (blockList.includes(user)) { comment.setAttribute("user-blocked", true); btnDiv.setAttribute("title", "解封"); btn.setAttribute("aria-pressed", true); } else { comment.setAttribute("user-blocked", false); btnDiv.setAttribute("title", "拉黑"); btn.setAttribute("aria-pressed", false); } } function blockUser(user) { blockList.push(user); // 添加黑名单,使后续dom由callback处理 GM_setValue("blockList", blockList); // 更正已由callback处理过的dom document.querySelectorAll("ytd-comment-renderer[user-blocked='false']").forEach((v, i) => { commentHandler(v); }); } function disblockUser(user) { blockList = blockList.filter(v => v != user); // 从黑名单移除用户,使后续dom由callback处理 GM_setValue("blockList", blockList); // 更正已由callback处理过的dom document.querySelectorAll("ytd-comment-renderer[user-blocked='true']").forEach((v, i) => { commentHandler(v); }); } const callback = function (mutationsList, observer) { for (let mutation of mutationsList) { if (mutation.type === 'childList') { var nodes = mutation.addedNodes; nodes.forEach((v, i) => { if (v.nodeName === 'YTD-COMMENT-RENDERER') { if (!v.querySelector("#block-button")) { injectBlockButton(v); // 注入拉黑按钮 } commentHandler(v); } }) } } } const callback_top = function (mutationsList, observer) { for (let mutation of mutationsList) { if (mutation.type === 'childList') { var nodes = mutation.addedNodes; nodes.forEach((v, i) => { var comments_entry = document.querySelector('ytd-item-section-renderer > #contents:not([hooked])'); if (comments_entry) { comments_entry.setAttribute("hooked", true); observer.disconnect(); createNodeListener(comments_entry, config, callback); } }) } } } function blockReset() { blockList = []; GM_setValue("blockList", blockList); document.querySelectorAll("ytd-comment-renderer[user-blocked='true']").forEach((v, i) => { commentHandler(v); }); setTimeout(function () { alert('YouTube 用户黑名单已重置成功!'); }, 1); } function blockExport() { var link = document.createElement("a"); var text = JSON.stringify(blockList, null, 2); link.setAttribute("download", "blockList.json"); link.setAttribute("href", "data:text/json," + text); link.click(); } const distinct = (value, index, self) => { return self.indexOf(value) === index; }; function handleFileSelect(e) { var files = e.target.files; if (files.length < 1) { setTimeout(function () { alert('请选择一份黑名单文件!'); }, 1); return; } var file = files[0]; var reader = new FileReader(); reader.onload = (e) => { try { var content = JSON.parse(e.target.result); if (Array.isArray(content)) { blockList = content.concat(blockList).filter(distinct);//拼接,去重。 GM_setValue("blockList", blockList); // 更正已由callback处理过的dom document.querySelectorAll("ytd-comment-renderer[user-blocked='false']").forEach((v, i) => { commentHandler(v); }); setTimeout(function () { alert('导入成功!'); }, 1); } else { setTimeout(function () { alert('错误,格式非法!'); }, 1); } } catch (e) { setTimeout(function () { alert(e); }, 1); } } reader.readAsText(file); } function blockImport() { var input = document.createElement("input"); input.setAttribute("type", "file"); input.setAttribute("accept", ".json,text/json"); input.addEventListener("change", handleFileSelect); input.click(); } /** * @type {string[]} */ var blockList = GM_getValue("blockList"); if (!blockList) { blockList = []; } GM_addStyle("ytd-comment-renderer[user-blocked='true']{height:1.8rem;opacity:0.3;overflow:hidden;box-shadow:0 0 5px #F44336;}" + "ytd-comment-renderer[user-blocked='true']:hover{height:auto;transition:all 1s;-ms-transition:all 1s;-o-transition:all 1s;-moz-transition:all 1s;-webkit-transition:all 1s;opacity:1;}" + "#block-button{width:32px;height:32px;border:none;border-radius:50%;outline:none;}" + "#block-button-item{cursor:pointer;width:32px;height:32px;border:none;outline:none;padding: 8px;border-radius:50%;background:none;}" + "#block-button-item:active{width:32px;height:32px;background: radial-gradient(#ffffff,#E2E2E2);}" + "#block-button-icon{fill:grey;}" + "#block-button-item[aria-pressed=\"true\"]>#block-button-icon{fill:#065FD4;}"); const targetNode = document.querySelector('ytd-app'); const config = { attributes: false, childList: true, subtree: true }; const localobserver = createNodeListener(targetNode, config, callback_top); GM_registerMenuCommand("重置", blockReset, "R"); GM_registerMenuCommand("导入", blockImport, "I"); GM_registerMenuCommand("导出", blockExport, "E"); })();